CouchDB中的全文搜索

时间:2011-03-12 21:59:34

标签: performance postgresql indexing full-text-search couchdb

我有一个问题,希望得到你的回答: - )

所以,我拿了geonames.org并将所有德国城市的数据导入所有地区。

如果我进入“汉堡”,它会列出“汉堡中心,汉堡机场”等。该应用程序位于一个无法访问互联网的封闭网络中,因此我无法访问geonames.org Web服务并且必须导入数据。 :( 这个城市及其所有地区都是汽车完工。因此,每次击键都会导致XHR请求等等。

现在我的客户询问是否有可能拥有世界上的所有数据。最后,大约5.000.000行,有45.000.000个替代名称等。

Postgres每次查询需要大约3秒钟,这使得自动完成无法使用。

现在我想到了CouchDb,已经使用过了。我的问题:

我想发布“Ham”,我希望CouchDB获取所有以“Ham”开头的文档。如果我进入“汉堡”,我希望它返回汉堡等等。

CouchDB是否适合它?您可以推荐哪些其他数据库以低延迟(可能是内存中)和数百万个数据集进行响应?数据集不会定期更改,而是相当静态!

3 个答案:

答案 0 :(得分:19)

如果我理解你的问题,可能你所需要的只是已经在CouchDB中构建。

  1. 获取一系列名称以例如名称开头的文档“火腿”。您可以使用request with a string rangestartkey="Ham"&endkey="Ham\ufff0"
  2. 如果您需要更全面的搜索,可以创建一个包含其他地方名称的视图作为键。因此,您再次可以使用上述技术查询范围。
  3. 这是一个视图函数:

    function(doc) {
        for (var name in doc.places) {
            emit(name, doc._id);
        }
    }
    

    另请参阅有关CouchDB typeahead and autocomplete search的CouchOne博客文章以及有关CouchDB autocomplete的邮件列表中的此讨论。

答案 1 :(得分:15)

使用PostgreSQL优化搜索

您的搜索锚定在开头不需要模糊搜索逻辑。这是全文搜索的典型用例。

如果它变得更模糊或者您的搜索在开始时锚定,请查看此处了解更多信息:
Similar UTF-8 strings for autocomplete field
More on pattern matching in Postgres.

在PostgreSQL中,您可以使用高级索引功能,使查询非常快。请特别注意operator classesindexes on expressions

1)text_pattern_ops

假设您的列是文本类型,您可以使用文本模式运算符的特殊索引,如下所示:

CREATE INDEX name_text_pattern_ops_idx
ON tbl (name text_pattern_ops);

SELECT name
FROM   tbl
WHERE  name ~~ ('Hambu' || '%');

这假设您使用C以外的数据库区域设置进行操作 - 在您的情况下很可能是de_DE.UTF-8。您可以还使用区域设置“C”设置数据库。我引用manual here

  

如果您使用C语言环境,则不需要xxx_pattern_ops   运算符类,因为具有默认运算符类的索引是   可用于C语言环境中的模式匹配查询。


2)表达式索引

我想你也想让搜索不区分大小写。所以让我们采取另一个步骤,使其成为表达式的索引:

CREATE INDEX lower_name_text_pattern_ops_idx
ON tbl (lower(name) text_pattern_ops);

SELECT name
FROM   tbl
WHERE  lower(name) ~~ (lower('Hambu') || '%');

要使用索引,WHERE子句必须与索引表达式匹配。


3)优化索引大小和速度

最后,您可能还希望对主要字符数施加限制,以最大限度地减少索引的大小并进一步加快速度:

CREATE INDEX lower_left_name_text_pattern_ops_idx
ON tbl (lower(left(name,10)) text_pattern_ops);

SELECT name
FROM   tbl
WHERE  lower(left(name,10)) ~~ (lower('Hambu') || '%');
Postgres 9.1引入了

left()。在旧版本中使用substring(name, 1,10)


4)涵盖所有可能的请求

超过10个字符的字符串呢?

SELECT name
FROM   tbl
WHERE  lower(left(name,10)) ~ (lower(left('Hambu678910',10)) || '%');
AND    lower(name) ~~ (lower('Hambu678910') || '%');

这看起来多余,但您需要拼写这种方式来实际使用索引。索引搜索会将其缩小到几个条目,附加条款会过滤其余条目。尝试找到最佳点。取决于数据分布和典型用例。 10个字符似乎是一个很好的起点。对于超过10个字符,left()有效地变成了一种非常快速且简单的散列算法,这对于许多(但不是全部)用例来说已经足够了。


5)使用CLUSTER

优化光盘表示

因此,主要的访问模式是根据索引lower_left_name_text_pattern_ops_idx检索一堆相邻的行。而你大部分都是读书而且几乎没有写过。这是CLUSTER教科书案例。我quote the manual

  

当表格被聚类时,它会根据索引信息进行物理重新排序。

使用像你这样的大表,这可以大大缩短响应时间,因为要获取的所有行都在磁盘上相同或相邻的块中。

第一个电话:

CLUSTER tbl USING lower_left_name_text_pattern_ops_idx;

将保存要使用的索引的信息,并且后续调用将重新对表进行聚类:

CLUSTER tbl;
CLUSTER;    -- cluster all tables in the db that have previously been clustered.

如果你不想重复它:

ALTER TABLE tbl SET WITHOUT CLUSTER;

对于具有更多写入负载的表,请查看pg_repack,这可以在没有表的独占锁的情况下执行相同操作。


6)防止结果中的行太多

要求搜索字符串至少包含3或4个字符。为了完整起见,我补充说,无论如何你可能会这样做 并LIMIT返回的行数:

SELECT name
FROM   tbl
WHERE  lower(left(name,10)) ~~ (lower('Hambu') || '%')
LIMIT  501;

如果您的查询返回超过500行,请告诉用户缩小其搜索范围。


7)优化过滤方法(运算符)

如果你绝对必须挤出每一微秒,你可以使用text_pattern_ops family的运算符。像这样:

SELECT name
FROM   tbl
WHERE  lower(left(name, 10)) ~>=~ lower('Hambu')
AND    lower(left(name, 10)) ~<=~ (lower('Hambu') || chr(2097151));

你最后的特技表现得很少。通常,标准操作员是更好的选择。


如果你这样做,搜索时间将减少到几毫秒。

答案 2 :(得分:10)

我认为更好的方法是将数据保存在数据库(Postgres或CouchDB)上,并使用全文搜索引擎(如LuceneSolrElasticSearch)对其进行索引。

话虽如此,有project integrating CouchDB with Lucene