Elasticsearch是用Java开发的基于Apache Lucene的一个近乎实时的分布式搜索分析引擎。维基百科、Stack Overflow、GitHub等都采用它来作为全文搜索引擎。本文旨在用docker来快速入门并尝试Elasticsearch提供的基本功能。
对Apache Solr入门有兴趣的朋友请参考用容器快速上手Apache Solr。
启动
关于Elasticsearch的入门知识,有一本gitbook:《Elasticsearch 权威指南》翻译得不错。本文关心的是实际操作,所以这就开始吧。通过docker,一条命令就可以直接启动Elasticsearch:
1
| docker run -d --net=host --name=es elasticsearch:2.3.5
|
我用的是mac,通过docker-machine env default
命令可以看到默认的default docker-machine
的IP地址是192.168.99.100
,于是便可以通过http://192.168.99.100:9200/来从Elasticsearch获得json数据了:
1 2 3 4 5 6 7 8 9 10 11 12
| { "name" : "Lorvex", "cluster_name" : "elasticsearch", "version" : { "number" : "2.3.5", "build_hash" : "90f439ff60a3c0f497f91663701e64ccd01edbb4", "build_timestamp" : "2016-07-27T10:36:52Z", "build_snapshot" : false, "lucene_version" : "5.5.0" }, "tagline" : "You Know, for Search" }
|
也可以使用_cat
直接在命令行获取Elasticsearch的健康状态和节点状态:
1 2
| curl '192.168.99.100:9200/_cat/health?v' curl '192.168.99.100:9200/_cat/nodes?v'
|
索引
与Solr不同,Elasticsearch只支持json格式。创建索引的过程,就是向服务器POST数据的过程:
1 2 3 4 5 6 7 8
| curl -XPOST 'http://192.168.99.100:9200/megacorp/employee' -d ' { "first_name" : "John", "last_name" : "Smith", "age" : 25, "about" : "I love to go rock climbing", "interests": [ "sports", "music" ] }'
|
立刻就能得到Elasticsearch返回的结果:{“_index”:”megacorp”,”_type”:”employee”,”_id”:”AVbK2ssDm7bdYo65QJ6k”,”_version”:1,”_shards”:{“total”:2,”successful”:1,”failed”:0},”created”:true}。Url里的megacorp是索引的名字(想象成一个数据库),employee是类型的名字(想象成一张表)。返回的_id
是Elasticsearch随机创建的一个ID,用于标识数据。
可以通过以下url获取Elasticsearch的索引信息:
1
| curl 'http://192.168.99.100:9200/_cat/indices?v'
|
修改索引的话,发出PUT请求就可以了(别忘了把ID换成你自己生成的_id
):
1 2 3 4 5 6 7 8
| curl -XPUT 'http://192.168.99.100:9200/megacorp/employee/AVbK2ssDm7bdYo65QJ6k' -d ' { "first_name" : "Johnie", "last_name" : "Smithreen", "age" : 38, "about" : "I do not love to go rock climbing", "interests": [] }'
|
如果指定的ID不存在,那么PUT也会新建一条记录。删除索引的话,发出DELETE请求就可以了:
1
| curl -XDELETE 'http://192.168.99.100:9200/megacorp/employee/AVbK2ssDm7bdYo65QJ6k'
|
或者删除整个索引:
1
| curl -XDELETE 'http://192.168.99.100:9200/megacorp'
|
再次获取索引信息,便会看到已经没有索引了。让我们加上三条数据,以备下一节搜索使用:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| curl -XPOST 'http://192.168.99.100:9200/megacorp/employee/1' -d ' { "first_name" : "John", "last_name" : "Smith", "age" : 25, "about" : "I love to go rock climbing", "interests": [ "sports", "music" ] }' curl -XPOST 'http://192.168.99.100:9200/megacorp/employee/2' -d ' { "first_name" : "Jane", "last_name" : "Smith", "age" : 32, "about" : "I like to collect rock albums", "interests": [ "music" ] }' curl -XPOST 'http://192.168.99.100:9200/megacorp/employee/3' -d ' { "first_name" : "Douglas", "last_name" : "Fir", "age" : 35, "about": "I like to build cabinets", "interests": [ "forestry" ] }'
|
搜索
查询
要查看刚刚创建的数据,直接get就可以了:
1 2 3
| curl 'http://192.168.99.100:9200/megacorp/employee/1' curl 'http://192.168.99.100:9200/megacorp/employee/2' curl 'http://192.168.99.100:9200/megacorp/employee/3'
|
全部查询改一下url就可以了:
1 2 3
| curl 'http://192.168.99.100:9200/megacorp/employee/_search' curl 'http://192.168.99.100:9200/megacorp/_search' curl 'http://192.168.99.100:9200/_search'
|
简易搜索
上面的命令都只能算查询,还不算搜索。接下来让我们尝试Elasticsearch强大的搜索功能:
1 2 3 4 5 6 7 8 9
| curl 'http://192.168.99.100:9200/megacorp/employee/_search?q=last_name:Smith' curl 'http://192.168.99.100:9200/megacorp/employee/_search' -d ' { "query" : { "match" : { "last_name" : "Smith" } } }'
|
查询DSL
上面两条命令都是去获取last_name
为Smith
的数据,但是下面一条用到了查询DSL。这是Elasticsearch提供的DSL查询语言,可以通过它完成更加复杂的搜索。接下来除了Smith
以外,我们还要增加30岁以上这一条件:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| curl 'http://192.168.99.100:9200/megacorp/employee/_search' -d ' { "query" : { "filtered" : { "filter" : { "range" : { "age" : { "gt" : 30 } } }, "query" : { "match" : { "last_name" : "smith" } } } } }'
|
在这里介绍一下filter
和query
。可以这么理解:filter
是精确查找(想象成sql里where子句的=、<、>),速度快,有缓存。query
是模糊查找(想象成sql里where子句的like,但是能根据匹配度打分),虽然没有filter
那么快,但是查询结果能更准确一些。业务上的查询通常都是同时使用二者,通过filter
快速找到对象数据,再通过query
来匹配。
全文搜索
接下来尝试全文搜索,从about
的句子里查询想要的数据:
1 2 3 4 5 6 7 8
| curl 'http://192.168.99.100:9200/megacorp/employee/_search' -d ' { "query" : { "match" : { "about" : "rock climbing" } } }'
|
查到了两个结果。返回值包含了一项_score
,既含有rock
又含有climbing
的数据,得分明显高于只含有rock
的数据。理所当然的,得分高的数据排在上面。如果想要精确匹配rock climbing
,把match
改为match_phrase
就可以了:
1 2 3 4 5 6 7 8
| curl 'http://192.168.99.100:9200/megacorp/employee/_search' -d ' { "query" : { "match_phrase" : { "about" : "rock climbing" } } }'
|
统计
查找最受欢迎的兴趣:
1 2 3 4 5 6 7 8
| curl 'http://192.168.99.100:9200/megacorp/employee/_search' -d ' { "aggs": { "all_interests": { "terms": { "field": "interests" } } } }'
|
查找每个兴趣的平均年龄:
1 2 3 4 5 6 7 8 9 10 11 12 13
| curl 'http://192.168.99.100:9200/megacorp/employee/_search' -d ' { "aggs" : { "all_interests" : { "terms" : { "field" : "interests" }, "aggs" : { "avg_age" : { "avg" : { "field" : "age" } } } } } }'
|
这些数据都是实时计算出来的。就像使用SQL来查询数据库一样,Elasticsearch提供了自己的DSL来让我们基于复杂的条件来搜索。这里是统计功能的官方文档。
中文
同Solr一样,要想让Elasticsearch支持中文分词,需要使用中文分词组件。这里我们还是用mmseg插件。首先下载并解压:
1 2 3
| wget -c https://github.com/medcl/elasticsearch-analysis-mmseg/releases/download/v1.9.4/elasticsearch-analysis-mmseg-1.9.4.zip unzip -d elasticsearch-analysis-mmseg-1.9.4 elasticsearch-analysis-mmseg-1.9.4.zip sed -i 's/2.3.4/2.3.5/' elasticsearch-analysis-mmseg-1.9.4/plugin-descriptor.properties
|
1.9.4版的插件只支持Elasticsearch 2.3.4版,要想支持2.3.5,就需要把elasticsearch.version
配置改为2.3.5
。除此之外,还需要加点儿东西到Elasticsearch的配置文件里:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| cat << EOF > es.yml network.host: 0.0.0.0 index: analysis: analyzer: mmseg_maxword: type: custom filter: - lowercase tokenizer: mmseg_maxword mmseg_maxword_with_cut_letter_digi: type: custom filter: - lowercase - cut_letter_digit tokenizer: mmseg_maxword EOF
|
上述配置文件的具体语法可以参考这里。接下来就可以启动容器了,分别挂载配置文件和插件:
1 2 3 4 5
| docker rm -f es docker run -d --net=host --name=es \ -v `pwd`/es.yml:/usr/share/elasticsearch/config/elasticsearch.yml \ -v `pwd`/elasticsearch-analysis-mmseg-1.9.4/:/usr/share/elasticsearch/plugins/elasticsearch-analysis-mmseg-1.9.4/ \ elasticsearch:2.3.5
|
Elasticsearch服务启动后,就可以增加索引和映射(mapping,可以理解为数据类型,有点像solr的schema),并且插入一些数据了:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| curl -XPUT http://192.168.99.100:9200/index curl -XPOST http://192.168.99.100:9200/index/fulltext/_mapping -d' { "fulltext": { "_all": { "analyzer": "mmseg_maxword", "search_analyzer": "mmseg_maxword", "term_vector": "no", "store": "false" }, "properties": { "content": { "type": "string", "store": "no", "term_vector": "with_positions_offsets", "analyzer": "mmseg_maxword", "search_analyzer": "mmseg_maxword", "include_in_all": "true", "boost": 8 } } } }' curl -XPOST http://192.168.99.100:9200/index/fulltext/1 -d'{content:"美国留给伊拉克的是个烂摊子吗"}' curl -XPOST http://192.168.99.100:9200/index/fulltext/2 -d'{content:"公安部:各地校车将享最高路权"}' curl -XPOST http://192.168.99.100:9200/index/fulltext/3 -d'{content:"中韩渔警冲突调查:韩警平均每天扣1艘中国渔船"}' curl -XPOST http://192.168.99.100:9200/index/fulltext/4 -d'{content:"中国驻洛杉矶领事馆遭亚裔男子枪击 嫌犯已自首"}'
|
最后搜索中国
看看:
1 2 3 4 5 6 7 8 9 10 11 12
| curl -XPOST http://192.168.99.100:9200/index/fulltext/_search -d' { "query" : { "term" : { "content" : "中国" }}, "highlight" : { "pre_tags" : ["<tag1>", "<tag2>"], "post_tags" : ["</tag1>", "</tag2>"], "fields" : { "content" : {} } } } '
|
可以使用_analyze
来查看分析的过程:
1
| curl "http://192.168.99.100:9200/index/_analyze?analyzer=mmseg_maxword&pretty=true" -d "美国留给伊拉克的是个烂摊子吗"
|
可见,这句话被解析成:美国、留给、伊、拉克、的、是个、烂、摊子、吗,还不是非常完美。伊拉克
、烂摊子
都没有很好地识别出来。
有兴趣的话,还可以重建容器,跟无中文分词的效果对比一下。除了mmseg,大神medcl还写了一个elasticsearch-rtf版本,涵盖诸多中文分词工具,可以直接使用。