Elasticsearch 索引创建 / 数据检索
- es 6.0 开始不推荐一个
index
下多个type
的模式,并且会在 7.0 中完全移除。在 6.0 的index
下是无法创建多个type
的,type
带来的字段类型冲突和检索效率下降的问题,导致了type
会被移除。(5.x到6.x) _all
字段也被舍弃了,使用copy_to
自定义联合字段。(5.x到6.x)type:text/keyword
来决定是否分词,index: true/false
决定是否索引(2.x到5.x)analyzer
来单独设定分词器(2.x到5.x)
创建索引
先把 ik
装上,重启服务。
# 使用 elasticsearch-plugin 安装 elasticsearch-plugin install \ https://github.com/medcl/elasticsearch-analysis-ik/releases/download/v6.6.2/elasticsearch-analysis-ik-6.6.2.zip
文档字段类型参考:
https://www.elastic.co/guide/...
文档字段其他参数参考(不同字段类型可能会有相应的特征属性):
https://www.elastic.co/guide/...
我们新建一个名news
的索引
:
设定默认分词器为ik分词器用来处理中文
使用默认名 _doc 定义 type
故意关闭_source存储(用来验证 store 选项)
title 不存储 author 不分词 content 存储
_source
字段的含义可以看下这篇博文:https://blog.csdn.net/napoay/...
PUT /news { "settings": { "number_of_shards": 5, "number_of_replicas": 1, "index": { "analysis.analyzer.default.type" : "ik_smart" } }, "mappings": { "_doc": { "_source": { "enabled": false }, "properties": { "news_id": { "type": "integer", "index": true }, "title": { "type": "text", "store": false }, "author": { "type": "keyword" }, "content": { "type": "text", "store": true }, "created_at": { "type": "date", "format": "yyyy-MM-dd hh:mm:ss" } } } } } # 查看创建的结构 GET /news/_mapping
验证分词器是否生效
# 验证分词插件是否生效 GET /_analyze { "analyzer": "ik_smart", "text": "我热爱祖国" } GET /_analyze { "analyzer": "ik_max_word", "text": "我热爱祖国" }
# 索引的默认分词器 GET /news/_analyze { "text": "我热爱祖国!" }
# 指定字段 分词器将根据字段属性做相应分词处理 # author 为 keyword 是不会做分词处理 GET /news/_analyze { "field": "author" "text": "我热爱祖国!" } # title 的分词结果 GET /news/_analyze { "field": "title" "text": "我热爱祖国!" }
添加文档
用于演示,后面的查询会以这些文档为例。
POST /news/_doc { "news_id": 1, "title": "我们一起学旺叫", "author": "才华横溢王大猫", "content": "我们一起学旺叫,一起旺旺旺旺旺,在你面撒个娇,哎呦旺旺旺旺旺,我的尾巴可劲儿摇", "created_at": "2019-03-26 11:55:20" } { "news_id": 2, "title": "我们一起学猫叫", "author": "王大猫不会被分词", "content": "我们一起学猫叫,还是旺旺旺旺旺,在你面撒个娇,哎呦旺旺旺旺旺,我的尾巴可劲儿摇", "created_at": "2019-03-26 11:55:20" } { "news_id": 3, "title": "实在编不出来了", "author": "王大猫", "content": "实在编不出来了,随便写点数据做测试吧,旺旺旺", "created_at": "2019-03-26 11:55:20" }
检索数据
GET /news/_doc/_search
为查询news
下_doc
的文档的接口,我们用 restApi+DSL
演示
match_all
即无检索条件获取全部数据
#无条件分页检索 以 news_id 排序 GET /news/_doc/_search { "query": { "match_all": {} }, "from": 0, "size": 2, "sort": { "news_id": "desc" } }
因为我们关掉了_source
字段,即 ES
只会对数据建立倒排索引
,不会存储其原数据,所以结果里没有相关文档原数据内容。关掉的原因主要是想演示highlight
机制。
match
普通检索,很多文章都说match
查询会对查询内容进行分词,其实并不完全正确,match
查询也要看检索的字段type
类型,如果字段类型本身就是不分词的keyword
(not_analyzed
),那match
就等同于term
查询了。
我们可以通过分词器explain
一下字段会被如何处理:
GET /news/_analyze { "filed": "title", "text": "我会被如何处理呢?分词?不分词?" }
查询
GET /news/_doc/_search { "query": { "match": { "title": "我们会被分词" } }, "highlight": { "fields": { "title": {} } } }
通过highlight
我们可以将检索到的关键词以高亮的方式返回上下文内容,如果关闭了_source
就得开启字段的store
属性存储字段的原数据,这样才能做高亮处理,不然没有原内容了,也就没办法高亮关键词了
multi_match
对多个字段进行检索,比如我想查询title
或content
中有我们
关键词的文档,如下即可:
GET /news/_doc/_search { "query": { "multi_match": { "query": "我们是好人", "fields": ["title", "content"] } }, "highlight": { "fields": { "title": {}, "content": {} } } }
match_phrase
这个需要认证理解一下,match_phrase
,短语查询,何为短语查询呢?简单来说即被查询的文档字段中要包含查询内容
被分词解析后的所有关键词
,且关键词
在文档中的分布距离差offset
要满足slop
设定的阈值。slop
表征可以将关键词
平移几次来满足在文档中的分布,如果slop
足够的大,那么即便所有关键词
在文档中分布的很离散,也是可以通过平移满足的。
content: i love china match_phrase: i china slop: 0//查不到 需要将 i china 的 china 关键词 slop 1 后变为 i - china 才能满足 slop: 1//查得到
测试实例
# 先看下查询会被如何解析分词 GET /news/_analyze { "field": "title", "text": "我们学" } # reponse { "tokens": [ { "token": "我们", "start_offset": 0, "end_offset": 2, "type": "CN_WORD", "position": 0 }, { "token": "学", "start_offset": 2, "end_offset": 3, "type": "CN_CHAR", "position": 1 } ] }
# 再看下某文档的title是被怎样建立倒排索引的 GET /news/_analyze { "field": "title", "text": "我们一起学旺叫" } # reponse { "tokens": [ { "token": "我们", "start_offset": 0, "end_offset": 2, "type": "CN_WORD", "position": 0 }, { "token": "一起", "start_offset": 2, "end_offset": 4, "type": "CN_WORD", "position": 1 }, { "token": "学", "start_offset": 4, "end_offset": 5, "type": "CN_CHAR", "position": 2 }, ... ] }
注意position
字段,只有slop
的阈值大于
两个不相邻的关键词的position
差时,才能满足平移关键词
至查询内容短语
分布的位置条件。
查询内容被分词为:["我们", "学"]
,而文档中["我们", "学"]
两个关键字的距离为 1
,所以,slop
必须大于等于1
,此文档才能被查询到。
使用查询短语模式:
GET /news/_doc/_search { "query": { "match_phrase": { "title": { "query": "我们学", "slop": 1 } } }, "highlight": { "fields": { "title": {} } } }
查询结果:
{ ... { "_index": "news", "_type": "_doc", "_id": "if-CuGkBddO9SrfVBoil", "_score": 0.37229446, "highlight": { "title": [ "<em>我们</em>一起<em>学</em>猫叫" ] } }, { "_index": "news", "_type": "_doc", "_id": "iP-AuGkBddO9SrfVOIg3", "_score": 0.37229446, "highlight": { "title": [ "<em>我们</em>一起<em>学</em>旺叫" ] } } ... }
term
term
要理解只是不对查询条件
分词,作为一个关键词
去检索索引。但文档存储时字段是否被分词建立索引由_mappings
时设定了。可能有["我们", "一起"]
两个索引,但并没有["我们一起"]
这个索引,查询不到。keyword
类型的字段则存储时不分词,建立完整索引,查询时也不会对查询条件
分词,是强一致性的。
GET /news/_doc/_search { "query": { "term": { "title": "我们一起" } }, "highlight": { "fields": { "title": {} } } }
terms
terms
则是给定多个关键词
,就好比人工分词
{ "query": { "terms": { "title": ["我们", "一起"] } }, "highlight": { "fields": { "title": {} } } }
满足["我们", "一起"]
任意关键字的文档都能被检索到。
wildcard
shell
通配符查询: ?
一个字符 *
多个字符,查询倒排索引
中符合pattern
的关键词。
查询有两个字符的关键词的文档
{ "query": { "wildcard": { "title": "??" } }, "highlight": { "fields": { "title": {}, "content": {} } } }
prefix
前缀查询,查询倒排索引
中符合pattern
的关键词。
{ "query": { "prefix": { "title": "我" } }, "highlight": { "fields": { "title": {}, "content": {} } } }
regexp
正则表达式查询,查询倒排索引
中符合pattern
的关键词。
查询含有2 ~ 3 个字符的关键词的文档
{ "query": { "regexp": { "title": ".{2,3}" } }, "highlight": { "fields": { "title": {}, "content": {} } } }
bool
布尔查询通过 bool
链接多个查询组合:must
:必须全满足must_not
:必须全不满足should
:满足一个即可
{ "query": { "bool": { "must": { "match": { "title": "绝对要有我们" } }, "must_not": { "term": { "title": "绝对不能有我" } }, "should": [ { "match": { "content": "我们" } }, { "multi_match": { "query": "满足", "fields": ["title", "content"] } }, { "match_phrase": { "title": "一个即可" } } ], "filter": { "range": { "created_at": { "lt": "2020-12-05 12:00:00", "gt": "2019-01-05 12:00:00" } } } } }, "highlight": { "fields": { "title": {}, "content": {} } } }
filter
filter
通常情况下会配合match
之类的使用,对符合查询条件的数据进行过滤。
{ "query": { "bool": { "must": { "match_all": {} }, "filter": { "range": { "created_at": { "lt": "2020-12-05 12:00:00", "gt": "2017-12-05 12:00:00" } } } } } }
或者单独使用
{ "query": { "constant_score" : { "filter": { "range": { "created_at": { "lt": "2020-12-05 12:00:00", "gt": "2017-12-05 12:00:00" } } } } } }
多个过滤条件:2017-12-05 12:00:00 <= created_at < 2020-12-05 12:00:00 and news_id >= 2
{ "query": { "constant_score" : { "filter": { "bool": { "must": [ { "range": { "created_at": { "lt": "2020-12-05 12:00:00", "gt": "2017-12-05 12:00:00" } } }, { "range": { "news_id": { "gte": 2 } } } ] } } } } }
相关推荐
另外一部分,则需要先做聚类、分类处理,将聚合出的分类结果存入ES集群的聚类索引中。数据处理层的聚合结果存入ES中的指定索引,同时将每个聚合主题相关的数据存入每个document下面的某个field下。