我有一个由Postgres支持的Rails 4应用程序,我的一个表看起来像这样:
ActiveRecord::Schema.define(version: 20141117132948) do
# These are extensions that must be enabled in order to support this database
enable_extension "plpgsql"
...
create_table "boards", force: true do |t|
...
t.string "part_name", null: false
t.string "details", limit: 1024
t.integer "sku", limit: 8, null: false
t.integer "ct_id", limit: 8, null: false, default: 0
...
end
...
end
我希望能够找到与给定搜索字词匹配的所有记录。例如,如果搜索字词为123
,那么我希望能够找到part_name
,details
,sku
或ct_id
包含{{}的所有电路板1}}。例如,我想要一个sku为'9371239583'的电路板,因为它包含序列'123'。
这是我用来查找匹配记录的代码。
123
我知道SQL语句t = Board.arel_table
q = params[:q] # This is the search term, e.g. '123'
@boards = @boards.where(
t[:part_name].matches("%#{q}%")
.or(t[:details].matches("%#{q}%"))
.or(t[:sku].matches("%#{q}%")) # This doesn't work because 'sku' is an integer
.or(t[:ct_id].matches("%#{q}%")) # same here
)
工作正常,但我找不到将两者结合在一起的方法。
有没有办法在不更改数据库架构的情况下解决此问题?
答案 0 :(得分:2)
你的一个问题是postgres中的默认索引类型是btree,虽然它在LIKE 'blah%'
上相当不错,但在LIKE '%blah'
[1}并不是很好]。要解决此问题,您应该使用带有pg_trgm
扩展名[2]的gist或gin索引。要安装它,您需要在数据库上运行以下命令一次:
create extension pg_trgm;
另一个原因是您一次比较多个列。处理这种情况的一种方法是对表达式[3]进行索引。这个的缺点(以及我一般会给你的解决方案)是它会显着减慢更新。
CREATE INDEX board_concat_idx ON boards USING gin ((part_name || ' ' || details || ' ' || sku || ' ' || ct_id) gin_trgm_ops)
最后,您应该拥有以下高性能索引查询:
SELECT * FROM boards WHERE (part_name || ' ' || details || ' ' || sku || ' ' || ct_id) LIKE '%123%'
注意 - 我已经用空格连接了这些列,但是你应该确保使用一个永远不会出现在列或查询中的字符/序列。这很重要,因为您不希望part_name以1结尾,详细信息以23开头,并获得匹配。
PS。我希望你至少在postgres 9.1。另外,我很好奇为什么你需要这个。