Elasticsearch:从搜索中获取选定的字段 fields

您所在的位置:网站首页 elasticsearchclient查询 Elasticsearch:从搜索中获取选定的字段 fields

Elasticsearch:从搜索中获取选定的字段 fields

#Elasticsearch:从搜索中获取选定的字段 fields| 来源: 网络整理| 查看: 265

在实际的搜索返回数据中,我们经常会用选择地返回所需要的字段或部分的 source。这在某些情况下非常有用,因为对于大规模的数据来说,返回的数据大小直接影响网路带宽的使用以及内存的使用。默认情况下,搜索响应中的每个匹配都包含文档 _source,这是在为文档建立索引时提供的整个 JSON 对象。 要检索搜索响应中的特定字段,可以使用 fields 参数:

POST my-index-000001/_search { "query": { "match": { "message": "foo" } }, "fields": ["user.id", "@timestamp"], "_source": false }

fields 参数同时查询文档的 _source 和索引 mapping,以加载和返回值。因为它利用了 mapping,所以与直接引用 _source 相比,字段具有一些优点:它接受 multi-fields 和字段别名,并且还以一致的方式设置诸如日期之类的字段值的格式。

文档的 _source 存储在 Lucene 中的单个字段中。因此,即使仅请求少量字段,也必须加载并解析整个 _source 对象。为避免此限制,你可以尝试另一种加载字段的方法:

使用 docvalue_fields 参数获取选定字段的值。当返回相当少量的支持 doc 值的字段(例如关键字和日期)时,这是一个不错的选择。 使用 stored_fields 参数获取特定存储字段(使用 store 映射选项的字段)的值。

你还可以使用 script_field 参数通过脚本来转换响应中的字段值。

你可以在以下各节中找到有关这些方法的更多详细信息:

Fields Doc value fields Stored fields Source filtering Script fields Fields

fields 参数允许检索搜索响应中的文档字段列表。 它同时查阅文档 _source 和索引 mapping,以符合其映射类型的标准化方式返回每个值。 默认情况下,日期字段是根据其 mapping 中的日期格式参数设置格式的。 你还可以使用 fields 参数来检索运行时字段值。我们使用如下的文档作为例子:

PUT developers/_doc/1 { "name": { "firstname": "san", "secondname": "zhang" }, "age": 20, "city": "Beijing", "skills": ["Java", "C++"], "DOB": "1988-06-04" } PUT developers/_doc/2 { "name": { "firstname": "si", "secondname": "li" }, "age": 30, "city": "Shanghai", "skills": ["Ruby", "C++"], "DOB": "1999-07-08" }

在上面,我们创建了一个叫做 developers 的索引。其中的 DOB 字段指的是 date of birth。上面的 developer 的 mapping 是:

GET developers/_mapping { "developers" : { "mappings" : { "properties" : { "DOB" : { "type" : "date" }, "age" : { "type" : "long" }, "city" : { "type" : "text", "fields" : { "keyword" : { "type" : "keyword", "ignore_above" : 256 } } }, "name" : { "properties" : { "firstname" : { "type" : "text", "fields" : { "keyword" : { "type" : "keyword", "ignore_above" : 256 } } }, "secondname" : { "type" : "text", "fields" : { "keyword" : { "type" : "keyword", "ignore_above" : 256 } } } } }, "nianling" : { "type" : "alias", "path" : "age" }, "skills" : { "type" : "text", "fields" : { "keyword" : { "type" : "keyword", "ignore_above" : 256 } } } } } } }

以下搜索请求使用 fields 参数来检索 city 字段,以 skil*开头的所有字段, name.firstname 以及 DOB 字段的值:

GET developers/_search { "query": { "match": { "city": "Beijing" } }, "fields": [ "name.firstname", "ski*", "nianling", "city", { "field": "DOB", "format": "epoch_millis" } ] }

在上面,我也使用了一个 alias 字段 nianling。上面的显示结果为:

{ "took" : 0, "timed_out" : false, "_shards" : { "total" : 1, "successful" : 1, "skipped" : 0, "failed" : 0 }, "hits" : { "total" : { "value" : 1, "relation" : "eq" }, "max_score" : 0.6931471, "hits" : [ { "_index" : "developers", "_type" : "_doc", "_id" : "1", "_score" : 0.6931471, "_source" : { "name" : { "firstname" : "san", "secondname" : "zhang" }, "age" : 20, "city" : "Beijing", "skills" : [ "Java", "C++" ], "DOB" : "1988-06-04" }, "fields" : { "skills" : [ "Java", "C++" ], "name.firstname" : [ "san" ], "city" : [ "Beijing" ], "DOB" : [ "612921600000" ], "nianling" : [ 20 ], "skills.keyword" : [ "Java", "C++" ] } } ] } }

