实战:企业级全文搜索引擎ElasticSearch快速上手

ElasticSearch是一个基于Lucene的搜索服务器。它提供了一个分布式多用户能力的全文搜索引擎,基于RESTful web接口。Elasticsearch是用Java开发的,并作为Apache许可条款下的开放源码发布,是当前流行的企业级搜索引擎。设计用于云计算中,能够达到实时搜索,稳定,可靠,快速,安装使用方便。

1 配置ElasticSearch环境

1.1 使用wget下载安装包

$ wget https://artifacts.elastic.co/downloads/elasticsearch/elasticsearch-6.3.2.zip

补充:wget是一个下载文件的工具,它非常稳定并且支持HTTPHTTPSFTP协议,可以使用HTTP代理。(另外,curl是一个比wget更重量级的下载工具,如果需要复杂的参数设置可以使用curl,可以把curl当成精简指令的浏览器)

References:
[1] https://www.elastic.co/downloads/elasticsearch
[2] http://www.cnblogs.com/peida/archive/2013/03/18/
2965369.html
[3] http://www.ruanyifeng.com/blog/2017/08/elasticsearch.html
[4] https://linux.cn/article-9330-1.html

1.2 运行ElasticSearch

1.2.1 解压并启动引擎

$ unzip elasticsearch-6.3.2.zip
$ ./bin/elasticsearch

[2018-08-18T06:43:33,887][INFO ][o.e.n.Node] [] initializing ...
[2018-08-18T06:43:33,962][INFO ][o.e.e.NodeEnvironment] [zkQedry] using [1] data
//…(省略若干)
adding template [.monitoring-kibana] for index patterns [.monitoring-kibana-6-*]
[2018-08-18T06:43:45,413][INFO ][o.e.l.LicenseService] [zkQedry] license[25a42449-a71a-4893-b3dc-6c6c4a2d07a9] mode [basic] – valid

补充:[1] 可以直接在后台运行,1)$Ctrl+z将程序挂起,2)$bg唤醒程序并在后台运行。或者可以直接 $./bin/elasticsearch &

[2] 默认情况下,ElasticSearch只允许本机访问,如果需要远程访问,可以修改 ElasticSearch安装目录的config/elasticsearch.yml文件,去掉network.host的注释,将它的值改成0.0.0.0(设成0.0.0.0让任何人都可以访问,线上服务不要这样设置,要设成具体的IP),然后重新启动ElasticSearch

1.2.2 浏览器访问9200端口(也可以使用curl命令)

此时,访问本地9200端口即可,http://localhost:9200/

{
"name" : "zkQedry",
"cluster_name" : "elasticsearch",
"cluster_uuid" : "O3ZXqDrXTda3aAzPpJUM7Q",
"version" : {
"number" : "6.3.2",
"build_flavor" : "default",
"build_type" : "zip",
"build_hash" : "053779d",
"build_date" : "2018-07-20T05:20:23.451332Z",
"build_snapshot" : false,
"lucene_version" : "7.3.1",
"minimum_wire_compatibility_version" : "5.6.0",
"minimum_index_compatibility_version" : "5.0.0"
},
"tagline" : "You Know, for Search"
}

也可以使用curl命令访问,如下,

$ sudo apt install curl
$ curl localhost:9200

1.2.3 当前节点的所有Index

Index也就是反向索引文件。Index有许多条Document组成,而Document使用JSON表示,记录的是具体信息。
具体操作,可以直接访问浏览器:http://localhost:9200/_cat/indices?v ,亦或通过命令访问(curl可以看做一个精简指令版的浏览器)

$ curl -X GET 'http://localhost:9200/_cat/indices?v'

注:-X表示指定http代理。

1.2.4 Type用来对Document分组

这里需要注意,性质完全不同的数据应该存为两个不同的Index,而不是利用Type来区分。
如果需要列出每个Index所包含的Type,可以这样,http://localhost:9200/_mapping?pretty=true

$ curl 'localhost:9200/_mapping?pretty=true'

Tips:根据规划,ElasticSearch 6.x 版只允许每个Index包含一个TypeElasticSearch 7.x 版将会彻底移除Type

2 如何索引?

2.1 中文分词(IK分词)

我测试了很多种开源的分词工具,其中IK分词是效果最好的。可以先去GitHub上找到与ElasticSearch版本相对应的zip,(找到对应版本后,右击.zip文件,再“复制链接地址”)。
https://github.com/medcl/elasticsearch-analysis-ik/releases

