2017-06-11 12 views
0

カテゴリ(以下のERBを参照)で検索できるようにする高度なクエリを実行しようとしています。複数の結合を持つ可能性が高いRails高度なクエリ

検索語句は文字列です。

  1. 検索語が作物(CropCategory)の直接的な名前で、クエリが検索語に一致するカテゴリ名を持つすべての寄付金を返す必要があります。基本的に3つの場合があります。
  2. 検索用語はカテゴリ(特定の作物ではない)であり、クエリは検索用語と一致するカテゴリ名を持つすべての寄付と、指定されたカテゴリ内の任意の作物名に一致するカテゴリ名を持つすべての寄付を返さなければなりません検索用語で
  3. 検索用語が作物またはカテゴリと一致しませんそれは何も返しません。 ERB of the Database

検索用語は、カテゴリー「とうもろこし」のすべてのエントリが返されるインスタンスのための「コーン」であれば現在、私は何の問題もなく、ケース1を処理することができます。

ケース2は部分的にしか処理されません。検索語が「葉っぱの緑」である場合、カテゴリー「葉っぱの緑」のすべてのエントリが返されますが、「レタス」または「ケール」の項目は、レタスに属しているレタスに属するにもかかわらず、「葉の緑」というカテゴリーに属しません。

クエリのコードは次のようになります。あなたが何をしたいか

donations = Donation.all.order("date DESC") 
donations = donations.joins(:category).includes(:category) 
donations = donations.where("name like ?", "%#{search_term}%").references(:categories) if search_term.present? 

答えて

1

は、カテゴリ名または指定した文字列に一致する作物名のいずれかでフィルタ、その後、両方の彼らのカテゴリへと、そのカテゴリに関連付けられている作物へのすべての寄付に参加しています。そのSQLでやって起動してみましょう:

SELECT d.* 
FROM donations d 
JOIN categories ca  ON ca.id = d.category_id 
JOIN categories_crops cc ON cc.category_id = ca.id 
JOIN crops cr   ON cr.id = cc.crop_id 
WHERE 
    ca.name LIKE 'foo%' 
OR 
    cr.name LIKE 'foo%' 

だからあなたのデータセットは以下のように見えるとします

donations   categories   crops  categories_crops 
---------------- ------------------ ------------ --------------------- 
id | category_id id | name   id | name  crop_id | category_id 
================ ================== ============ ===================== 
1 | 1    1 | leafy greens 1 | lettuce 1  | 1 
2 | 1    2 | dark greens  2 | broccoli 2  | 2 
3 | 2         3 | kale  3  | 1 
                 3  | 2 

ザ・はスリムダウン、次のようになります事前にフィルタ処理セットが生成されます上記のJOIN簡単に表現するために:あなたが見ることができるように

donations X categories X categories_crops X crops 
------------------------------------------------- 
donation_id | category.name | crops.name 
================================================= 
1   | leafy greens | lettuce 
1   | leafy greens | kale 
2   | leafy greens | lettuce 
2   | leafy greens | kale 
3   | dark greens | broccoli 
3   | dark greens | kale 

、各寄付IDは、この時点で、私はSUないよ、ということに注意してください(に寄付カテゴリの下にあったすべての作物の横に表示されますデータモデルが正しいか間違っているかどうかいずれにしても、私が描いている原則はあなたと他の人を助けるはずです。我々は緑豊かなによってフィルタリングのであれば、我々はすべてが理にかなっていることをうまくいけば、我々はケールによってフィルタする場合、我々は寄付金1を取り戻すだろう寄付1と2を取り戻す2、および3

たい最後にSQLをActiveRecordに変換することができます。それはかなり直接的です:

Donations 
    .joins(category: :crops) 
    .where('categories.name LIKE :term OR crops.name LIKE :term', term: "%#{search_term}%") 

joinは正確にあなたの関係が設定されている方法に応じて、category: { categories_crops: :crops })、または何か他のものをする必要があります。アイデアは、上記のような結合を構築するために定義した関係をトラバースすることです。


EDIT: - >カテゴリ関係なく、同じ名前のカテゴリの作物名に置き換えコメントでパーOPの明確化は、データモデルは、作物から推測されるカテゴリの暗黙の階層が含まれています。追加のロジックが必要だがデータベース設計から明らかにならない暗黙の関係は、クエリするのが難しい場合があります。データモデルが更新する必要がある場合が多いということがよくあります。しかし、まずクエリを更新してみましょう。

検索対象のカテゴリに「属し」ているカテゴリも含める必要があります。追加する必要があります。すべてのカテゴリに親カテゴリがあるわけではないため、外部結合である必要があります。しかし、参加条件は変わってきます。私の最初の考えは、親カテゴリの名前にカテゴリを結合する一時テーブルを作成し、その上に結合するだけです。だから、クエリは次のようになります。レールに翻訳

SELECT d.* 
FROM donations d 
JOIN categories ca  ON ca.id = d.category_id 
JOIN categories_crops cc ON cc.category_id = ca.id 
JOIN crops cr   ON cr.id = cc.crop_id 
LEFT JOIN (
    SELECT ca2.name as parent_category_name, ca3.id as child_category_id 
    FROM categories_crops cc2 
    JOIN categories ca2 ON ca2.id = cc2.category_id 
    JOIN crops cr2 ON cr2.id = cc2.crop_id 
    JOIN categories ca3 ON ca3.name = cr2.name 
) cp 
ON cp.child_category_id = ca.id 

WHERE 
    ca.name LIKE 'foo%' 
OR 
    cr.name LIKE 'foo%' 
OR 
    cp.parent_category_name LIKE 'foo%' 

:応答のための

Donations 
    .joins(category: :crops) 
    .joins(<<-SQL) 
    LEFT JOIN (
     SELECT ca2.name as parent_category_name, ca3.category_id as child_category_id 
     FROM categories_crops cc2 
     JOIN categories ca2 ON ca2.id = cc2.category_id 
     JOIN crops cr2 ON cr2.id = cc2.crop_id 
     JOIN categories ca3 ON ca3.name = cr2.name 
    ) cp ON cp.child_category_id = categories.id 
    SQL 
    .where('categories.name LIKE :term OR crops.name LIKE :term OR cp.parent_category_name LIKE :term', term: "%#{search_term}%") 
+0

ありがとう!これは検索用語が** kale **の場合に役立つようです** kale **と** leafy green **の両方の項目を取得しますが、** leafy green ** **はまだ** leafy green **エントリー。 – Bevilacqua

+0

@Bevilacquaあなたは、そのシナリオでどんなことを期待しているかについて詳しく説明できますか?なぜ「葉っぱの緑」の検索が「レタス」と「ケール」以上に戻ってくるのかわからない。 –

+0

これは実際には私が返すものですが、現在はリーフグリーンで明示的にマークされたエントリのみを返します。 Ex。カテゴリ:[lettuce]、[kale]、[leefy greens]の3つのエントリが寄付されている場合。私は葉緑色のエントリだけを取得します。 – Bevilacqua