ES的index、store、_source、copy_to和_all的区别

Elasticsearch中有大量关键概念容易混淆,对于初学者来说是噩梦:

  • _source字段里存储了什么?
  • index属性的作用是什么?
  • 何时应该开启_all字段?
  • store属性和_source字段有什么关系?
  • store属性和_all字段有什么关系?
  • 什么情况下不用保留_source字段?

本文通过问答及展开描述的方式,深入理解Elasticsearch中的_source_all字段和storeindex属性。

1.基本概念

1.1._source

存储的原始数据。_source中的内容就是搜索api返回的内容

默认情况下,Elasticsearch里面有2份内容,一份是原始文档,也就是_source字段里的内容,我们在Elasticsearch中搜索文档,查看的文档内容就是_source中的内容。另一份是倒排索引,倒排索引中的数据结构是倒排记录表,记录了词项和文档之间的对应关系。

1.2.index属性

index使用倒排索引存储的是,分词器分析完的词和文档的对应关系。

那么文档索引到Elasticsearch的时候,默认情况下是对所有字段创建倒排索引的(动态mapping解析出来为数字类型、布尔类型的字段除外),某个字段是否生成倒排索引是由字段的index属性控制的,在Elasticsearch 5之前,index属性的取值有三个:

  1. analyzed:字段被索引,会做分词,可搜索。反过来,如果需要根据某个字段进搜索,index属性就应该设置为analyzed。
  2. not_analyzed:字段值不分词,会被原样写入索引。反过来,如果某些字段需要完全匹配,比如人名、地名,index属性设置为not_analyzed为佳。
  3. no:字段不写入索引,当然也就不能搜索。反过来,有些业务要求某些字段不能被搜索,那么index属性设置为no即可。

ES6.3 index属性支持false和true,false不能搜索相当于no,true可以索引。默认使用standard分词

1.3.store

By default, field values are indexed to make them searchable, but they are not stored. This means that the field can be queried, but the original field value cannot be retrieved.

Usually this doesn’t matter. The field value is already part of the _source field, which is stored by default. If you only want to retrieve the value of a single field or of a few fields, instead of the whole_source, then this can be achieved with source filtering.

In certain situations it can make sense to store a field. For instance, if you have a document with atitle, a date, and a very large content field, you may want to retrieve just the title and the datewithout having to extract those fields from a large _source field:

默认为no,被store标记的fields被存储在和index不同的fragment中,以便于快速检索。虽然store占用磁盘空间,但是减少了计算。store的值可以取yes/no或者true/false,默认值是no或者false。

如果在{"store":yes}的情况下,ES会对该字段单独存储倒排索引,每次根据ID检索的时候,会多走一次IO来从倒排索引取数据。

而如果_source enabled 情况下,ES可以直接根据Client类来解析_source JSON,只需一次IO就将所有字段都检索出来了。

如果需要高亮处理,这里就要说到store属性,store属性用于指定是否将原始字段写入索引,默认取值为no。如果在Lucene中,高亮功能和store属性是否存储息息相关,因为需要根据偏移位置到原始文档中找到关键字才能加上高亮的片段。在Elasticsearch,因为_source中已经存储了一份原始文档,可以根据_source中的原始文档实现高亮,在索引中再存储原始文档就多余了,所以Elasticsearch默认是把store属性设置为no。

注意:如果想要对某个字段实现高亮功能,_source和store至少保留一个。

1.4._all

_all: 在6.0+ 中 , 该字段 默认被禁用,建议使用copy_to。再说_all字段,顾名思义,_all字段里面包含了一个文档里面的所有信息,是一个超级字段。如果开启_all字段,那么所有字段会组成一个超级字段,这个字段包含了其他字段的所有内容,空格隔开。当然也可以设置只存储某几个字段到_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"
        }
      }
    ]
  }
}

2.配置

2.1._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": {
            ...
        }
    }
}

测试,首先创建一个索引:

PUT book2

设置mapping,禁用_source:

POST book2/english/_mapping {
   "book2": {
      "_source": {
         "enabled": false
      }
   }
}

插入数据:

POST /book2/english/
{
    "title":"test!",
    "content":"test good Hellow"
}

搜索"test"

POST book2/_search
{
    "query":{
        "term":{
            "title":"test"
        }
    }
}

结果,只返回了id,没有_suorce,任然可以搜索到。

{
    "took": ,
    "timed_out": false,
    "_shards": {
        "total": ,
        "successful": ,
        "skipped": ,
        "failed":
    },
    "hits": {
        "total": ,
        "max_score": 0.2876821,
        "hits": [
            {
                "_index": "book2",
                "_type": "english",
                "_id": "zns1Z2UBYLvVFwGW4Hea",
                "_score": 0.2876821
            }
        ]
    }
}

当_source=false,store和index必须有一个为true,原始数据不保存,倒排索引必须要存储,否则去哪里查询呢,验证下:

POST book3/english/_mapping
{
    "english":{
        "_source": {
         "enabled": false
      },
       "properties": {
         "content":{
             "type":"text",
            "store":"false",
            "index":"no"
        },
         "title":{
             "type":"text",
            "store":"false",
            "index":"no"
        }
      }
    }
}

报错:

{
    "error": {
        "root_cause": [
            {
                "type": "illegal_argument_exception",
                "reason": "Could not convert [content.index] to boolean"
            }
        ],
        "type": "illegal_argument_exception",
        "reason": "Could not convert [content.index] to boolean",
        "caused_by": {
            "type": "illegal_argument_exception",
            "reason": "Failed to parse value [no] as only [true] or [false] are allowed."
        }
    },
    "status":
}

