从mongodb到elasticsearch的实时同步

        由于项目中需要使用中文的全局搜索,然而mongodb无法做到中文的语义分析,所以只好使用专业的搜索数据库elasticsearch,而数据是写入到mongodb的,如果修改业务,写入mongodb同时写入elasticsearch,就会显得逻辑很冗余,而mongodb也没有SQLAlchemy那样可以监听事务提交,所以只好找找其它方法,发现有很多将mongodb直接同步到elasticsearch的daemon,但是调研一番后发现不是不能用了就是年久失修,最终选择了monstache,官方的介绍是:a go daemon that syncs mongodb to elasticsearch in realtime,贴上github地址:https://github.com/rwynn/monstache

         要想同步,mongodb必须使用repica set, 这样才会产生oplog,而monstache则是使用oplog来实现实时同步的,monstache可以配置参数namespace-regex来决定哪些database的哪些collection是需要同步的,规则如下:

      namespace-regex = '^mydb\.(mycollection|\$cmd)$'

         例如,我需要备份mydb的mycollection,则配置为 '^mydb\.mycolletion$' 就行了,它会监听到所有的写操作,并完成同步,关于如何配置repica set和monstache,这里就不详细介绍了,贴上monstache的参考文档:https://rwynn.github.io/monstache-site/start/。

        同步好之后发现同步后index的analyzer是standard analyzer,比如你要搜索’中国‘,它会将其分词为’中‘, ’国‘(标准叫法token), 只要含有其中一个token即满足搜索条件,显然这样也满足不了业务需求,所以必须使用中文的分词器,我选择了IK中文分词器,先下载包(注意版本一定要与elasticsearch的版本一直,我使用的是6.2.4),然后将包解压,在/usr/share/elasticsearch/plugins目录下mkdir ik,然后将解压后的所有文件copy到ik目录下(不要将这些文件放在elasticsearch目录中,copy这个目录,这样会导致elasticsearch无法重启,直接copy这些文件),然后重启elasticsearch,具体细节见github:https://github.com/medcl/elasticsearch-analysis-ik 。这个网页中描写了ik分词器的2种不同的模式 ik_smart 和 ik_max_word ,请仔细阅读其区别。

        关于elasticsearch的基本使用,参考http://www.ruanyifeng.com/blog/2017/08/elasticsearch.html, 安装好后怎么将其应用到已经存在的index中呢?由于每个index的mapping(类似于关系数据库中的schema)已经定义好,可否直接更改呢,阅读官方文档后发现可以给每个字段增加analyzer,每个字段(property)可以做多种用途的搜索,所以可以针对这个字段,设置多个fields,例如可以针对博客的内容设置一个standard分词器,一个中文ik_smart分词器,一个中文ik_max_word分词器,这个完全看你的需求。添加分词器使用如下命令:

curl -H "Content-Type: application/json" -XPUT "http://127.0.0.1:9200/ruminate.story/_mapping/_doc/" -d '{"properties":{"body":{"type":"text", "fields":{"cnw":{"type": "text","analyzer":"ik_max_word", "search_analyzer": "ik_max_word"}}}}}'

增加分词器后,搜索发现原先的内容并没有被分词,只有添加分词器之后添加的document才会被分词,例如之前的内容为“我来自中国”,添加分词器后搜索“中国”,搜索不到,因为只有之前standard分词器对其分过词,新加的分词器尚未对分词。这里有一个坑需要注意下,ik_smart分词器会将“我是中国人”分词为“我”,“是”,“中国人”,搜索中国是搜不到它的(从没想过会这样,应该叫lazy分词器吧,看来smart跟lazy还是挺有关联的),所以我使用了ik_max_word分词器。问题来了,之前的数据搜不到咋办,方法是reindex, reindex需要预先定义好新index的mapping,可以直接使用旧index的mapping:查出旧index的mapping后,将“mapping”中的内容拷贝然后reindex时作为 ‘-d’ 的参数传入(注意'-d'后面的json一定要用单引号包裹起来):

curl -H "Content-Type: application/json" -XPUT "http://127.0.0.1:9200/ruminate.story/_mapping/_doc/" -d '{"properties":{"body":{"type":"text", "fields":{"cnw":{"type": "text","analyzer":"ik_max_word", "search_analyzer": "ik_max_word"}}}}}'

reindex时,数据拷贝时会根据新index的分词器进行分词,这样旧的数据也被新加的分词器分词啦!那么只剩下最后一个问题了,monstache同步到了旧的index,怎么让其同步到新的index呢,很简单,删除旧index, 将新的index以旧index名取一个别名。

curl -H "Content-Type: application/json" -X PUT "http://127.0.0.1:9200/ruminate.story.new/_alias/ruminate.story"

这样就大工告成啦!

        另外注意两点:

        1. elasticsearch每次query返回的默认是10个结果,如果需要调整,query时指定from和to两个参数

        2. 文中所指的分词器analyzer,其实是分析器,真正的分词器tokenizer是其一部分,analyzer可由3部分组成,但是只有分词器是必须的,故用分词器来代替analyzer方便理解。关于这部分内容,参考http://www.rendoumi.com/elasticsearchzhong-de/。

        总的来说,elasticsearch内容颇多,而且更新快,碰到问题google后的结果大部分都过时了,最好的方法就是看官网,官网是英文的,需要点耐心。上面的内容如果有误,请指出,谢谢!

你可能感兴趣的:(数据库,全文搜索)