Elasticsearch中的_source、_all、store、index

本文深入解析Elasticsearch中数据的存储机制,包括_source、_all字段的功能与使用场景,以及store属性对字段存储的影响,帮助读者理解如何高效管理和查询数据。

首先要明确一份数据在进入es中之后通常是如何存储的

原始文档数据在进入es中之后,es会将其存为两份,一部分是对其中的索引字段数据进行分词索引,然后存储所有的分词索引结果,这个结果并不一定是整个文档数据(通常都指定索引部分字段)。而另一份则是原始文档数据,而该文档的所有的分词索引都会指向该文档。

1. _source的作用

_source就是指源文档的存储,你可以理解为是es将你传输的原始文档数据放在了_source字段中存储,该设置是用来控制是否存储源文档数据的,如果将该设置设置为false,那就表示不会存储源文档的数据。会出现的问题就是导致查询的时候无法完整展示所有文档数据,只能看到索引中的数据。在检索数据时,最终检索结果实际上就是_source中的数据。

2._all的作用

与_source类似,_all也是一个字段,同样也是存储了完整的文档数据,但不同的是_all是一个超级字段。以图中的文档为例,如果开启_all字段,那么name+author+content会组成一个超级字段,这个字段包含了其他字段的所有内容,当然也可以设置只存储某几个字段到_all属性里面或者排除某些字段,该字段默认不会被存储,也就是说只会在进行分词索引时存在,分词索引结束后该字段就不会存在,如果要存储该字段,对_all字段开启store属性即可。

_all主要是在当检索内容无法确定是在哪个字段上的时候,比较适合开启该字段。

3.文档对象中设置字段的store属性作用

@Document(indexName = "item",type = "docs", shards = 1, replicas = 0)
public class Item {
    @Id
    private Long id;

    @Field(type = FieldType.Text, analyzer = "ik_max_word", store = false)
    private String title; //标题

    @Field(type = FieldType.Keyword)
    private String category;// 分类

    @Field(type = FieldType.Keyword)
    private String brand; // 品牌

    @Field(type = FieldType.Double)
    private Double price; // 价格

    @Field(index = false, type = FieldType.Keyword)
    private String images; // 图片地址

    public Item() {
    }
    
}

store属性设置作用于文档中的某个字段之上,表示是否开启索引字段原数据存储,其实简单来理解就是将_source的范围缩小为某个属性字段,设置为true后,es会将该属性字段的原数据额外单独存储一份,即该字段的原数据会存储两份,一份是在原始文档,另一份则是单独该字段的存储。这也是为什么es的API中默认将该属性设为false,因为其与_source字段重复存储了同一部分数据。

_source和store两个设置任意为true都能够对字段实现高亮查询,因为高亮查询必须保证原字段数据的存储,才能进行高亮查询。

通常不建议开启该属性,store属性和_source字段两个设置同时开启会存储重复数据。而且当通过索引检索时,检索的结果中如果包含store属性字段,该store属性字段的内容不会从原始文档中读取,而是额外进行一次IO,读取该字段的单独存储的文档,所以会损失部分性能。

总结:

(1)_source和_all其实都是两个字段,只不过区别是_source是存储的结构化的原始文档,而_all是存储的是一个所有field字段拼接而成的字符串,两者是有区别的。

(2)store是作用于field(字段)上的属性,决定该field是否单独存储一份文档,该属性可以作用于_all字段上,但与_source字段重复。

 

