几天后,我想出了解决方案!这是我所做的:
这是我想用 OR 链接在一起的查询:
people = People.where(status: status_approved).fuzzy_search(first_name: "Test").where("last_name LIKE ?", "Test")
正如 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
然后我挖掘了 textacular gem 并发现了排名是如何生成的。我在 textacular.rb 文件中找到了它,然后使用它制作了 SQL 查询。我还将连接最后两个条件的“AND”替换为“OR”:
# 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"
在引用表和字段时,我去掉了 SQL 查询中的所有引号,因为当我将它们保留在那里时,即使我使用单引号,它也会给我错误消息。
然后,我使用find_by_sql 方法检索数组中的People 对象ID。符号(:status、:query、:query_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_by_sql 返回的是 Array 对象而不是 CollectionProxy 对象,因为通常会返回,所以我不能使用 ActiveRecord 查询方法,例如where 在这个数组上。使用 ID,我们可以执行另一个查询以获取 CollectionProxy 对象。但是,有一个问题:如果我们只是简单地运行People.where(id: people_ids),ID的顺序将不会被保留,所以我们所做的所有相关性排名都是徒劳的。
幸运的是,有一个名为 order_as_specified 的不错的 gem,它允许我们按照 ID 的特定顺序检索所有 People 对象。尽管 gem 可以工作,但我没有使用它,而是编写了一小行代码来制作可以保持顺序的条件。
order_by = people_ids.map { |id| "people.id='#{id}' DESC" }.join(", ")
如果我们的 people_ids 数组是 [1, 12, 3],它将创建以下 ORDER 语句:
"people.id='1' DESC, people.id='12' DESC, people.id='3' DESC"
我从this comment 了解到,以这种方式编写 ORDER 语句会保留顺序。
现在,剩下的就是从 ActiveRecord 中检索 People 对象,确保指定顺序。
people = People.where(id: people_ids).order(order_by)
然后就做到了!我不必担心删除任何重复的 ID,因为 ActiveRecord 会在您运行 where 命令时自动执行此操作。
我了解此代码的可移植性不是很好,如果修改了 people 表的任何列,则需要进行一些更改,但它运行良好,并且根据控制台似乎只执行一个查询。