当名称可以是任何语言时,如何按名称索引postgres表?

Alb*_*iir 5 postgresql indexing localization tokenize string-comparison

我有一个大的postgres位置(商店,地标等)表,用户可以通过各种方式搜索.当用户想要搜索某个地点的名称时,系统当前会这样做(假设搜索在咖啡馆):

lower(location_name) LIKE '%cafe%'
Run Code Online (Sandbox Code Playgroud)

作为查询的一部分.这非常低效.这是非常的.我必须加快速度.我试过索引表

gin(to_tsvector('simple', location_name))
Run Code Online (Sandbox Code Playgroud)

和搜索

(to_tsvector('simple',location_name) @@ to_tsquery('simple','cafe'))
Run Code Online (Sandbox Code Playgroud)

它工作得很漂亮,并且将搜索时间减少了几个数量级.

但是,位置名称可以是任何语言,包括中文等不是以空格分隔的语言.这个新系统无法找到任何中文位置,除非我搜索确切的名称,而旧系统可以找到与部分名称匹配就好了.

所以,我的问题是:我可以让它同时适用于所有语言,还是我走错了路?

wil*_*ynn 4

如果您想优化任意子字符串匹配,一种选择是使用模块pg_tgrm. 添加索引:

CREATE INDEX table_location_name_trigrams_key ON table
  USING gin (location_name gin_trgm_ops);
Run Code Online (Sandbox Code Playgroud)

这会将“Simple Cafe”分解为“sim”、“imp”、“mpl”等,并为每行中的每个 trigam 的索引添加一个条目。然后,查询规划器可以自动使用该索引进行子字符串模式匹配,包括:

SELECT * FROM table WHERE location_name ILIKE '%cafe%';
Run Code Online (Sandbox Code Playgroud)

此查询将在索引中查找“caf”和“afe”,找到交集,获取这些行,然后根据您的模式检查每一行。(最后一项检查是必要的,因为“caf”和“afe”的交集既匹配“simple cafe”又匹配“unsafescaffolding”,而“%cafe%”应该只匹配一个)。随着输入模式变长,索引会变得更加有效,因为它可以排除更多行,但它仍然不如索引整个单词那么有效,因此不要指望性能会比to_tsvector.

问题是,三元组对于三个字符以下的模式根本不起作用。这可能会或可能不会影响您的申请。


编辑:我最初将其添加为评论。

昨晚,当我快要睡着的时候,我又有了一个想法。创建一个cjk_chars函数,它接受输入字符串、regexp_matches整个 CJK Unicode 范围,并返回任何此类字符的数组,或者NULL如果没有则返回。在 上添加 GIN 索引cjk_chars(location_name)。然后查询:

WHERE CASE
  WHEN cjk_chars('query') IS NOT NULL THEN
    cjk_chars(location_name) @> cjk_chars('query')
    AND location_name LIKE '%query%'
  ELSE
    <tsvector/trigrams>
  END
Run Code Online (Sandbox Code Playgroud)

哒哒,一元字母!