如果我们不想要 _source,我们也可以直接使用如下的查询:

GET developers/_search { "query": { "match": { "city": "Beijing" } }, "fields": [ "name.firstname", "ski*", "nianling", "city", { "field": "DOB", "format": "epoch_millis" } ], "_source": false }

上面显示的结果为:

{ "took" : 0, "timed_out" : false, "_shards" : { "total" : 1, "successful" : 1, "skipped" : 0, "failed" : 0 }, "hits" : { "total" : { "value" : 1, "relation" : "eq" }, "max_score" : 0.6931471, "hits" : [ { "_index" : "developers", "_type" : "_doc", "_id" : "1", "_score" : 0.6931471, "fields" : { "skills" : [ "Java", "C++" ], "name.firstname" : [ "san" ], "city" : [ "Beijing" ], "DOB" : [ "612921600000" ], "nianling" : [ 20 ], "skills.keyword" : [ "Java", "C++" ] } } ] } }

在上面我们看到 DOB 是以我们想要的格式进行显示的。我们也可以使用 ski* 来显示 multi-fields 字段 skills 以及 skill.keyword。fields 不允许返回整个对象。它只能返回 leaf field。

在这里特别指出的是,我们可以直接可以通过  source filtering 的方法来返回 _source 中的部分字段:

GET developers/_search { "query": { "match": { "city": "Beijing" } }, "_source": ["city", "age", "name"] }

上面搜索的返回结果为:

{ "took" : 0, "timed_out" : false, "_shards" : { "total" : 1, "successful" : 1, "skipped" : 0, "failed" : 0 }, "hits" : { "total" : { "value" : 1, "relation" : "eq" }, "max_score" : 0.6931471, "hits" : [ { "_index" : "developers", "_type" : "_doc", "_id" : "1", "_score" : 0.6931471, "_source" : { "city" : "Beijing", "name" : { "secondname" : "zhang", "firstname" : "san" }, "age" : 20 } } ] } }

fields 参数处理字段类型,例如字段 aliases 和 constant_keyword,其值并不总是出现在 _source 中。 还应其他映射选项也被考虑,包括 ignore_above,ignore_malformed 和null_value。

注意:即使 _source 中只有一个值,fields 响应也总是为每个字段返回一个值数组。 这是因为 Elasticsearch 没有专用的数组类型,并且任何字段都可以包含多个值。 fields 参数也不能保证以特定顺序返回数组值。 有关更多背景信息,请参见映射文档中的 array。

目前在 Kibana 中的 Discover 中,基本都是使用 fiields 而不 source 来进行显示的:

处理 nested 字段

nested 字段的字段响应与常规对象字段的字段响应略有不同。 常规对象字段内的 leaf value 作为扁平列表返回时,nested 字段内的值被分组以维护原始 nested 数组内每个对象的独立性。 对于 nested 字段数组内的每个条目,除非父 nested 对象内还有其他 nested 字段,否则值将再次作为一个扁平列表返回,在这种情况下,将对较深的 nested 字段再次重复相同的过程。

给定以下 mapping,其中 user 是一个 nested 字段,在索引以下文档并检索到 user 字段下的所有字段之后:

PUT my-index-000001 { "mappings": { "properties": { "group" : { "type" : "keyword" }, "user": { "type": "nested", "properties": { "first" : { "type" : "keyword" }, "last" : { "type" : "keyword" } } } } } } PUT my-index-000001/_doc/1?refresh=true { "group" : "fans", "user" : [ { "first" : "John", "last" : "Smith" }, { "first" : "Alice", "last" : "White" } ] } POST my-index-000001/_search { "fields": ["*"], "_source": false }

响应会将 first 和 last 分组,而不是将它们作为扁平列表返回。

{ "took" : 1, "timed_out" : false, "_shards" : { "total" : 1, "successful" : 1, "skipped" : 0, "failed" : 0 }, "hits" : { "total" : { "value" : 1, "relation" : "eq" }, "max_score" : 1.0, "hits" : [ { "_index" : "my-index-000001", "_type" : "_doc", "_id" : "1", "_score" : 1.0, "fields" : { "user" : [ { "last" : [ "Smith" ], "first" : [ "John" ] }, { "last" : [ "White" ], "first" : [ "Alice" ] } ], "group" : [ "fans" ] } } ] } }