2.2._all配置(copy_to)

  • _all: 在6.0+ 中 , 该字段 默认被禁用,同时在创建index的时候不能 enable;_all 字段能捕获所有字段,它将所有其他字段的值连接成一个大字符串,使用空格作为分隔符,然后 进行分析和索引,但不存储。这意味着它可以被搜索,但不能被检索。 建议使用 copy_to 实现 用户自定义的_all 功能
{
   "yourtype": {
      "_all": {
         "enabled": true
      },
      "properties": {
            ...
      }
   }
}

也可以通过在字段中指定某个字段是否包含在_all中:

{
   "yourtype": {
      "properties": {
         "field1": {
             "type": "string",
             "include_in_all": false
          },
          "field2": {
             "type": "string",
             "include_in_all": true
          }
      }
   }
}

如果要把字段原始值保存,要设置store属性为true,这样索引会更大,需要根据需求使用。下面给出测试代码。
创建test索引:

DELETE  book2 PUT book2

copy_to语法:

POST book2/english/_mapping
{
    "english":{        "properties": {
         "content":{
             "type":"text",
             "copy_to":"all_text"
        },
         "title":{
             "type":"text",
             "copy_to":"all_text"
        },
        "all_text":{
             "type":"text"
        }
      }
    }
}

插入数据:

POST /book2/english/
{
    "title":"test!",
    "content":"test good Hellow"
}

查询:

POST book2/_search{
    "query":{
        "term":{
            "all_text":"test"
        }
    }
}

结果:

{
    "took": ,
    "timed_out": false,
    "_shards": {
        "total": ,
        "successful": ,
        "skipped": ,
        "failed":
    },
    "hits": {
        "total": ,
        "max_score": 0.39556286,
        "hits": [
            {
                "_index": "book2",
                "_type": "english",
                "_id": "0HtjZ2UBYLvVFwGWl3f7",
                "_score": 0.39556286,
                "_source": {
                    "title": "test!god",
                    "content": "test good Hellow"
                }
            },
            {
                "_index": "book2",
                "_type": "english",
                "_id": "z3tjZ2UBYLvVFwGWWXd3",
                "_score": 0.39556286,
                "_source": {
                    "title": "test!",
                    "content": "test good Hellow"
                }
            }
        ]
    }
}

2.3.index和store配置

"index":false设置不可搜索

POST book3/english/_mapping
{
    "english":{
        "_source": {
         "enabled": false
      },
       "properties": {
         "content":{
            "type":"text",
            "store":true,
            "index":false
        },
         "title":{
             "type":"text",
            "store":true,
            "index":false
        }
      }
    }
}

查询:

POST book3/_search
{
    "query":{
        "term":{
            "content":"test"
        }
    }
} 结果:
{
    "error": {
        "root_cause": [
            {
                "type": "query_shard_exception",
                "reason": "failed to create query: {\n  \"term\" : {\n    \"content\" : {\n      \"value\" : \"test\",\n      \"boost\" : 1.0\n    }\n  }\n}",
                "index_uuid": "FvNPHNb7Sa6H757_lKRhpg",
                "index": "book3"
            }
        ],
        "type": "search_phase_execution_exception",
        "reason": "all shards failed",
        "phase": "query",
        "grouped": true,
        "failed_shards": [
            {
                "shard": ,
                "index": "book3",
                "node": "R8t6R20XQritJB_5QVQsvg",
                "reason": {
                    "type": "query_shard_exception",
                    "reason": "failed to create query: {\n  \"term\" : {\n    \"content\" : {\n      \"value\" : \"test\",\n      \"boost\" : 1.0\n    }\n  }\n}",
                    "index_uuid": "FvNPHNb7Sa6H757_lKRhpg",
                    "index": "book3",
                    "caused_by": {
                        "type": "illegal_argument_exception",
                        "reason": "Cannot search on field [content] since it is not indexed."
                    }
                }
            }
        ]
    },
    "status":
}

正确配置:

POST book3/english/_mapping
{
    "english":{
        "_source": {
         "enabled": false
      },
       "properties": {
         "content":{
            "type":"text",
            "store":true,
            "index":"true"
        },
         "title":{
            "type":"text",
            "store":true,
            "index":"true"
        }
      }
    }
}

高亮:

{
    "query":{
        "term":{
            "title":"test"
        }
    },
    "highlight":{
        "fields":{
            "title":{}
        }
    }
}

结果:

{
    "took": ,
    "timed_out": false,
    "_shards": {
        "total": ,
        "successful": ,
        "skipped": ,
        "failed":
    },
    "hits": {
        "total": ,
        "max_score": 0.2876821,
        "hits": [
            {
                "_index": "book3",
                "_type": "english",
                "_id": "2nt_Z2UBYLvVFwGWfXcn",
                "_score": 0.2876821,
                "highlight": {
                    "title": [
                        "<em>test</em>!"
                    ]
                }
            }
        ]
    }
}

2.4_source与store 的关系

_source\store true False
enabled store为true的字段从倒排索引里检索,浪费IO次数; 所有字段根据Client类解析存储的 _source JSON串进行检索,仅需一次IO;
disabled store为true的字段从倒排索引里检索,其他字段能检索不能高亮展示; 所有字段只能检索,不能高亮展示;

在Elasticsearch,因为_source中已经存储了一份原始文档,可以根据_source中的原始文档实现高亮,在索引中再存储原始文档就多余了,所以Elasticsearch默认是把store属性设置为false

https://www.bbsmax.com/A/x9J22oONJ6/

https://www.bbsmax.com/A/amd0vMq1Jg/

posted @ 2023-01-10 09:38  未月廿三  阅读(367)  评论(0编辑  收藏  举报