补充:可以将该项目加入Star。科普一下GitHub三大功能:WatchStarForkWatch,作者更新了你将会收到提醒;Star,只是收藏功能,方便以后查找;Fork,你想动手修改该项目。

2.1.1 安装IK分词

ElasticSearch安装IK中文分词工具,

$ ./bin/elasticsearch-plugin install https://github.com/medcl/elasticsearch-analysis-ik/releases/download/v6.3.2/elasticsearch-analysis-ik-6.3.2.zip

2.1.2 重启生效

安装完成之后,重启ElasticSearch将会自动加载elasticsearch-analysis-ik-6.3.2Ctrl+c可以停止,如果引擎在后台运行,可以fg唤醒到前台运行,再Ctrl+c
如果有多个后台程序在运行,也可以通过jobs命令查找后台运行程序(n为编号),然后fg nCtrl+c。或者直接kill -9 n,也可以killall -9 elasticsearch

References:
[1] https://github.com/medcl/elasticsearch-analysis-ik/releases

2.2 ElasticSearch索引器

2.2.1 创建索引(Index)

新建Index,可以直接向ElasticSearch服务器发出PUT请求。下面的例子是新建一个名叫weatherIndex

$ curl -X PUT 'localhost:9200/weather'

[2018-08-18T21:29:19,208][INFO ][o.e.c.m.MetaDataCreateIndexService] [zkQedry] [weather] creating index, cause [api], templates [], shards [5]/[1], mappings []
{"acknowledged":true,"shards_acknowledged":true,"index":"weather"}

2.2.2 指定中文分词器

首先新建一个名称为accountsIndex,里面有一个名称为personTypeperson有三个字段。这三个字段都是中文,而且类型都是文本(text),所以需要指定中文分词器,不能使用默认的英文分词器。analyzer是字段文本的分词器,search_analyzer是搜索词的分词器。ik_max_word分词器是IK分词工具提供的。

强调: 以下为一整条命令(下同),-d后面是用单引号包含的一段JSON文本。

$ curl -H 'Content-Type: application/json' -X PUT 'localhost:9200/accounts' -d '
{
"mappings": {
"person": {
"properties": {
"user": {
"type": "text",
"analyzer": "ik_max_word",
"search_analyzer": "ik_max_word"
},
"title": {
"type": "text",
"analyzer": "ik_max_word",
"search_analyzer": "ik_max_word"
},
"desc": {
"type": "text",
"analyzer": "ik_max_word",
"search_analyzer": "ik_max_word"
}
}
}
}
}'

注:-H表示自定义头信息。-dhttp post方式传输数据,后跟数据体。

运行结果如下,

[2018-08-18T22:13:03,301][INFO ][o.w.a.d.Monitor          ] try load config from /home/qingdujun/Applications/elasticsearch-6.3.2/config/analysis-ik/IKAnalyzer.cfg.xml
[2018-08-18T22:13:03,672][INFO ][o.e.c.m.MetaDataCreateIndexService] [zkQedry] [accounts] creating index, cause [api], templates [], shards [5]/[1], mappings [person]
{"acknowledged":true,"shards_acknowledged":true,"index":"accounts"}

2.2.3 新增记录(Document)

1.PUT请求新增记录

向指定的/Index/Type发送PUT请求,就可以在Index里面新增一条记录。比如,向/accounts/person发送请求,就可以新增一条人员记录。

$ curl -H 'Content-Type: application/json' -X PUT 'localhost:9200/accounts/person/1' -d '
{
"user": "张三",
"title": "工程师",
"desc": "数据库管理"
}'

运行结果如下,

{
"_index": "accounts",
"_type": "person",
"_id": "1",
"_version": 1,
"result": "created",
"_shards": {
"total": 2,
"successful": 1,
"failed": 0
},
"_seq_no": 0,
"_primary_term": 1
}

2.指定ID号

如果你仔细看,会发现请求路径是/accounts/person/1,最后的1是该条记录的Id。它不一定是数字,任意字符串(比如abc)都可以。又如,

$ curl -H 'Content-Type: application/json' -X PUT 'localhost:9200/accounts/person/abc' -d '
{
"user": "Lisi",
"title": "工程师",
"desc": "Tester"
}'

运行两次上述命令,结果如下,