无论用于搜索它们的模式如何,nested 字段都将按其 nested 路径进行分组。 例如,在上面的示例中仅查询 user.first 字段:

POST my-index-000001/_search { "fields": ["user.first"], "_source": false }

将仅返回用户的 first 字段,但仍保持 nested 用户数组的结构:

{ "took" : 0, "timed_out" : false, "_shards" : { "total" : 1, "successful" : 1, "skipped" : 0, "failed" : 0 }, "hits" : { "total" : { "value" : 1, "relation" : "eq" }, "max_score" : 1.0, "hits" : [ { "_index" : "my-index-000001", "_type" : "_doc", "_id" : "1", "_score" : 1.0, "fields" : { "user" : [ { "first" : [ "John" ] }, { "first" : [ "Alice" ] } ] } } ] } }

但是,当字段模式直接定位嵌套的 user 字段时,由于该模式与任何 leaf field 都不匹配,因此不会返回任何值。就像上面所说的那样,它不能返回任何 Object。

检索未映射的字段

默认情况下,fields 参数仅返回映射字段的值。 但是,Elasticsearch 允许将未映射的字段存储在 _source 中,例如,通过将 “动态字段映射” 设置为 false 或使用具有 enable: false的对象字段,从而禁用对其内容的解析和索引编制。

可以使用字段部分中的 include_unmapped 选项从 _source 检索此类对象中的字段:

PUT my-index-000002 { "mappings": { "enabled": false } }

在上面,我们对索引 my-index-000002 禁止所有的 mapping。它也意味着我们不能对它做任何的搜索:

GET my-index-000002/_search { "query": { "match": { "user_id": "kimchy" } } }

上面的搜索将不会返回任何的数值。

PUT my-index-000002/_doc/1?refresh=true { "user_id": "kimchy", "session_data": { "object": { "some_field": "some_value" } } } POST my-index-000002/_search { "fields": [ "user_id", { "field": "session_data.object.*", "include_unmapped" : true } ], "_source": false }

即使未映射字段,响应也将在 session_data.object.* 路径下包含字段结果。由于未映射但 user_id 字段模式的 include_unmapped 标志未设置为 true,因此响应中将不包含 user_id。上面的搜索结果为:

{ "took" : 0, "timed_out" : false, "_shards" : { "total" : 1, "successful" : 1, "skipped" : 0, "failed" : 0 }, "hits" : { "total" : { "value" : 1, "relation" : "eq" }, "max_score" : 1.0, "hits" : [ { "_index" : "my-index-000002", "_type" : "_doc", "_id" : "1", "_score" : 1.0, "fields" : { "session_data.object.some_field" : [ "some_value" ] } } ] } } Doc value 字段

你可以使用 docvalue_fields 参数返回搜索响应中一个或多个字段的 doc value。

Doc value 存储与 _source 相同的值,但采用基于磁盘的基于列的结构,该结构针对排序和聚合进行了优化。 由于每个字段都是单独存储的,因此 Elasticsearch 仅读取请求的字段值,并且可以避免加载整个文档 _source。

默认情况下,将为支持的字段存储文档值。 但是,text 或 text_annotated 字段不支持  doc  value。

PUT developers/_doc/1 { "name": { "firstname": "san", "secondname": "zhang" }, "age": 20, "city": "Beijing", "skills": ["Java", "C++"], "DOB": "1988-06-04" } PUT developers/_doc/2 { "name": { "firstname": "si", "secondname": "li" }, "age": 30, "city": "Shanghai", "skills": ["Ruby", "C++"], "DOB": "1999-07-08" }

以下搜索请求使用 docvalue_fields 参数检索 age 字段,以 ski*开头的所有字段以及 DOB 字段的 doc value:

GET developers/_search { "query": { "match": { "city": "Beijing" } }, "docvalue_fields": [ "age", "ski*.keyword", { "field": "date", "format": "epoch_millis" } ] }

提示:你不能使用 docvalue_fields 参数来检索 nested 对象的 doc value。 如果指定 nested 对象,则搜索将为该字段返回一个空数组([])。 要访问 nested 字段,请使用inner_hits 参数的 docvalue_fields 属性。

上面的搜索结果为:

