【发布时间】:2021-10-31 01:12:09
【问题描述】:
我提供了很多上下文来为这个问题奠定基础。我要解决的是使用结构化数据对多个数据库表进行快速准确的模糊搜索,而不是全文文档搜索。
如果重要的话,我正在使用 postgreSQL 13.4+ 和 Rails 6+。
我有几个表格的结构化数据:
class Contact
attribute :id
attribute :first_name
attribute :last_name
attribute :email
attribute :phone
end
class Organization
attribute :name
attribute :license_number
end
...several other tables...
我正在尝试实现快速准确的模糊搜索,以便我可以一次搜索所有这些表(Rails 模型)。
目前,我有一个单独的搜索查询,使用 ILIKE 连接我想要针对每个模型即时搜索的列:
# contact.rb
scope :search -> (q) { where("concat_ws(' ', first_name, last_name, email, phone) ILIKE :q", q: "%#{q}%")
# organization.rb
scope :search -> (q) { where("concat_ws(' ', name, license_number) ILIKE :q", q: "%#{q}%") }
在我的搜索控制器中,我分别查询每个表并显示每个模型的前 3 个结果。
@contacts = Contact.search(params[:q]).limit(3)
@organizations = Organization.search(params[:q]).limit(3)
这有效,但速度相当慢,而且不像我想要的那样准确。
我目前的方法存在问题:
- 只有数千条记录,速度较慢(相对而言)。
- 不准确,因为
ILIKE必须在字符串中的某处完全匹配,而我想实现模糊搜索(即,使用ILIKE,“smth”不会匹配“smith”)。 - 未加权;我想将
contacts.last_name列的权重置于organizations.name之上,因为联系人表通常是优先级更高的搜索项。
我的解决方案
我的理论解决方案是创建一个search_entries 多态表,该表为我要搜索的每个contact、organization 等都有一个单独的记录,然后可以为这个search_entries 表建立索引快速检索。
class SearchEntry
attribute :data
belongs_to :searchable, polymorphic: true
# Store data as all lowercase to optimize search (avoid lower method in PG)
def data=(text)
self[:data] = text.lowercase
end
end
但是,我遇到的问题是如何构建此表,以便可以快速对其进行索引和搜索。
contact = Contact.first
SearchEntry.create(searchable: contact, data: "#{contact.first_name} #{contact.last_name} #{contact.email} #{contact.phone}")
organization = Organization.first
SearchEntry.create(searchable: organization, data: "#{organization.name} #{organization.license_number}")
这使我能够执行以下操作:
SearchEntry.where("data LIKE :q", q: "%#{q}%")
甚至使用 PG 的 similarity() 函数进行模糊搜索:
SearchEntry.connection.execute("SELECT * FROM search_entries ORDER BY SIMILARITY(data, '#{q}') LIMIT 10")
我相信我也可以在 data 字段上使用带有 pg_trgm 的 GIN 索引来优化搜索(不是 100%...)。
这将我的搜索简化为对单个表的单个查询,但它仍然不允许我进行加权列搜索(即,contacts.last_name 比 organizations.name 更重要)。
问题
- 这种方法是否可以让我对数据进行索引,以便进行非常快速的模糊搜索? (我知道“非常快”是主观的,所以我的意思是有效地使用 PG 以尽快获得结果)。
- 我能否使用
GIN索引和pg_trgm三元组来索引这些数据以进行快速模糊搜索? - 如何在这样的方法中实现某些值的权重高于其他值?
【问题讨论】:
-
有一个工具可以做到这一点github.com/sunspot/sunspot
-
您是否考虑过使用诸如 Solr、Lucene 或 ElasticSearch 之类的 OFS 搜索解决方案是否会更好?
-
@max:如果无法在 PG 本身中获得我需要的东西,我会将 ElasticSearch 视为我的后备方案。我试图通过将其全部保存在数据库中来减少对成本和复杂性的依赖。
-
@AhmedKamal:我现在正试图避免外部依赖,但如果在 PG 中完成搜索不能像我需要的那样工作,我会看看 ElasticSearch,因为我还有更多熟悉它。
标签: ruby-on-rails postgresql search