Ruby on rails 活动记录-带或的链查询

Ruby on rails 活动记录-带或的链查询,ruby-on-rails,postgresql,ruby-on-rails-4,rails-activerecord,Ruby On Rails,Postgresql,Ruby On Rails 4,Rails Activerecord,轨道:4.1.2 数据库:PostgreSQL 对于我的一个查询,我使用来自gem和activerecord的方法。如何用“OR”而不是“AND”链接以下一些查询: 我想把最后两个作用域(fuzzy_search和它后面的where)用“OR”而不是“and”链接在一起。所以我想检索所有被批准和(名字类似于“Test”或姓氏包含“Test”)的人。我已经为此挣扎了很长一段时间,所以任何帮助都将不胜感激 我深入到fuzzy_搜索中,发现它会被翻译成: SELECT "people".*, COAL

轨道:4.1.2

数据库:PostgreSQL

对于我的一个查询,我使用来自gem和activerecord的方法。如何用“OR”而不是“AND”链接以下一些查询:


我想把最后两个作用域(
fuzzy_search
和它后面的
where
)用“OR”而不是“and”链接在一起。所以我想检索所有被批准和(名字类似于“Test”或姓氏包含“Test”)的人。我已经为此挣扎了很长一段时间,所以任何帮助都将不胜感激

我深入到fuzzy_搜索中,发现它会被翻译成:

SELECT "people".*, COALESCE(similarity("people"."first_name", 'test'), 0) AS "rankxxx" 
FROM "people" 
WHERE (("people"."first_name" % 'abc'))  
ORDER BY "rankxxx" DESC
也就是说,如果您不关心保留顺序,它只会按
WHERE((“people.”“first_name”%%“abc”)过滤结果。

知道了这一点,现在您可以简单地编写具有类似功能的查询:

People.where(status: status_approved)
      .where('(first_name % :key) OR (last_name LIKE :key)', key: 'Test')

如果您想要订单,请指定加入2个条件后您希望订单是什么。

几天后,我想出了解决方案!以下是我所做的:

这是我想与OR链接在一起的查询:

people = People.where(status: status_approved).fuzzy_search(first_name: "Test").where("last_name LIKE ?", "Test")
# Generate a random number for the ordering
rank = rand(100000000000000000).to_s

# Create the SQL query
sql_query = "SELECT people.*, COALESCE(similarity(people.first_name, :query), 0)" + 
            " AS rank#{rank} FROM people" +
            " WHERE (people.status = :status AND" +
            " ((people.first_name % :query) OR (last_name LIKE :query_like)))" + 
            " ORDER BY rank#{rank} DESC"
正如Hoang Phan所建议的,当您查看控制台时,会生成以下SQL:

SELECT "people".*, COALESCE(similarity("people"."first_name", 'test'), 0) AS "rank69146689305952314"
FROM "people"
WHERE "people"."status" = 1 AND (("people"."first_name" % 'Test')) AND (last_name LIKE 'Test')  ORDER BY "rank69146689305952314" DESC
然后我深入研究了文本宝石,发现了排名是如何产生的。我在文件中找到了它,然后使用它构建了SQL查询。我还将连接最后两个条件的“AND”替换为“OR”:

在引用表和字段时,我去掉了SQL查询中的所有引号,因为当我将它们保留在表和字段中时,它会给我错误消息,即使我使用了单引号

然后,我使用该方法在一个数组中检索
People
对象ID。符号(
:status
:query
:query\u like
)用于防止SQL注入,因此我相应地设置了它们的值:

# Retrieve all the IDs of People who are approved and whose first name and last name match the search query.
# The IDs are sorted in order of most relevant to the search query.
people_ids = People.find_by_sql([sql_query, query: "Test", query_like: "%Test%", status: 1]).map(&:id)
我得到的是数组中的ID,而不是
People
对象,因为
find\u by\u sql
返回的是
array
对象,而不是通常返回的
CollectionProxy
对象,因此我无法在此数组上使用ActiveRecord查询方法,例如
where
。使用这些ID,我们可以执行另一个查询来获取
CollectionProxy
对象。但是,有一个问题:如果我们只是运行
People.where(id:People\u id)
,那么id的顺序将不会被保留,因此我们所做的所有相关性排序都是徒劳的

幸运的是,有一个很好的gem,名为,它允许我们按照id的特定顺序检索所有
People
对象。虽然gem可以工作,但我没有使用它,而是编写了一行代码来创建条件,以保持秩序

order_by = people_ids.map { |id| "people.id='#{id}' DESC" }.join(", ")
people = People.where(id: people_ids).order(order_by)
如果我们的
people\u id
数组是
[1,12,3]
,它将创建以下订单语句:

"people.id='1' DESC, people.id='12' DESC, people.id='3' DESC"
我从中了解到,以这种方式编写ORDER语句将保持秩序

order_by = people_ids.map { |id| "people.id='#{id}' DESC" }.join(", ")
people = People.where(id: people_ids).order(order_by)
现在,剩下的就是从ActiveRecord中检索
People
对象,确保指定顺序

order_by = people_ids.map { |id| "people.id='#{id}' DESC" }.join(", ")
people = People.where(id: people_ids).order(order_by)
就这样!我不担心删除任何重复的ID,因为当您运行
where
命令时,ActiveRecord会自动执行此操作


我知道此代码的可移植性不强,如果修改了
人员
表的任何列,都需要进行一些更改,但它工作得很好,似乎只根据控制台执行一个查询。

谢谢您的回答!这很有帮助!我更喜欢在查询中使用散列条件,因为它们更整洁,更容易修改,但如果必须这样做,我将使用原始SQL<代码>模糊搜索使用“rankxxx”字段将结果从最相关到最不相关排序。但是,每次运行查询时,“rank”后面的“xxx”部分似乎是唯一的数字(可能是时间戳)。你知道我会如何按“rankxxx”排序结果吗?我宁愿说你不能,gem使用随机数的原因是为了防止以后有人使用它。您可以使用与他们相同的选择(如
合并(相似性…)和
),但不推荐使用。在评估这两种情况时,您会遇到麻烦。在这种情况下,如果您关心顺序,最好使用activerecord选择2次,然后根据需要聚合结果。你知道这不会对性能造成太大的损失,而且更易于阅读和维护。谢谢你的回复。我不相信执行两个查询会有帮助,因为我无法获得排名顺序。第一个查询的结果将是唯一按排名排序的结果。我已经复制了生成的查询并对其进行了修改,以便它可以使用
find\u by\u sql
方法。我添加了变量转义以防止SQL注入。我现在唯一的问题是,对于原始SQL,我没有得到唯一的记录。我尝试在SELECT语句后添加DISTINCT关键字,但没有效果。我已经成功了!我更愿意让DISTINCT关键字工作,但现在我使用
uniq
方法在查询完成后过滤掉任何重复的记录。我将在未来几天内发布我的答案。我基本上不得不使用原始SQL,尽管我不想这么做。