2

ユーザーが選択したタグを「すべて一致」させようとする方法を持つモデルがあります。ただし、アプリで 2 つのタグを選択し、パラメーターを以下のメソッドに渡すとすぐに、結果が返されません。1 つの選択で問題なく動作します。

ご覧のとおり、タグ付けによる多数のタグを持つスタイル モデルがあります。ユーザーが選択したタグを「すべて一致」させたいのですが、filter_with_params 定義の下の 3 行目は、適切なクエリを試行することです。

したがって、params[:t] = ["green", "yellow", "red"] を filter メソッドに渡すと、これら 3 つのタグすべてを持つスタイルのみを返したいとします。

モデルのスニペットを次に示します。

class Style < ActiveRecord::Base
    has_many :tagizations, :dependent => :destroy
    has_many :tags, :through => :tagizations

    def self.filter_with_params(params)
        scoped = self.where("styles.name != ''")
        scoped = scoped.includes(:tags)
        scoped = params[:t].inject(scoped){|memo, val| memo.where(:tags => {:name => val})} if params[:t]  
        scoped
    end
end

以下は、2 つのリクエストの開発ログ出力を含む Gist です。

https://gist.github.com/jgrannas/6195528

.TO_SQL の使用

SINGLE TAG (作品):

Style.filter_with_params({:t => ["engagement"]}).to_sql
    => "SELECT \"styles\".* FROM \"styles\"  WHERE \"tags\".\"name\" = 'engagement' AND (styles.name != '')" 

複数のタグ (機能しません)

Style.filter_with_params({:t => ["engagement", "Halo"]}).to_sql
     => "SELECT \"styles\".* FROM \"styles\"  WHERE \"tags\".\"name\" = 'engagement' AND \"tags\".\"name\" = 'Halo' AND (styles.name != '')

ポストグルを使用しています

4

3 に答える 3

2

問題は次のとおりです。

WHERE "tags"."name" = 'engagement' AND "tags"."name" = 'Halo'

問題は、3 つの名前すべてを持つ単一のタグを持つものを探していることですが、これは不可能です。欲しいのは、名前ごとにタグが付いているものです。

これが機能するかどうかは完全にはわかりませんが、次のことを試してください。

def self.filter_with_params(params)
    scoped = self.select("distinct styles.*, count(*) AS count")
    scoped = scoped.where("styles.name != ''")
    scoped = scoped.joins(:tags)
    if params[:t]
      scoped = scoped.where(tags: { name: params[:t] })
      scoped = scoped.group('styles.id')
      scoped = scoped.having("count = #{params[:t].size}")
    end
    scoped
end

ここで基本的に行っているのは、スタイルとタグの組み合わせごとに 1 つの行を持つテーブルを作成することですが、 のタグのみですparams[:t]。たとえば、 の行style1 - tag1と の行がありstyle1 - tag2ます。次に、行をスタイル ID でグループ化して、一意のスタイルごとに 1 つのレコードのみを取得できるようにします。しかし、各グループの行数も数えています。タグのセット内のすべてのタグの行を持つレコードのみが必要なので、残りは破棄します。

注: 上記のコードは MySQL では機能しますが、postgres では機能しません。postgres のドキュメントにSELECTは次のように書かれています。

「出力列の名前は、ORDER BY および GROUP BY 句で列の値を参照するために使用できますが、WHERE または HAVING 句では使用できません。代わりに式を書き出す必要があります。」

だから私たちは必要ですscoped = scoped.having("count(*) = #{params[:t].size}")

于 2013-08-09T17:52:34.970 に答える
0

タグごとに存在するSQL句を挿入することをお勧めします。出力 SQL は次のようになります。

SELECT styles.* 
FROM styles
WHERE styles.name != ''
AND exists(SELECT * from tags where style_id = styles.id AND tags.name = 'red')
AND exists(SELECT * from tags where style_id = styles.id AND tags.name = 'green')

私の知る限り、それを実現するには SQL を手動で挿入する必要があります。したがって、次のようなもの:

def self.filter_with_params(params)
    scoped = self.where("styles.name != ''")
    scoped = scoped.includes(:tags)
    params[:t].each do |tag|
        scoped.where('EXISTS(select 1 FROM tags where tags.name = ? and tags.style_id = styles.id )', tag) 
    end
    scoped
end
于 2013-08-12T16:15:19.910 に答える
0

これを試して:

def self.with_tags *tags
  joins(:tags).where(:tags => {:name => tags}).group("#{self.table_name}.id").having("COUNT(DISTINCT tags.name) = ?", tags.length)
end
于 2013-08-13T10:29:28.500 に答える