{
"_index": "accounts",
"_type": "person",
"_id": "abc",
"_version": 2,
"result": "updated",
"_shards": {
"total": 2,
"successful": 1,
"failed": 0
},
"_seq_no": 1,
"_primary_term": 1
}

3.POST请求新增记录

新增记录的时候,也可以不指定Id,这时要改成POST请求。向/accounts/person发出一个 POST请求,添加一个记录。这时,服务器返回的JSON对象里面,_id字段就是一个随机字符串。

$ curl -H 'Content-Type: application/json' -X POST 'localhost:9200/accounts/person' -d '
{
"user": "王五",
"title": "工程师",
"desc": "APP研发"
}'

运行结果如下,

{
"_index": "accounts",
"_type": "person",
"_id": "ICLcUGUB_3E11rNmsRTy",
"_version": 1,
"result": "created",
"_shards": {
"total": 2,
"successful": 1,
"failed": 0
},
"_seq_no": 1,
"_primary_term": 1
}

补充:如果没有先创建Index(这个例子是accounts),直接执行上面的命令,ElasticSearch 也不会报错,而是直接生成指定的Index。所以,打字的时候要小心,不要写错Index的名称。

2.2.4 查看新增记录

/Index/Type/Id发出GET请求,就可以查看这条记录。

$ curl 'localhost:9200/accounts/person/1?pretty=true'

上面代码请求查看/accounts/person/1这条记录,URL的参数pretty=true表示以易读的格式返回。返回的数据中,found字段表示查询成功,_source字段返回原始记录。

{
"_index" : "accounts",
"_type" : "person",
"_id" : "1",
"_version" : 1,
"found" : true,
"_source" : {
"user" : "张三",
"title" : "工程师",
"desc" : "数据库管理"
}
}

如果Id不正确,就查不到数据,found字段就是false

{
"_index" : "accounts",
"_type" : "person",
"_id" : "2",
"found" : false
}

2.2.5 删除记录(Document)

删除记录就是发出DELETE请求。

$ curl -X DELETE 'localhost:9200/accounts/person/abc'

运行结果如下,

{
"_index": "accounts",
"_type": "person",
"_id": "abc",
"_version": 3,
"result": "deleted",
"_shards": {
"total": 2,
"successful": 1,
"failed": 0
},
"_seq_no": 2,
"_primary_term": 1
}

2.2.6 更新记录

更新记录就是使用PUT请求,重新发送一次数据。这里将原始数据从”数据库管理”改成”数据库管理,软件开发”。 返回结果里面,有几个字段发生了变化(加粗部分)。

$ curl -H 'Content-Type: application/json' -X PUT 'localhost:9200/accounts/person/1' -d '
{
"user": "张三",
"title": "工程师",
"desc": "数据库管理,软件开发"
}'

运行结果如下,

{
"_index": "accounts",
"_type": "person",
"_id": "1",
"_version": 2,
"result": "updated",
"_shards": {
"total": 2,
"successful": 1,
"failed": 0
},
"_seq_no": 2,
"_primary_term": 1
}

2.2.7 删除整个索引(Index)

发出DELETE请求,删除这个Index

$ curl -X DELETE 'localhost:9200/weather'

[2018-08-18T21:33:25,127][INFO ][o.e.c.m.MetaDataDeleteIndexService] [zkQedry] [weather/WvgC_tsOT2CUAwH0os-hLA] deleting index
{"acknowledged":true}

3 如何搜索?

使用GET方法,直接请求/Index/Type/_search,就会返回所有记录。

$ curl 'localhost:9200/accounts/person/_search?pretty=true'

返回结果的took字段表示该操作的耗时(单位为ms),timed_out字段表示是否超时,hits字段表示命中的记录,里面子字段的含义如下,

  1. total:返回记录数,本例是2条。
  2. max_score:最高的匹配程度,本例是1.0
  3. hits:返回的记录组成的数组。

返回的记录中,每条记录都有一个_score字段,表示匹配的程序,默认是按照这个字段降序排列。

{
"took" : 4,
"timed_out" : false,
"_shards" : {
"total" : 5,
"successful" : 5,
"skipped" : 0,
"failed" : 0
},
"hits" : {
"total" : 2,
"max_score" : 1.0,
"hits" : [
{
"_index" : "accounts",
"_type" : "person",
"_id" : "ICLcUGUB_3E11rNmsRTy",
"_score" : 1.0,
"_source" : {
"user" : "王五",
"title" : "工程师",
"desc" : "APP研发"
}
},
{
"_index" : "accounts",
"_type" : "person",
"_id" : "1",
"_score" : 1.0,
"_source" : {
"user" : "张三",
"title" : "工程师",
"desc" : "数据库管理,Modify"
}
}
]
}
}

