部分内容整理自:Elasticsearch 7.8 索引创建 / 数据检索_oqzuser12345678999q的博客-优快云博客
ES中有几个关键属性容易混淆:
很多人搞不清楚_source字段里存储的是什么?
store属性的true或false和_source字段有什么关系?
store属性设置为true和_all有什么关系?
index属性又起到什么作用?
什么时候设置store属性为true?
什么时候应该开启_all字段?
默认情况下,Elasticsearch里面有2份内容,一份是原始文档,也就是_source字段里的内容,我们在Elasticsearch中搜索文档,查看的文档内容就是_source中的内容。
另一份是倒排索引,倒排索引中的数据结构是倒排记录表,记录了词项和文档之间的对应关系,比如关键词”中国人”包含在文档ID为1的文档中,倒排记录表中存储的就是这种对应关系,当然也包括词频等更多信息。Elasticsearch底层用的是Lucene的API,Elasticsearch之所以能完成全文搜索的功能就是因为存储的有倒排索引。
那么文档索引到Elasticsearch的时候,默认情况下是对所有字段创建倒排索引的(动态mapping解析出来为数字类型、布尔类型的字段除外),某个字段是否生成倒排索引是由字段的index属性控制的,在Elasticsearch 5之前,index属性的取值有三个:
analyzed:字段被索引,会做分词,可搜索。反过来,如果需要根据某个字段进搜索,index属性就应该设置为analyzed。
not_analyzed:字段值不分词,会被原样写入索引。反过来,如果某些字段需要完全匹配,比如人名、地名,index属性设置为not_analyzed为佳。
no:字段不写入索引,当然也就不能搜索。反过来,有些业务要求某些字段不能被搜索,那么index属性设置为no即可。
再说_all字段,顾名思义,_all字段里面包含了一个文档里面的所有信息,是一个超级字段。以图中的文档为例,如果开启_all字段,那么title+content会组成一个超级字段,这个字段包含了其他字段的所有内容,当然也可以设置只存储某几个字段到_all属性里面或者排除某些字段。
回到图一的第一象限,用户输入关键词" 中国人",分词以后,Elasticsearch从倒排记录表中查找哪些文档包含词项"中国人 ",注意变化,分词之前" 中国人"是用户查询(query),分词之后在倒排索引中" 中国人"是词项(term)。Elasticsearch根据文档ID(通常是文档ID的集合)返回文档内容给用户,如图一第四象限所示。
关键字高亮实质上是根据倒排记录中的词项偏移位置,找到关键词,加上前端的高亮代码。
这里就要说到store属性,store属性用于指定是否将原始字段写入索引,默认取值为no。如果在Lucene中,高亮功能和store属性是否存储息息相关,因为需要根据偏移位置到原始文档中找到关键字才能加上高亮的片段。在Elasticsearch,因为_source中已经存储了一份原始文档,可以根据_source中的原始文档实现高亮,在索引中再存储原始文档就多余了,所以Elasticsearch默认是把store属性设置为no。
注意:如果想要对某个字段实现高亮功能,_source和store至少保留一个。
一、_source配置
_source字段默认是存储的, 什么情况下不用保留_source字段?
如果某个字段内容非常多,业务里面只需要能对该字段进行搜索,最后返回文档id,查看文档内容会再次到mysql或者hbase中取数据,把大字段的内容存在Elasticsearch中只会增大索引,这一点文档数量越大结果越明显,如果一条文档节省几KB,放大到亿万级的量结果也是非常可观的。
如果想要关闭_source字段,在mapping中的设置如下:
{
"yourtype":{
"_source":{
"enabled":false
},
"properties": {
...
}
}
}
如果只想存储某几个字段的原始值到Elasticsearch,可以通过incudes参数来设置,在mapping中的设置如下:
{
"yourtype":{
"_source":{
"includes":["field1","field2"]
},
"properties": {
...
}
}
}
同样,可以通过excludes参数排除某些字段:
{
"yourtype":{
"_source":{
"excludes":["field1","field2"]
},
"properties": {
...
}
}
}
测试一下,禁用_source,首先创建一个索引:
PUT /news1
{
"settings": {
"number_of_shards": 5,
"number_of_replicas": 1,
"index": {
"analysis.analyzer.default.type" : "ik_smart"
}
},
"mappings": {
"_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"
}
}
}
}
添加数据
POST /news1/_doc
{
"news_id": 3,
"title": "实在编不出来了",
"author": "王大猫",
"content": "实在编不出来了,随便写点数据做测试吧,旺旺旺",
"created_at": "2019-03-26 11:55:20"
}
查询发现结果没有_source字段
GET /news1/_doc/_search
"hits" : [
{
"_index" : "news1",
"_type" : "_doc",
"_id" : "WjFeRn0BmrueiDWaaSmS",
"_score" : 1.0
}
]
而不显示声明_source enabled:false,创建相似的索引
PUT /news
{
"settings": {
"number_of_shards": 5,
"number_of_replicas": 1,
"index": {
"analysis.analyzer.default.type" : "ik_smart"
}
},
"mappings": {
"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"
}
}
}
}
POST /news1/_doc
{
"news_id": 2,
"title": "实在编不出来了",
"author": "王大猫",
"content": "实在编不出来了,随便写点数据做测试吧,旺旺旺",
"created_at": "2019-03-26 11:55:20"
}
GET /news/_doc/_search
"hits" : [
{
"_index" : "news",
"_type" : "_doc",
"_id" : "WDFQRn0BmrueiDWaQylt",
"_score" : 1.0,
"_source" : {
"news_id" : 2,
"title" : "我们一起学猫叫",
"author" : "王大猫不会被分词",
"content" : "我们一起学猫叫,还是旺旺旺旺旺,在你面撒个娇,哎呦旺旺旺旺旺,我的尾巴可劲儿摇",
"created_at" : "2019-03-26 11:55:20"
}
}
]
# 指定字段 分词器将根据字段属性做相应分词处理
# author 为 keyword 是不会做分词处理
GET /news/_analyze
{
"text": "我热爱祖国!"
}
# title 的分词结果
GET /news/_analyze
{
"field": "author",
"text": "我热爱祖国!"
}