<think>我们正在处理Elasticsearch文档,目标是提取与`_source`同级的`fields`下的`sinkTime`和`timestamp`字段值。根据引用[3],运行时字段(Runtime Fields)是在查询时动态计算的字段,但这里我们要提取的是已经存储在文档中的字段(在`fields`部分)。 在Elasticsearch中,文档的原始内容存储在`_source`字段中,但有时为了优化,我们可能只存储某些字段(使用`store`参数设置为true)以便单独检索。这些存储的字段会出现在返回结果的`fields`部分(与`_source`同级)。 因此,我们需要在查询时指定返回的字段(`fields`参数)来获取`sinkTime`和`timestamp`。 步骤: 1. 在查询中,使用`_source`参数来控制是否返回`_source`(这里我们不需要`_source`,所以可以设置为false)。 2. 使用`fields`参数指定要返回的字段,即`sinkTime`和`timestamp`。 示例查询(使用REST API): ```json { "_source": false, "fields": [ "sinkTime", "timestamp" ], "query": { "match_all": {} } } ``` 注意:要能够通过`fields`参数检索这些字段,这些字段在映射中必须被设置为`store: true`(默认是false)。如果没有设置,那么即使它们在`_source`中存在,也无法通过`fields`返回,除非使用运行时字段(但运行时字段是动态计算的,不是存储的)。 引用[1]中提到:为了平衡搜索性能和灵活性,请索引你经常搜索和过滤的字段。这里我们提取的字段可能是经常被检索的,因此建议在映射中设置`store: true`。 如果字段没有被存储(即`store: false`),那么我们只能从`_source`中提取。但是,如果只需要这两个字段,我们可以通过`_source`过滤来获取: ```json { "_source": ["sinkTime", "timestamp"], "query": { "match_all": {} } } ``` 这样,返回的文档中将只包含`_source`,而`_source`中只包含指定的两个字段。 总结: 有两种方法: 方法一:使用`fields`参数(要求字段在映射中设置`store: true`) 方法二:使用`_source`过滤(不需要字段被存储,但需要从`_source`中解析) 根据你的需求选择合适的方法。如果字段已经被存储,使用方法一更高效(因为不需要解析`_source`);否则使用方法二。 如果文档中确实有与`_source`同级的`fields`(即存储字段),那么方法一就是正确的。 因此,查询的构建取决于你的映射设置。 示例代码(使用Python客户端): ```python from elasticsearch import Elasticsearch es = Elasticsearch() # 方法一:使用fields参数(要求字段被存储) resp = es.search( index="your_index", _source=False, fields=["sinkTime", "timestamp"], body={ "query": { "match_all": {} } } ) # 处理结果 for hit in resp['hits']['hits']: sink_time = hit['fields'].get('sinkTime') timestamp = hit['fields'].get('timestamp') # 注意:存储的字段返回的是数组形式(即使只有一个值),所以可能需要取第一个元素 # 例如:sink_time = sink_time[0] if sink_time else None # 方法二:使用_source过滤(不需要字段被存储) resp = es.search( index="your_index", _source=["sinkTime", "timestamp"], body={ "query": { "match_all": {} } } ) for hit in resp['hits']['hits']: source = hit.get('_source', {}) sink_time = source.get('sinkTime') timestamp = source.get('timestamp') ``` 注意:在方法一中,返回的`fields`中的每个字段都是一个数组(因为Elasticsearch允许一个字段有多个值)。所以,如果这些字段是单值的,你可能需要取第一个元素(如上面的注释所示)。而在方法二中,返回的是字段的原始类型(因为是从`_source`中解析出来的)。 根据引用[2],Elasticsearch具有字段类型推断的能力,但这里我们直接使用存储的字段,所以类型应该是映射中定义的类型。 请根据实际情况选择合适的方法。</think>在 Elasticsearch 中提取与 `_source` 同级的 `fields` 下的 `sinkTime` 和 `timestamp` 字段值,可以通过以下方法实现: ### 1. 使用 `fields` 参数检索存储字段 当字段被显式设置为 `store: true` 时,可以直接通过 `fields` 参数检索: ```json GET your_index/_search { "_source": false, // 不返回_source "fields": ["sinkTime", "timestamp"], // 指定要提取的字段 "query": { "match_all": {} // 查询条件 } } ``` **返回结果示例**: ```json { "hits": { "hits": [ { "fields": { "sinkTime": ["2023-10-05T08:30:00Z"], // 存储字段值 "timestamp": ["2023-10-05T08:30:00.123Z"] } } ] } } ``` **注意**: - 要求字段在映射中明确设置 `store: true`(默认值为 `false`) - 返回值为数组格式,即使只有一个值[^3] --- ### 2. 使用运行时字段动态提取(无需预存储) 如果字段未存储,可通过运行时字段动态计算: ```json GET your_index/_search { "runtime_mappings": { "runtime_sinkTime": { "type": "date", "script": "emit(doc['sinkTime'].value)" // 从_source动态计算 }, "runtime_timestamp": { "type": "date", "script": "emit(doc['timestamp'].value)" } }, "fields": ["runtime_sinkTime", "runtime_timestamp"] } ``` **优势**: - 无需修改索引映射 - 灵活处理未存储的字段[^1][^3] --- ### 3. 结合索引字段和运行时字段(推荐) 为平衡性能与灵活性: ```json GET your_index/_search { "runtime_mappings": { "combined_time": { "type": "date", "script": """ def sink = doc.containsKey('sinkTime') ? doc['sinkTime'].value : null; def ts = doc['timestamp'].value; emit(sink != null ? sink : ts); // 优先使用sinkTime """ } }, "fields": ["combined_time"] } ``` 此方法优先使用索引字段,减少运行时计算开销[^1]。 --- ### 关键区别总结 | 方法 | 是否需要 `store:true` | 性能影响 | 适用场景 | |------|----------------------|----------|----------| | `fields`参数 | 必需 | ⭐⭐⭐⭐高 | 高频访问的存储字段 | | 运行时字段 | 不需要 | ⭐⭐中 | 临时分析或未存储字段 | | 混合方案 | 部分需要 | ⭐⭐⭐高 | 平衡性能与灵活性 | **最佳实践**: 1. 高频查询字段(如 `timestamp`)设置为 `store: true` 2. 低频或临时字段使用运行时字段 3. 通过 `doc_values` 优化数值型字段访问[^1][^2] --- ### 示例:Python客户端实现 ```python from elasticsearch import Elasticsearch es = Elasticsearch() # 提取存储字段 response = es.search( index="your_index", _source=False, fields=["sinkTime", "timestamp"], body={"query": {"match_all": {}}} ) for hit in response["hits"]["hits"]: print(f"SinkTime: {hit['fields']['sinkTime'][0]}") print(f"Timestamp: {hit['fields']['timestamp'][0]}") ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值