在系列的第一篇文章中咱们介绍了ElasticSearch的基本概念和操做,本文将继续介绍ElasticSearch查询和索引功能。html
目录:git
结构化搜索将查询条件以json的形式包含在http请求的body中,一般状况下搜索请求应该使用GET方法但不是全部的客户端和服务端都支持GET请求包含body。 所以,ElasticSearch支持使用GET或POST进行搜索。github
本节示例均使用默认analysis
和mapping
配置, 此状态下ElasticSearch与普通的文档数据库几乎无异。咱们将在下一节中介绍如何进行模糊查询,使它成为真正的搜索引擎。算法
精确查询一般起到过滤的做用,由于不使用分析器解析一般很是迅速。sql
查询可使用from和size参数对结果进行分页:数据库
{ "from": 0, "size": 10, "query": { "term": { "nickname": "easy" } } }
term查询用于进行精确匹配:json
POST /blog/user/_search { "query": { "term": { "nickname": "easy" } } } response: { "took": 23, "timed_out": false, "_shards": { "total": 5, "successful": 5, "failed": 0 }, "hits": { "total": 2, "max_score": 0.45532417, "hits": [ { "_index": "blog", "_type": "user", "_id": "1", "_score": 0.45532417, "_source": { "nickname": "easy", "username": "user1", "status": "normal" } }, { "_index": "blog", "_type": "user", "_id": "2", "_score": 0.43648314, "_source": { "nickname": "easy", "username": "user2" "status": "normal" } } ] } }
在响应的hits.hits
字段中咱们能够看到匹配的文档,文档_score
字段是采用TF-IDF算法得出匹配程度得分,结果集中的文档按照得分降序排列。缓存
上述查询能够用sql表示为:app
SELECT * FROM user WHERE nickname = "easy";
terms查询能够视为多个term查询的组合:less
POST /blog/user/_search { "query": { "terms": { "nickname": ["easy", "ease"] } } } response: { "took": 18, "timed_out": false, "_shards": { "total": 5, "successful": 5, "failed": 0 }, "hits": { "total": 2, "max_score": 1.0970675, "hits": [ { "_index": "blog", "_type": "user", "_id": "2", "_score": 1.5779335, "_source": { "nickname": "ease", "status": "normal" } }, { "_index": "blog", "_type": "user", "_id": "4", "_score": 1.0970675, "_source": { "nickname": "easy", "content": "simple", "status": "normal" } } ] } }
range
查询用于数值等类型进行过滤,好比示例中过滤粉丝数大于等于2的用户:
GET /user/naive/_search { "query": { "range": { "followerCount": { "gte": 2 }, "registerTime": { "gt": "2017-01-01 00:00:00", "lt": "2018-01-01 00:00:00" } } } }
示例中的range查询能够用SQL描述为:
SELECT * FROM user_naive WHERE followerCount >= 2 AND unix_timestamp(registerTime) > unix_timestamp('2017-01-01 00:00:00') AND unix_timestamp(registerTime) < unix_timestamp('2018-01-01 00:00:00');
可用的范围表达式有:
lt
: 小于(less than)lte
: 小于等于(less than / equals)gt
: 大于(greater than)gte
: 大于等于(greater than / equals)能够进行范围查询的字段:
用户输入字符串一般没法进行精确查询,全文查询可使用分析器解析查询字符串而后根据相关度筛选查询结果。
match
查询全文字段(类型为analyzed
)时,会使用分析器将查询字符串解析成词条列表,而后进行匹配。
查询字符串"easy simple"会被standard分析器解析为[easy
, simple
], 这个查询等价于:
{ "terms": { "nickname": ["easy", "simple"] } }
POST /blog/user/_search { "query": { "match": { "nickname": "easy simple" } } } response: { "took": 19, "timed_out": false, "_shards": { "total": 5, "successful": 5, "failed": 0 }, "hits": { "total": 3, "max_score": 1.1382749, "hits": [ { "_index": "blog", "_type": "user", "_id": "3", "_score": 1.1382749, "_source": { "nickname": "simple", "status": "normal" } }, { "_index": "blog", "_type": "user", "_id": "1", "_score": 1.049597, "_source": { "nickname": "easy", "status": "normal" } } ] } }
若以eas sim
为关键词进行term查询不会匹配到任何文档。
multi_match
用于对多个字段进行同一个查询:
{ "query": { "multi_match": { "analyzer": "keyword", "query": "easy", "fields": [ "nickname.ngram", "username.ngram" ] } } }
上述查询等同于对nickname.ngram
和username.ngram
两个字段进行match查询,对结果进行综合排序。
排序方式有:
best_fields
most_fields
cross_fields
script查询可使用脚本描述查询条件,进行复杂的查询。
{ "query": { "script": { "script": { "source": "doc['count'].value > params.limit", "lang": "painless", "params": { "limit": 1 } } } }
咱们一般使用ElasticSearch提供的painless脚本语言编写脚本,也可使用Groovy等脚本语言。
组合查询用于将多个查询组合起来,以表达更复杂的查询条件或排序规则。
组合查询都是能够自由嵌套的,如咱们能够bool
查询中使用另外一个bool
查询或dis_max
做为must
子查询。
Bool查询用于组合多个条件查询相关度最高的文档, 下面展现了一个Bool查询请求:
POST /user/naive/_search { "query": { "bool": { "must": { "match": { "nickname": "easy" } }, "must_not": { "match": { "nickname": "hard" } }, "should": { "match": { "nickname": "simple" } }, "filter": [ { "term": { "status": "normal" } } ] } } } response: { "took": 20, "timed_out": false, "_shards": { "total": 5, "successful": 5, "failed": 0 }, "hits": { "total": 3, "max_score": 1.3847495, "hits": [ { "_index": "blog", "_type": "user", "_id": "4", "_score": 1.3847495, "_source": { "nickname": "easy", "content": "simple", "status": "normal" } }, { "_index": "blog", "_type": "user", "_id": "5", "_score": 0.45532417, "_source": { "nickname": "easy", "content": "bitter", "status": "normal" } } ] } }
上述bool查询的目标为:
must
条件必须知足, 即nickname
字段必须与词条easy
匹配,字段的匹配程度会影响得分must_not
条件必须不知足, 即nickname
字段不能与词条hard
匹配should
条件不作要求, 但知足should
条件的文档会得到更高的相关度评分_score
。 当should
查询中包含多个字段时, 会将各字段得分的和做为总分。因此查询到两个nickname与easy
匹配的文档,可是content
为simple
的字段得到了更高的评分。filter
条件必须知足,可是匹配程度不会影响得分。filter
子查询通常使用精确查询,由于过滤器不进行相关度计算且使用过滤器缓存, 其执行速度很是快。
上文已经提到bool
查询的should
查询会将各字段得分之和做为总分,然而在实际应用中一般一个字段高度匹配的文档可能比拥有多个字段低匹配更符合用户的指望。
dismax查询一样用于组合多个查询,可是按照匹配程度最高的字段肯定总分:
{ "query": { "dis_max": { "queries": [ { "match": { "nickname": "easy" } }, { "match": { "content": "easy" } } ] } } }
function_score能够更加自由地控制评分过程:
{ "query": { "function_score": { "filter": { "term": { "uid": 1 } }, "functions": [ { "filter": { "term": { "features": "a" }}, "weight": 1 }, { "filter": { "term": { "features": "b" }}, "weight": 2 } ], "score_mode": "sum", } } }
一致性随机
function_score
能够对结果进行随机排序:
{ "query": { "random_score": { "seed": 2 }, "bost_mode": "replace", "query": { "dis_max": { "queries": [ { "multi_match": { "analyzer": "keyword", "query": "finley", "boost": 1.2, "fields": [ "username.ngram" ] } }, { "multi_match": { "analyzer": "keyword", "query": "finley", "boost": 1, "fields": [ "nickname.ngram" ] } } ] } } } }
只要随机种子random_score.seed
不变结果的排序就不会变化,这种方式被称为一致性随机评分。
一致性随机评分机制能够为每一个用户生成一个独有的排序,并支持翻页浏览。
ElasticSearch的搜索结果默认按照_score
进行降序排列,_score
是根据TF-IDF算法得出文档与查询调价匹配度。
在一些状况下咱们但愿自定义排序方式, 好比按建立时间排列。
POST /blog/user/_search { "query" : { "term" : { "uid" : 1 } }, "sort": { "date": { "order": "desc" } } }
{ "query" : { "term" : { "uid" : 1 } }, "sort": { "script_score": { "source": "_score / (params.current - doc['create_at'].value)" "params": { "current": 1534606168 } } } }
ElasticSearch中的Index是最高级的逻辑的结构, 相似于MySQL中的数据库(schema),能够配置独立的搜索策略和存储策略。
ElasticSearch经过倒排索引进行搜索,所谓倒排索引是指对文档进行分析提取关键词,而后创建关键词到文档的索引,当咱们搜索关键词时就能够找到它所关联的文档。
咱们能够经过在Index中配置分析器和映射来设置倒排索引的策略。
分析器是通用的从文档提取关键词的方法,即将文档中某个字段映射为关键字的方法。例如:过滤停用词,分词,添加同义词等。
映射则是具体指定文档中的某个字段应该使用什么分析器来提取关键词。
这个拆分的过程便是分析的过程, 分析执行的操做包括不限于: 字符过滤, 分词, 停用词过滤, 添加同义词.
ES提供了不少内置分析器:
standard
: 默认分析器, 根据Unicode定义删除标点并将词条小写simple
: 根据空格分词并将词条小写whitespace
: 根据空格分词但不将词条小写english
: 过滤英文停用词并将词条还原为词根, 详情参考官方文档.ngram
: 滑动窗口分析器,取文本中全部子串做为关键词。 好比对easy
进行处理能够获得关键词:e
, a
, s
, y
, ea
, as
, sy
, eas
, asy
, easy
。edge-ngram
: 边缘固定滑动窗口分析器,取文本全部从头开始的子串做为关键词。 好比对easy
进行处理能够获得关键词:e
, ea
, eas
, easy
。经常使用于联想搜索,根据用户输入前几个字符进行搜索。此外, 也能够经过配置字符过滤器(char_filter
), 词过滤器(filter
), 分词器(tokenizer
)的方式来自定义分析器。
这里展现基于ngram的分析器定义:
PUT /blog { "settings": { "analysis": { "filter": { "grams_filter": { "type": "ngram", "min_gram": 1, "max_gram": 5 } }, "analyzer": { "gram_analyzer": { "type": "custom", "tokenizer": "standard", "filter": [ "lowercase", "grams_filter" ] } } } } }, mappings: {...} }
自定义分析器的更多信息能够查阅官方文档:
映射则是具体指定文档中的某个字段应该使用什么分析器来提取关键词:
PUT /blog { "settings": { ... }, "mappings": { "user": { "properties": { "nickname": { "type": "string", "analyzer": "gram_analyzer", "fields": { "keyword:": { "type": "keyword" } } }, "status": { "type": "text", "fields": { "keyword:": { "type": "keyword" } } } } } } }
上述JSON中user
项定义了一个根对象, 它一般与文档对应。根对象下能够包含下列几个字段:
文档中每个字段都有3个配置项:
type
: 指定字段类型, 如:text
, long
, double
和date
.index
: 指定字段索引的类型:
no
: 不可被搜索not_analyzed
: 必须精确匹配analyzed
: 使用分析器创建倒排索引analyzer
: 该字段使用的默认分析器fields
: 字段的属性, 能够配置独立的type, index和analyzer。用于将一个字段映射为不一样类型适应不一样用途。在分析器及映射两节中展现了建立索引所需的PUT请求片断,将类型和映射一节中PUT请求的settings字段, 用分析器一节中的settings字段替换便可获得完整建立索引请求。
发送DELETE请求能够删除索引:
DELETE /user
: 删除user索引DELET /user1,user2
: 删除user1和user2两个suoyinDELETE /user*
: 根据通配符删除索引DELET /_all
, DELETE /*
: 删除全部索引GET /_cat/indices
能够列出ElasticSearch上的全部索引。
GET /blog?pretty
列出索引blog的全部信息。
若须要进行中文搜索咱们须要使用中文分析器,它的核心是中文分词器(tokenizer)。
这里咱们使用插件elastic-analysis-ik进行中文搜索。
PUT /post/Post { "settings": { "analysis": { "analyzer": { "cn_smart": { "type": "custom", "tokenizer": "ik_smart", "filter": [ { "type": "length", "min": "3" } ] }, "max_word": { "type": "custom", "tokenizer": "ik_max_word", "filter": [ { "type": "length", "min": "3" } ] } } } }, "mappings": { "user": { "properties": { "content": { "type": "text", "analyzer": "cn_smart", "fields": { "max_word": { "type": "text", "analyzer": "max_word" } } } } } } }
使用match进行查询:
{ "query" : { "match" : { "content" : "搜索" } } }
{ "query" : { "match" : { "content.max_word" : "搜索" } } }
ik_smart
会进行标准的中文分词, ik_max_word
则会试图穷尽全部分词