3.1 全文搜索

ElasticSearch的查询非常特别,使用自己的查询语法,要求GET请求带有数据体。指定的匹配条件是desc字段里面包含”研发”这个词。

$ curl -H 'Content-Type: application/json' 'localhost:9200/accounts/person/_search'  -d '
{
"query" : { "match" : { "desc" : "研发" }}
}'

运行结果如下,

{
"took": 19,
"timed_out": false,
"_shards": {
"total": 5,
"successful": 5,
"skipped": 0,
"failed": 0
},
"hits": {
"total": 1,
"max_score": 0.8405092,
"hits": [{
"_index": "accounts",
"_type": "person",
"_id": "ICLcUGUB_3E11rNmsRTy",
"_score": 0.8405092,
"_source": {
"user": "王五",
"title": "工程师",
"desc": "APP研发"
}
}]
}
}

3.1.1 设置返回结果条数(size)

ElasticSearch默认一次返回10条结果,可以通过size字段改变这个设置,以下指定只返回一条结果。

$ curl -H 'Content-Type: application/json' 'localhost:9200/accounts/person/_search'  -d '
{
"query" : { "match" : { "title" : "工程师" }},
"size":1
}'

运行结果如下,

{
"took": 7,
"timed_out": false,
"_shards": {
"total": 5,
"successful": 5,
"skipped": 0,
"failed": 0
},
"hits": {
"total": 2,
"max_score": 0.5469647,
"hits": [{
"_index": "accounts",
"_type": "person",
"_id": "ICLcUGUB_3E11rNmsRTy",
"_score": 0.5469647,
"_source": {
"user": "王五",
"title": "工程师",
"desc": "APP研发"
}
}]
}
}

3.1.2 指定位移(from)

还可以通过from字段,指定位移。以下从搜索到的结果中的第1条(默认是第0条)开始,只返回一条结果。

$ curl -H 'Content-Type: application/json' 'localhost:9200/accounts/person/_search'  -d '
{
"query" : { "match" : { "desc" : "Modify" }},
"from":1,
"size":1
}'

运行结果如下,

{
"took": 5,
"timed_out": false,
"_shards": {
"total": 5,
"successful": 5,
"skipped": 0,
"failed": 0
},
"hits": {
"total": 1,
"max_score": 0.5897495,
"hits": []
}
}

注意:假设搜索到的结果有10条,from=3的话,表示相似度最高的前3条(即0,1,2)被跳过,从第4条开始显示。

3.2 逻辑运算

3.2.1 OR搜索

如果有多个搜索关键字,ElasticSearch认为它们是or关系。以下搜索的是“研发 or Modify”

$ curl -H 'Content-Type: application/json' 'localhost:9200/accounts/person/_search'  -d '
{
"query" : { "match" : { "desc" : "研发 Modify" }}
}'

运行结果如下,

{
"took": 6,
"timed_out": false,
"_shards": {
"total": 5,
"successful": 5,
"skipped": 0,
"failed": 0
},
"hits": {
"total": 2,
"max_score": 0.8405092,
"hits": [{
"_index": "accounts",
"_type": "person",
"_id": "ICLcUGUB_3E11rNmsRTy",
"_score": 0.8405092,
"_source": {
"user": "王五",
"title": "工程师",
"desc": "APP研发"
}
}, {
"_index": "accounts",
"_type": "person",
"_id": "1",
"_score": 0.5897495,
"_source": {
"user": "张三",
"title": "工程师",
"desc": "数据库管理,Modify"
}
}]
}
}

3.2.2 AND搜索

如果要执行多个关键词的and搜索,必须使用布尔查询。

$ curl -H 'Content-Type: application/json' 'localhost:9200/accounts/person/_search'  -d '
{
"query": {
"bool": {
"must": [
{ "match": { "desc": "研发" } },
{ "match": { "desc": "Modify" } }
]
}
}
}'

运行结果如下,

{
"took": 9,
"timed_out": false,
"_shards": {
"total": 5,
"successful": 5,
"skipped": 0,
"failed": 0
},
"hits": {
"total": 0,
"max_score": null,
"hits": []
}
}