1

これは実行可能ですか?

私は次のスコープを持っています:

class Thing < ActiveRecord::Base

scope :with_tag, lambda{ |tag| joins(:tags).where('tags.name = ?', tag.name)
                                           .group('things.id') }

def withtag_search(tags)
  tags.inject(scoped) do |tagged_things, tag|
    tagged_things.with_tag(tag) 
  end
end

渡されたタグの配列に単一のタグがある場合は結果が得られThing.withtag_search(array_of_tags)ますが、その配列に複数のタグが含まれている場合は、結果として空の関係が得られます。それが役立つ場合:

Thing.withtag_search(["test_tag_1", "test_tag_2"])

SELECT "things".* 
FROM "things" 
INNER JOIN "things_tags" ON "things_tags"."thing_id" = "things"."id" 
INNER JOIN "tags" ON "tags"."id" = "things_tags"."tag_id" 
WHERE (tags.name = 'test_tag_1') AND (tags.name = 'test_tag_2') 
GROUP BY things.id

=> [] # class is ActiveRecord::Relation

一方

Thing.withtag_search(["test_tag_1"])

SELECT "things".* 
FROM "things" 
INNER JOIN "things_tags" ON "things_tags"."thing_id" = "things"."id" 
INNER JOIN "tags" ON "tags"."id" = "things_tags"."tag_id" 
WHERE (tags.name = 'test_tag_1') 
GROUP BY things.id

=> [<Thing id:1, ... >, <Thing id:2, ... >] # Relation including correctly all 
                                            # Things with that tag

これらの関係を連鎖させて、(他の理由の中でも)配列ではなく関係でのみ機能するページ付けにカミナリの宝石を使用できるようにしたいので、スコープを返す必要があります。

4

3 に答える 3

2

私もこの問題に遭遇しました。問題はRailsではなく、間違いなくMySQLです。

SQLは、次の一時JOINテーブルを作成します(必要なフィールドのみが表示されます)。

+-----------+-------------+---------+------------+
| things.id | things.name | tags.id | tags.name  |
+-----------+-------------+---------+------------+
|     1     |     ...     |    1    | test_tag_1 |
+-----------+-------------+---------+------------+
|    1      |     ...     |    2    | test_tag_2 |
+-----------+-------------+---------+------------+

Tagしたがって、すべてのsを1つの特定のに結合する代わりに、 -の組み合わせThingごとに1つの行を生成します(信じられない場合は、このSQLステートメントで実行してください)。問題は、クエリ基準が次のようになっていることです。これは、この各行に対してチェックされ、真になることはありません。両方を同時に等しくすることはできません!TagThingCOUNT(*)WHERE (tags.name = 'test_tag_1') AND (tags.name = 'test_tag_2')tags.nametest_tag_1test_tag_2

標準のSQLソリューションはSQLステートメントを使用することINTERSECTです...しかし残念ながらMySQLでは使用しません。

最善の解決策はThing.withtag_search、タグごとに実行し、返されるオブジェクトを収集して、次のように、各結果に含まれるオブジェクトのみを選択することです。

%w[test_tag_1 test_tag_2].collect do |tag|
  Thing.withtag_search(tag)
end.inject(&:&)

これをリレーションとして取得したい場合ActiveRecordは、おそらく次のように行うことができます。

ids = %w[test_tag_1 test_tag_2].collect do |tag|
  Thing.withtag_search(tag).collect(&:id)
end.inject(&:&)
Things.where(:id => ids)

(私が使用している)もう1つの解決策は、Thingテーブルにタグをキャッシュし、MySQLブール検索を実行することです。必要に応じて、このソリューションの詳細を説明します。

とにかく、これがあなたのお役に立てば幸いです。:)

于 2011-08-12T21:26:14.177 に答える
0

これは一見かなり複雑ですが、SQLに基づいて、次のことが必要です。

WHERE(tags.name IN('test_tag_1'、'test_tag_2'))

Rails 3についてはあまり扱っていませんが、JOINを適切に調整できれば、問題は解決するはずです。次のようなソリューションを試しましたか?

 joins(:tag).where('tags.name IN (?), tags.map { |tag| tag.name })

このようにして、期待どおりに参加します(INTERSECTIONではなくUNION)。これがこの問題について考えるのに役立つ方法であることを願っています。

于 2011-08-09T09:06:58.700 に答える
0

この問題の解決策を見つけることができないようです。そのため、カミナリを使用して独自のタグ付けを行う代わりに、「タグ付け可能としての行為」に切り替えて、ページを作成します

于 2011-08-12T20:51:38.793 に答える