我正在尝试执行允许按类别搜索的高级查询(请参阅下面的 ERB)。
搜索词将是一个字符串。基本上分为三种情况:
目前,如果搜索词是“玉米”,例如返回“玉米”类别的所有条目,我可以毫无问题地处理案例 1。
情况 2 只是部分处理。如果搜索词是“绿叶蔬菜”,则返回“绿叶蔬菜”类别的所有条目,尽管属于生菜的 CategoryCrop 也属于“绿叶蔬菜”类别,但不会返回“生菜”或“羽衣甘蓝”类别的条目
查询代码如下所示:
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?
您想要做的是将所有捐赠加入其类别和与该类别关联的作物,然后过滤与给定字符串匹配的类别名称或作物名称。让我们从在 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%'
So suppose your data set looks like the following:
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
The JOINs above will produce a pre-filtered set that looks something like this, slimmed down for easier representation:
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
As you can see, each donation ID appears next to all the crops that were under the category it donated to (note that, at this point, I'm not sure if I've got your data model right or wrong. Either way, the principles I'm describing should help you and others). So, if we filter by leafy, we'd get back donations 1 and 2. If we filter by kale, we'd get back donations 1, 2, and 3.
Hopefully that all makes sense, and we can get to the final step -- translating the SQL to ActiveRecord. It's pretty direct:
Donations
.joins(category: :crops)
.where('categories.name LIKE :term OR crops.name LIKE :term', term: "%#{search_term}%")
The join
may need to be category: { categories_crops: :crops })
, or something else, depending on exactly how your relations are set up. The idea is to traverse the relations you've defined to build the join like described above.
我们还需要包括“属于”正在搜索的类别的类别,因此我们需要一个额外的连接。由于并非每个类别都有父类别,因此它应该是外连接。然而,加入标准会很奇怪。我的第一个想法是创建一个临时表,将类别与其父类别名称连接起来,然后将其加入。所以查询变成:
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%'
翻译成 Rails:
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}%")
本文收集自互联网,转载请注明来源。
如有侵权,请联系 [email protected] 删除。
我来说两句