赞
踩
Elasticsearch中有大量关键概念容易混淆,对于初学者来说是噩梦:
_source
字段里存储了什么?index
属性的作用是什么?_all
字段?store
属性和_source
字段有什么关系?store
属性和_all
字段有什么关系?_source
字段?本文通过问答及展开描述的方式,深入理解Elasticsearch中的_source
、_all
字段和store
、index
属性。
Q:
_source
字段里存储了什么?
A:原始文档的内容。
图1 Elasticsearch中的_source、_all、store和index属性解析
如图1所示, 第二象限是一份原始文档,有title
和content
2个字段,字段取值分别为"我是中国人"和"热爱共产党",这一点没什么可解释的。我们把原始文档写入Elasticsearch,默认情况下,Elasticsearch里面有2份内容,一份是原始文档,也就是_source
字段里的内容,我们在Elasticsearch中搜索文档,查看的文档内容就是_source
中的内容,如图2,相信大家一定非常熟悉这个界面。
图2 _source字段举例
另一份是倒排索引
,倒排索引中的数据结构是倒排记录表,记录了词项和文档之间的对应关系,比如关键词"中国人"包含在文档ID为1的文档中,倒排记录表中存储的就是这种对应关系,当然也包括词频等更多信息。Elasticsearch底层用的是Lucene的API,Elasticsearch之所以能完成全文搜索的功能就是因为存储有倒排索引。如果把倒排索引拿掉,Elasticsearch是不是和mongoDB很像?
Q:
index
属性的作用是什么?
A:控制field是否生成倒排索引以及生成索引时是否做分词。
那么文档索引到Elasticsearch的时候,默认情况下是对所有字段创建倒排索引的(动态mapping解析出来为数字类型、布尔类型的字段除外),某个字段是否生成倒排索引是由字段的index属性控制的,在Elasticsearch 5之前,index
属性的取值有三个:
index
属性就应该设置为analyzed
。index
属性设置为not_analyzed为佳。index
属性设置为no即可。Q:何时应该开启
_all
字段?
A:_all
字段开启适用于不指定搜索某一个字段,根据关键词,搜索整个文档内容。
再说_all
字段,顾名思义,_all
字段是把所有其它字段中的值,以空格为分隔符组成一个大字符串,然后被分析和索引,但是不存储,也就是说它能被查询,但不能被取回显示,是一个超级字段。以图中的文档为例,如果开启_all
字段,那么title和content的值会组成一个超级字段,当然也可以设置只存储某几个字段到_all
属性里面或者排除某些字段。_all
字段在查询时占用更多的CPU和占用更多的磁盘空间,如果确实不需要它可以完全的关闭它或者基于字段定制。
_all
能让你在不知道要查找的内容是属于哪个具体字段的情况下进行搜索,例如:
- PUT my_index/user/1
- {
- "first_name": "John",
- "last_name": "Smith",
- "date_of_birth": "1970-10-24"
- }
- GET my_index/_search
- {
- "query": {
- "match": {
- "_all": "john smith 1970"
- }
- }
- }
得到的结果是:
- {
- "took": 20,
- "timed_out": false,
- "_shards": {
- "total": 5,
- "successful": 5,
- "failed": 0
- },
- "hits": {
- "total": 1,
- "max_score": 0.84748024,
- "hits": [
- {
- "_index": "my_index",
- "_type": "user",
- "_id": "1",
- "_score": 0.84748024,
- "_source": {
- "first_name": "John",
- "last_name": "Smith",
- "date_of_birth": "1970-10-24"
- }
- }
- ]
- }
- }
Q:
store
属性和_source
字段有什么关系?
A:_source
是字段,store
是字段的属性之一,Elasticsearch默认会存储一份_source
字段作为原始文档。store
属性和_source
字段配合进行设置可以决定字段的检索方式和是否高亮展示。
回到图一的第一象限,用户输入关键词"中国人",分词以后,Elasticsearch从倒排记录表中查找哪些文档包含词项"中国人 ",注意变化,分词之前" 中国人"是用户查询(query),分词之后在倒排索引中" 中国人"是词项(term)。Elasticsearch根据文档ID(通常是文档ID的集合)返回文档内容给用户,如图一第四象限所示。
通常情况下,对于用户查询的关键字要做高亮处理,如图3所示:
图3 搜索引擎中的关键字高亮
关键字高亮实质上是根据倒排记录中的词项偏移位置,找到关键词,加上前端的高亮代码。这里就要说到store
属性,store
属性用于指定是否将原始字段写入索引,默认取值为false。如果在Lucene中,高亮功能和store
属性是否存储息息相关,因为需要根据偏移位置到原始文档中找到关键字才能加上高亮的片段。两者不同取值时对字段检索和高亮的影响如下表:
_source\store | true | false |
---|---|---|
enabled | store 为true的字段从倒排索引里检索,浪费IO次数; | 所有字段根据Client类解析存储的 _source JSON串进行检索,仅需一次IO; |
disabled | store 为true的字段从倒排索引里检索,其他字段能检索不能高亮展示; | 所有字段只能检索,不能高亮展示; |
在Elasticsearch,因为_source
中已经存储了一份原始文档,可以根据_source
中的原始文档实现高亮,在索引中再存储原始文档就多余了,所以Elasticsearch默认是把store
属性设置为false。
如果想要对某个字段实现高亮功能,_source
和store
至少保留一个。下面给出一个例子,设置test索引不保存_source,title字段索引但不分析,字段原始值写入索引,content字段为默认属性,代码如下:
- PUT test/test/_mapping
- {
- "test": {
- "_source": {
- "enabled": false
- },
- "properties": {
- "title": {
- "type": "string",
- "index": "not_analyzed",
- "store": "true"
- },
- "content": {
- "type": "string"
- }
- }
- }
- }
- GET test/_search
- {
- "query": {
- "match": {
- "title": "我是中国人"
- }
- },
- "highlight": {
- "fields": {
- "title": {}
- }
- }
- }
- {
- "took": 6,
- "timed_out": false,
- "_shards": {
- "total": 5,
- "successful": 5,
- "failed": 0
- },
- "hits": {
- "total": 1,
- "max_score": 0.30685282,
- "hits": [
- {
- "_index": "test",
- "_type": "test",
- "_id": "1",
- "_score": 0.30685282,
- "highlight": {
- "title": [
- "<em>我是中国人</em>"
- ]
- }
- }
- ]
- }
- }
从返回结果中可以看到,虽然没有保存title字段到_source, 但是依然可以实现搜索高亮。
{"store": true}
既然这么费力不讨好,但是仍然有两个应用场景:
Q:
store
属性和_all
字段有什么关系?
A:_all
字段拥有store
属性,是从属关系。store
属性决定了_all
字段是否可以被高亮展示。
首先store
是_all
字段的一个属性,_all
字段默认不属于_source
,并且store
属性默认为false,所以不能被高亮展示。如果希望被高亮展示,则有两种方式:
_all
字段的store
为true,代价是大量的冗余存储;Q:什么情况下不用保留
_source
字段?
A:需要存储海量文档,Elasticsearch在架构中只做索引,存储选型为类似HBase的NoSQL,不存储_source
可以极大减少存储开销(Elasticsearch往往用的是SSD)。
_source
字段默认是存储的, 什么情况下不用保留_source字段?如果某个字段内容非常多,业务只需要能对该字段进行搜索,最后返回文档id,查看文档内容会再次到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": {
- ...
- }
- }
- }
至此,文章开头提出的几个问题都给出了答案。特别参考了这篇文章:图解Elasticsearch中的_source、_all、store和index属性。
作者:大数据之心
链接:https://www.jianshu.com/p/0908b9ee65fc
来源:简书
简书著作权归作者所有,任何形式的转载都请联系作者获得授权并注明出处。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。