{ "took" : 0, "timed_out" : false, "_shards" : { "total" : 1, "successful" : 1, "skipped" : 0, "failed" : 0 }, "hits" : { "total" : { "value" : 1, "relation" : "eq" }, "max_score" : 0.6931471, "hits" : [ { "_index" : "developers", "_type" : "_doc", "_id" : "1", "_score" : 0.6931471, "_source" : { "name" : { "firstname" : "san", "secondname" : "zhang" }, "age" : 20, "city" : "Beijing", "skills" : [ "Java", "C++" ], "DOB" : "1988-06-04" }, "fields" : { "age" : [ 20 ], "skills.keyword" : [ "C++", "Java" ] } } ] } } Stored fields

也可以使用 store 映射选项来存储单个字段的值。 你可以使用 stored_fields 参数将这些存储的值包括在搜索响应中。

警告:stored_fields 参数用于显式标记为存储在映射中的字段,该字段默认情况下处于关闭状态,通常不建议使用。 而是使用源过滤来选择要返回的原始源文档的子集。

允许有选择地为 search hit 表示的每个文档加载特定的 store 字段。

PUT my_index { "mappings": { "properties": { "title": { "type": "text", "store": true }, "date": { "type": "date", "store": true }, "content": { "type": "text" }, "city": { "type": "keyword" } } } } PUT my_index/_doc/1 { "title": "Some short title", "date": "2015-01-01", "content": "A very long content field...", "city": "Beijing" }

我们可以通过如下的方式来进行搜索:

GET my_index/_search { "stored_fields": [ "title", "date" ], "query": { "term": { "city": "Beijing" } } }

上面的搜索结果为:

{ "took" : 0, "timed_out" : false, "_shards" : { "total" : 1, "successful" : 1, "skipped" : 0, "failed" : 0 }, "hits" : { "total" : { "value" : 1, "relation" : "eq" }, "max_score" : 0.2876821, "hits" : [ { "_index" : "my_index", "_type" : "_doc", "_id" : "1", "_score" : 0.2876821, "fields" : { "date" : [ "2015-01-01T00:00:00.000Z" ], "title" : [ "Some short title" ] } } ] } }

在上面,我们可以使用 * 来返回所有的 stored fields:

GET my_index/_search { "stored_fields": "*", "query": { "term": { "city": "Beijing" } } }

空数组将导致每次匹配仅返回 _id 和 _type,例如:

GET my_index/_search { "stored_fields": [], "query": { "term": { "city": "Beijing" } } }

上面的搜索将返回:

{ "took" : 0, "timed_out" : false, "_shards" : { "total" : 1, "successful" : 1, "skipped" : 0, "failed" : 0 }, "hits" : { "total" : { "value" : 1, "relation" : "eq" }, "max_score" : 0.2876821, "hits" : [ { "_index" : "my_index", "_type" : "_doc", "_id" : "1", "_score" : 0.2876821 } ] } }

如果未设定为 store 请求的字段(将存储映射设置为false),则将忽略它们。

从文档本身获取的 store 字段值始终以数组形式返回。 相反,诸如 _routing 之类的元数据字段从不作为数组返回。

另外,只能通过 stored_fields 选项返回 leaf field。 如果指定了对象字段,它将被忽略。

注意:就其本身而言,stored_fields 不能用于加载 nested 对象中的字段 - 如果字段在其路径中包含 nested 对象,则不会为该存储字段返回任何数据。 要访问 nested 字段,必须在 inner_hits 块内使用 stored_fields。

禁止 stored fields

要完全禁用 store 字段(和元数据字段),请使用:_none_ :

GET my_index/_search { "stored_fields": "_none_", "query": { "term": { "city": "Beijing" } } } Soure filtering

你可以使用 _source 参数选择返回源的哪些字段。 这称为源过滤。

以下搜索 API 请求将 _source 请求主体参数设置为 false。 该文档来源不包含在响应中。

GET developers/_search { "_source": false, "query": { "match": { "city": "Beijing" } } }

要仅返回 source 字段的子集,请在 _source参 数中指定通配符(*)模式。 以下搜索 API 请求仅返回 ski 为开头的所有字段以及 name.fir 为开头的所有字段:

