CouchDB中的全文搜索

Jan*_* L. 14 postgresql indexing performance full-text-search couchdb

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

因此,我使用geonames.org并将所有德国城市的数据导入所有地区.

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

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

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

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

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

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

ssm*_*mir 19

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

  1. 获取一系列名称以例如"Ham"开头的文档.您可以使用带字符串范围请求:startkey="Ham"&endkey="Ham\ufff0"
  2. 如果您需要更全面的搜索,可以创建一个包含其他地方名称作为键的视图.因此,您再次可以使用上述技术查询范围.

这是一个视图函数:

function(doc) {
    for (var name in doc.places) {
        emit(name, doc._id);
    }
}
Run Code Online (Sandbox Code Playgroud)

另请参阅关于CouchDB预先输入和自动完成搜索的CouchOne博客文章以及关于CouchDB自动完成的邮件列表上的讨论.


Erw*_*ter 15

使用PostgreSQL优化搜索

您的搜索在开始时就已锚定,并且不需要模糊搜索逻辑.这不是全文搜索的典型用例.

如果它变得更模糊或者您的搜索没有在开始时锚定,请在此处查看更多:
自动完成字段的类似UTF-8字符串
有关Postgres中模式匹配的更多信息.

在PostgreSQL中,您可以使用高级索引功能,这些功能可以使查询速度非常快.特别是要查看表达式上的运算符类索引.

1)text_pattern_ops

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

CREATE INDEX name_text_pattern_ops_idx
ON tbl (name text_pattern_ops);

SELECT name
FROM   tbl
WHERE  name ~~ ('Hambu' || '%');
Run Code Online (Sandbox Code Playgroud)

这假设您使用的数据库区域设置不是C- de_DE.UTF-8在您的情况下很可能.您还可以使用区域设置"C"设置数据库.我在这里引用手册:

如果确实使用了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') || '%');
Run Code Online (Sandbox Code Playgroud)

要使用索引,该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') || '%');
Run Code Online (Sandbox Code Playgroud)

left()与Postgres 9.1一起介绍.substring(name, 1,10)在旧版本中使用.


4)涵盖所有可能的请求

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

SELECT name
FROM   tbl
WHERE  lower(left(name,10)) ~ (lower(left('Hambu678910',10)) || '%');
AND    lower(name) ~~ (lower('Hambu678910') || '%');
Run Code Online (Sandbox Code Playgroud)

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


5)优化光盘表示 CLUSTER

因此,主要的访问模式将是根据我们的索引检索一堆相邻的行lower_left_name_text_pattern_ops_idx.你大部分都读过,几乎没有写过.这是一本教科书案例CLUSTER.我引用手册:

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

使用像您这样的大表,这可以显着提高响应时间,因为要获取的所有行都在磁盘上的相同或相邻块中.

第一个电话:

CLUSTER tbl USING lower_left_name_text_pattern_ops_idx;
Run Code Online (Sandbox Code Playgroud)

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

CLUSTER tbl;
CLUSTER;    -- cluster all tables in the db that have previously been clustered.
Run Code Online (Sandbox Code Playgroud)

如果你不想重复它:

ALTER TABLE tbl SET WITHOUT CLUSTER;
Run Code Online (Sandbox Code Playgroud)

对于具有更多写入负载的表pg_repack,可以执行相同的操作,而无需对表进行独占锁定.


6)防止结果中的行太多

要求搜索字符串至少包含3或4个字符.我添加这个是为了完整,你可能还是这样做.
LIMIT返回的行数:

SELECT name
FROM   tbl
WHERE  lower(left(name,10)) ~~ (lower('Hambu') || '%')
LIMIT  501;
Run Code Online (Sandbox Code Playgroud)

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


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

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

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

这最后一次噱头你获得的收益很少.通常,标准操作员是更好的选择.


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


del*_*uan 10

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

话虽如此,还有一个将CouchDB与Lucene集成在一起项目.