由于大量的数据使用MongoDB作为数据库进行存储,现在需要对外提供文本搜索功能,在实践工程中,调研并尝试了多种方案,在本文中做个记录。
1、使用正则表达式
即使用find("textField":/搜索文本/}的方式对文本字段进行包含匹配。
这种方法的好处在于不需要进行额外的操作,现在常用的MongoDB服务器版本都能够支持,非常方便。
但是同时,缺点也非常明显,每次搜索必须遍历所有的数据,消耗的时间与数据量成正比,且基本无法进行优化。在大数据量情况下每次搜索小号的时间太长,可能无法接受。
2、分词存储
基本的步骤如下:
1、使用分词引擎对目标文本字段,例如textField进行分词
2、将分词的结果的每个词按照数组的形式存储在一个新的字段,例如searchField中
3、对searchField建立索引
4、搜索时,对请求串进行同样的分词,并将分词结果作为参数进行find({ searchField: {$all: splittedTextArray} })
这种方法的好处在于,适用于各个版本的MongoDB,同时基于索引和MongoDB对于数组的专门优化,在一定数据量的情况下速度也还可以
如果要使用这种方法,需要在新数据入库时进行处理
功能上比较简单,很多搜索常用的需求无法实现,效率也比不上专业的搜索引擎。可以作为一种无法增加组件时的参考方案。
3、使用MongoDB自带的文本搜索功能
MongoDB在较早的版本中就已经将文本搜索功能作为实验性功能加入,当时需要使用runCommand作为系统命令进行调用。
在后来的版本中作为正式的功能引入,直接就可以使用find({"$text":{"$search":"搜索文本"})进行搜索
在搜索之前,需要对文本字段建立索引:
db.coll.createIndex( { textField: "text" } )
对于一个集合,只能建立一个索引,但是一个索引中可以包含多个字段
文本搜索的参数有如下几个:
$search: <string>,
$language: <string>,
$caseSensitive: <boolean>,
$diacriticSensitive: <boolean>$search后面的关键词可以有多个,并且可以使用一些符号表示与或非的关系
使用$caseSensitive设置是否区分大小写,$language指示搜索的语言类型
$diacriticSensitive设置是否区别发音符号,例如,CAFÉ于Café是同一语义,只是重音不一样。
并且在结果中,可以使用{score:{$meta:"textScore"}来获得相似度
不过,只有在Mongodb 3.2之后的企业版中才开始加入了对中文的支持,之前或者社区的版本在建立全文索引时会自动过滤中文字符。
在Mongodb 3.2企业版中,对中文建立全文索引后,默认使用的是与英文同样的分词规则,即以空格与符号作为词与词之间分隔的界限。
所以,当使用“福尔摩斯”作为搜索词时,无法搜索到“福尔摩斯探案全集”,只能搜索到类似“福尔摩斯(1)”之类的结果
如果想解决这种情况,可以有两种方法:
一、在存储文本时,自行先对该文本进行分词处理,将需要搜索的文本加上空格即可
二、为MongoDB添加中文分词的支持
MongoDB企业版集成了基础技术Rosette 语义平台(RLP)根据语言来进行正规化、分词、断句、词干和分词。所以,可以使用RLP来实现对中文分词的支持。
如果想在MongoDB中使用RLP,MongoDB需要一个RLP基础语义组件的许可证。具体如何获得RLP的许可证,需要联系basistech公司。
当从basistech公司的邮件中获取了RLP证书文件(rlp-license.xml)后,解压拷贝到对应的目录中
<BT_ROOT>/rlp/rlp/licenses
然后使用参数指定该目录,重启MongoDB即可
--basisTechRootDirectory=<BT_ROOT>