GET developers/_search { "_source": ["ski*", "name.fir*"], "query": { "match": { "city": "Beijing" } } } { "took" : 0, "timed_out" : false, "_shards" : { "total" : 1, "successful" : 1, "skipped" : 0, "failed" : 0 }, "hits" : { "total" : { "value" : 1, "relation" : "eq" }, "max_score" : 0.6931471, "hits" : [ { "_index" : "developers", "_type" : "_doc", "_id" : "1", "_score" : 0.6931471, "_source" : { "skills" : [ "Java", "C++" ], "name" : { "firstname" : "san" } } } ] } }

为了更好地控制,你可以在 _source 参数中指定一个includes 和  excludes 模式的数组的对象。

如果指定了 includes 属性,则仅返回与其模式之一匹配的源字段。 你可以使用 excludes 属性从此子集中排除字段。

如果未指定 include 属性,则返回整个文档源,不包括与 excludes 属性中与模式匹配的任何字段。

以下搜索 API 请求仅返回 sk* 和 name 字段及其属性的源,不包括 name.secondname 字段。

GET developers/_search { "query": { "match_all": {} }, "_source": { "includes": [ "sk*", "name" ], "excludes": [ "name.secondname"] } } { "took" : 0, "timed_out" : false, "_shards" : { "total" : 1, "successful" : 1, "skipped" : 0, "failed" : 0 }, "hits" : { "total" : { "value" : 2, "relation" : "eq" }, "max_score" : 1.0, "hits" : [ { "_index" : "developers", "_type" : "_doc", "_id" : "1", "_score" : 1.0, "_source" : { "skills" : [ "Java", "C++" ], "name" : { "firstname" : "san" } } }, { "_index" : "developers", "_type" : "_doc", "_id" : "2", "_score" : 1.0, "_source" : { "skills" : [ "Ruby", "C++" ], "name" : { "firstname" : "si" } } } ] } } Scripted fields

你可以使用 script_fields 参数为每个 hit 检索进行脚本运算(基于不同的字段)。 例如:

GET developers/_search { "query": { "match_all": {} }, "script_fields": { "2_times_age": { "script": { "source": """ doc['age'].value * 2 """ } }, "3_times_age": { "script": { "source": """ doc['age'].value * 3 """ } } } }

上面将返回:

{ "took" : 2, "timed_out" : false, "_shards" : { "total" : 1, "successful" : 1, "skipped" : 0, "failed" : 0 }, "hits" : { "total" : { "value" : 2, "relation" : "eq" }, "max_score" : 1.0, "hits" : [ { "_index" : "developers", "_type" : "_doc", "_id" : "1", "_score" : 1.0, "fields" : { "2_times_age" : [ 40 ], "3_times_age" : [ 60 ] } }, { "_index" : "developers", "_type" : "_doc", "_id" : "2", "_score" : 1.0, "fields" : { "2_times_age" : [ 60 ], "3_times_age" : [ 90 ] } } ] } }

Scripted fields 可以在未 store 的字段上工作(在上述情况下为 age),并允许返回要返回的自定义值(脚本的计算值)。

脚本字段还可以使用 params['_ source'] 访问实际的 _source 文档并提取要从中返回的特定元素。 这是一个例子:

GET developers/_search { "query": { "match_all": {} }, "script_fields": { "my_city": { "script": { "source": """ "I am living in " + params["_source"]["city"] """ } } } } { "took" : 3, "timed_out" : false, "_shards" : { "total" : 1, "successful" : 1, "skipped" : 0, "failed" : 0 }, "hits" : { "total" : { "value" : 2, "relation" : "eq" }, "max_score" : 1.0, "hits" : [ { "_index" : "developers", "_type" : "_doc", "_id" : "1", "_score" : 1.0, "fields" : { "my_city" : [ "I am living in Beijing" ] } }, { "_index" : "developers", "_type" : "_doc", "_id" : "2", "_score" : 1.0, "fields" : { "my_city" : [ "I am living in Shanghai" ] } } ] } }

请注意此处的 _source 关键字,以浏览类似 json 的模型。

重要的是要了解 doc['my_field'].value 和 params['_ source'] ['my_field'] 之间的区别。 第一个使用 doc 关键字,将导致将该字段的术语加载到内存中(缓存),这将导致执行速度更快,但会占用更多内存。 此外,doc [...]表示法仅允许使用简单值字段(你无法从中返回 json 对象),并且仅对未分析或基于单个术语的字段有意义。 但是,从文档访问值的方式来说,仍然建议使用doc(即使有可能),因为 _source 每次使用时都必须加载和解析。 使用 _source 非常慢。



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3