我正在尝试执行允许按类别搜索的高级查询(请参阅下面的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?
答案 0 :(得分:1)
您要做的是将所有捐款加入其类别和与该类别相关的作物,然后过滤与给定字符串匹配的类别名称或作物名称。让我们从SQL中开始:
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-alpha.6/css/bootstrap.min.css" rel="stylesheet" />
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-alpha.6/js/bootstrap.min.js"></script>
<nav class="navbar navbar-inverse bg-inverse navbar-toggleable-sm bg-faded navbar-fixed-top">
<div class="container">
<button class="navbar-toggler navbar-toggler-right" type="button" data-toggle="collapse" data-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<a id="header-initials" class="navbar-brand" href="home">JT</a>
<div class="collapse navbar-collapse" id="navbarSupportedContent">
<ul class="navbar-nav mr-auto">
<li class="nav-item active">
<a class="nav-link" href="#home">home</a>
</li>
<li class="nav-item">
<a class="nav-link" href="#skills">skills</a>
</li>
<li class="nav-item">
<a class="nav-link" href="#about">about</a>
</li>
<li class="nav-item">
<a class="nav-link" href="#work">work</a>
</li>
<li class="nav-item">
<a class="nav-link" href="#contact">contact</a>
</li>
</ul>
</div>
</div>
</nav>
假设您的数据集如下所示:
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%'
上面的JOIN将生成一个预先过滤的集合,看起来像这样,为了更容易表示而缩小:
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
正如您所看到的,每个捐赠ID都显示在捐赠类别下的所有作物旁边(请注意,此时,我不确定我的数据模型是对还是错。无论哪种方式,我所描述的原则都应该帮助你和其他人。因此,如果我们按绿叶过滤,我们会收回捐款1和2.如果我们按羽衣甘蓝过滤,我们会收回捐款1,2和3
希望一切都有意义,我们可以进入最后一步 - 将SQL转换为ActiveRecord。这很直接:
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
Donations
.joins(category: :crops)
.where('categories.name LIKE :term OR crops.name LIKE :term', term: "%#{search_term}%")
可能需要join
或其他内容,具体取决于您的关系设置方式。我们的想法是遍历您定义的关系,以构建如上所述的连接。
<小时/> 编辑:根据OP在评论中的澄清,数据模型还包含一个隐含的类别层次结构,从作物推断出来 - &gt;类别关系,但用作物名称替换同名类别。隐含关系,需要额外的逻辑,但数据库设计不明显,可能很难查询 - 通常表明数据模型可能需要更新。但是,让我们从更新查询开始。
我们还需要包含“属于”搜索类别的类别,因此我们需要额外的加入。由于并非每个类别都有父类别,因此它应该是外部联接。然而,加入标准会很奇怪。我的第一个想法是创建一个临时表,将类别连接到其父类别名称,然后将其加入到该表中。所以查询变为:
category: { categories_crops: :crops })
转换为Rails:
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%'