最近项目要使用elasticsearch。。。这里总结一下看elasticsearch文档的一些总结吧。

基本概念

elasticsearch是一个全文本搜索的服务,比如github从几百亿行代码中搜索某个代码就用了elasticsearch。在mac下可以通过brew安装和管理,注意还要再安装一个kibana来进行可视化和管理(可以在kibana的console跑各种请求)。es默认跑在9200,kibana默认跑在5601端口。

这里注意对于中文场景要安装elasticsearch-analysis-ik进行分词,ik还支持自定义词典,可以在es-path/config/analysis-ik/custom下自定义dic。

概念对应关系如下表

传统数据库 ElasticSearch
Database Index
Table Type
Row Document
Column Field
schema mapping

elasticsearch的所有内容都是通过json格式传递的。每个文档都是一个json的形式。一般文档包括下面三个metadata:

  • _index:文档存储的地方(类似于database)
  • _type:文档代表的对象的类(类似于table)
  • _id:文档的唯一标示(类似于主见,可以自定义,也可以让es帮你生成)

es是支持分布式的,当然也有对应的shard和replica机制。

单机操作api

创建:

1
2
3
4
5
PUT /{index}/{type}/{id} 
{
"field":"value",
...
}

获取:

1
2
GET /{index}/{type}/{id}?_source=title,text#可以获取特定字段(包含metadata)
GET /{index}/{type}/{id}/_source#只获取_source字段,而无metadata

存在:

1
HEAD /{index}/{type}/{id}#判断document是不是存在,只返回状态码而没有具体数据

更新:

由于elasticsearch中的文档是不可变的,所以修改只能通过重建或者脚本的方式更新

1
2
3
4
5
6
7
8
9
PUT /{index}/{type}/{id}
{
...
}
#也可以用post,注意put和post的区别主要是post可以默认自动生成id
POST /{index}/{type}
{
...
}

脚本的方式:

1
2
3
4
5
6
7
8
9
10
11
POST {index}/{type}/{id}/_update#注意最后是一个_update
{
"script" : {
"source":"stx._source.tags.add(params.tag)",
"lang":"painless",
"params" : {
"tag":"blue"
}
}
}
#想增加一个字段tags的内容

删除:

1
DELETE /{index}/{type}/{id}#删除某个文档

搜索:

elasticsearch最重要的功能就是搜索,搜索的姿势也是很多的

1
2
3
4
GET /_search#在所有索引类型中搜索
GET /{index1},{index2}/_search#在index1和index2两种索引类型中搜索
GET /{index}/{type}/_search#在某个type中搜索
GET /_all/{type},{type}/_search#在所有索引的某些type上搜索

对于搜索的结果,可以用size分页,用from决定从哪里出发

当然平时比较多的是查找字符串

1
2
GET /_all/{type}/_search?q=tweet:elasticsearch#查找某个type中tweet:elasticsearch的文档
GET /_all/_doc/_search?q=%2bdescribtion%3amax#查找某个类中describtion字段包含max的文档

第二个要重点解释一下,其实在url encode之前为:

+describtion:max

如果要放在查询中,就要进行urlencode(percent encode),然后放在q后,即:

q=%2bdescribtion%3amax

这里的查询不仅限于字符串查找,还可以是特定条件下的查找,如

  • name字段包含"mary""john"
  • date晚于2014-09-10
  • _all字段包含"aggregations""geo"

这个可以用

1
+name:(mary john) +date:>2014-09-10 +(aggregations geo)

来表示,最后urlencode即可进行查询。

mapping和Analysis

es的mapping对应了传统数据库的schema,其中es会对不同的字段类型进行猜测,得到对应的mapping,在这种情况下,对某个field的类型猜测可能会是date,但如果用_all搜索这个field则认为他是string,从而带来搜索结果的不同。

对于一个搜索引擎,与数据库最本质的区别在于确切值和全文文本之间。

传统数据库需要在where语句里的特定匹配,但es则希望如下:

  • 一个针对"UK"的查询将返回涉及"United Kingdom"的文档
  • 一个针对"jump"的查询同时能够匹配"jumped""jumps""jumping"甚至`”leap”

等等等场景,为此引出了es最关键的技术——倒排索引。

Analysis

对于大小写或者同源词等原本是不能匹配的,需要通过特定的分析过程才能被检索,具体过程如下:

  • 首先,标记化一个文本快为适用于倒排索引的term
  • 标准化这些term为标准行事,提高可搜索率和查全率

一个完成的分析器(analyzer)包括三部分:

  1. 字符过滤器:去掉一些html标签,或者转化&为and等
  2. 分词器:通过空格或逗号分词(中文需要特定分词工具ik)
  3. 标记过滤:将词进行大小写转换、去掉一些停用词(a、the这些)或者增加词(同义词)

可以通过如下方式测试analyzer

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
GET _analyze?pretty
{
"analyzer": "ik_smart",
"text":"王者荣耀真好玩"
}

#output:
{
"tokens": [
{
"token": "王者荣耀",
"start_offset": 0,
"end_offset": 4,
"type": "CN_WORD",
"position": 0
},
{
"token": "真好玩",
"start_offset": 4,
"end_offset": 7,
"type": "CN_WORD",
"position": 1
}
]
}

如果想使用某个特定analyzer,而不是系统自带的standard,就需要通过mapping。

查看map需要用:

1
GET /{index}/_mapping/{type}

这样得到mappings的内容,注意现在一些field的类型是keyword(而不是text)是因为keyword不会自动分词并建立索引,但text会这么做。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
PUT final
{
"mappings": {
"_doc": {
"properties": {
"name": {
"type": "text"
},
"blob": {
"type": "text",
"analyzer":"ik_smart"
}

}
}
}
}