访问文档字段和特殊变量
编辑访问文档字段和特殊变量
编辑根据脚本的使用位置,它将有权访问某些特殊变量和文档字段。
更新脚本
编辑在 update、update-by-query 或 reindex API 中使用的脚本将有权访问 ctx
变量,该变量公开了
|
访问文档的 |
|
应该应用于文档的操作: |
|
访问 文档元数据字段,其中一些可能是只读的。 |
这些脚本无权访问 doc
变量,必须使用 ctx
来访问它们操作的文档。
搜索和聚合脚本
编辑除了每个搜索命中执行一次的 脚本字段 外,搜索和聚合中使用的脚本将对每个可能匹配查询或聚合的文档执行一次。根据您拥有的文档数量,这可能意味着数百万或数十亿次的执行:这些脚本需要快速!
可以使用 doc-values、 _source
字段 或 存储字段 从脚本访问字段值,下面将分别解释每个字段值。
访问脚本中文档的分数
编辑在 function_score
查询、基于脚本的排序 或 聚合 中使用的脚本可以访问 _score
变量,该变量表示文档的当前相关性得分。
以下是在 function_score
查询中使用脚本来更改每个文档的相关性 _score
的示例
resp = client.index( index="my-index-000001", id="1", refresh=True, document={ "text": "quick brown fox", "popularity": 1 }, ) print(resp) resp1 = client.index( index="my-index-000001", id="2", refresh=True, document={ "text": "quick fox", "popularity": 5 }, ) print(resp1) resp2 = client.search( index="my-index-000001", query={ "function_score": { "query": { "match": { "text": "quick brown fox" } }, "script_score": { "script": { "lang": "expression", "source": "_score * doc['popularity']" } } } }, ) print(resp2)
response = client.index( index: 'my-index-000001', id: 1, refresh: true, body: { text: 'quick brown fox', popularity: 1 } ) puts response response = client.index( index: 'my-index-000001', id: 2, refresh: true, body: { text: 'quick fox', popularity: 5 } ) puts response response = client.search( index: 'my-index-000001', body: { query: { function_score: { query: { match: { text: 'quick brown fox' } }, script_score: { script: { lang: 'expression', source: "_score * doc['popularity']" } } } } } ) puts response
const response = await client.index({ index: "my-index-000001", id: 1, refresh: "true", document: { text: "quick brown fox", popularity: 1, }, }); console.log(response); const response1 = await client.index({ index: "my-index-000001", id: 2, refresh: "true", document: { text: "quick fox", popularity: 5, }, }); console.log(response1); const response2 = await client.search({ index: "my-index-000001", query: { function_score: { query: { match: { text: "quick brown fox", }, }, script_score: { script: { lang: "expression", source: "_score * doc['popularity']", }, }, }, }, }); console.log(response2);
PUT my-index-000001/_doc/1?refresh { "text": "quick brown fox", "popularity": 1 } PUT my-index-000001/_doc/2?refresh { "text": "quick fox", "popularity": 5 } GET my-index-000001/_search { "query": { "function_score": { "query": { "match": { "text": "quick brown fox" } }, "script_score": { "script": { "lang": "expression", "source": "_score * doc['popularity']" } } } } }
访问脚本中文档的词项统计信息
编辑在 script_score
查询中使用的脚本可以访问 _termStats
变量,该变量提供有关子查询中词项的统计信息。
在以下示例中,在 script_score
查询中使用 _termStats
来检索 text
字段中词项 quick
、brown
和 fox
的平均词项频率
resp = client.index( index="my-index-000001", id="1", refresh=True, document={ "text": "quick brown fox" }, ) print(resp) resp1 = client.index( index="my-index-000001", id="2", refresh=True, document={ "text": "quick fox" }, ) print(resp1) resp2 = client.search( index="my-index-000001", query={ "script_score": { "query": { "match": { "text": "quick brown fox" } }, "script": { "source": "_termStats.termFreq().getAverage()" } } }, ) print(resp2)
const response = await client.index({ index: "my-index-000001", id: 1, refresh: "true", document: { text: "quick brown fox", }, }); console.log(response); const response1 = await client.index({ index: "my-index-000001", id: 2, refresh: "true", document: { text: "quick fox", }, }); console.log(response1); const response2 = await client.search({ index: "my-index-000001", query: { script_score: { query: { match: { text: "quick brown fox", }, }, script: { source: "_termStats.termFreq().getAverage()", }, }, }, }); console.log(response2);
PUT my-index-000001/_doc/1?refresh { "text": "quick brown fox" } PUT my-index-000001/_doc/2?refresh { "text": "quick fox" } GET my-index-000001/_search { "query": { "script_score": { "query": { "match": { "text": "quick brown fox" } }, "script": { "source": "_termStats.termFreq().getAverage()" } } } }
_termStats
提供以下用于处理词项统计信息的功能
-
uniqueTermsCount
:返回查询中唯一词项的总数。此值在所有文档中都相同。 -
matchedTermsCount
:返回当前文档中匹配的查询词项计数。 -
docFreq
:提供查询中词项的文档频率统计信息,指示每个词项包含多少个文档。此值在所有文档中都一致。 -
totalTermFreq
:提供所有文档中词项的总频率,表示每个词项在整个语料库中出现的频率。此值在所有文档中都一致。 -
termFreq
:返回当前文档中查询词项的频率,显示每个词项在该文档中出现的频率。
返回聚合统计信息的功能
docFreq
、termFreq
和 totalTermFreq
函数返回表示子查询所有词项的统计信息的对象。
统计信息提供对以下方法的支持
getAverage()
:返回指标的平均值。getMin()
:返回指标的最小值。getMax()
:返回指标的最大值。getSum()
:返回指标值的总和。getCount()
:返回指标计算中包含的词项计数。
需要 Painless 语言
仅当使用 Painless 脚本语言时,_termStats
变量才可用。
Doc values
编辑到目前为止,从脚本访问字段值最快、最有效的方法是使用 doc['field_name']
语法,该语法从 doc values 检索字段值。Doc values 是一个按列存储的字段值存储,默认情况下在所有字段上启用,但 分析过的 text
字段 除外。
resp = client.index( index="my-index-000001", id="1", refresh=True, document={ "cost_price": 100 }, ) print(resp) resp1 = client.search( index="my-index-000001", script_fields={ "sales_price": { "script": { "lang": "expression", "source": "doc['cost_price'] * markup", "params": { "markup": 0.2 } } } }, ) print(resp1)
response = client.index( index: 'my-index-000001', id: 1, refresh: true, body: { cost_price: 100 } ) puts response response = client.search( index: 'my-index-000001', body: { script_fields: { sales_price: { script: { lang: 'expression', source: "doc['cost_price'] * markup", params: { markup: 0.2 } } } } } ) puts response
const response = await client.index({ index: "my-index-000001", id: 1, refresh: "true", document: { cost_price: 100, }, }); console.log(response); const response1 = await client.search({ index: "my-index-000001", script_fields: { sales_price: { script: { lang: "expression", source: "doc['cost_price'] * markup", params: { markup: 0.2, }, }, }, }, }); console.log(response1);
PUT my-index-000001/_doc/1?refresh { "cost_price": 100 } GET my-index-000001/_search { "script_fields": { "sales_price": { "script": { "lang": "expression", "source": "doc['cost_price'] * markup", "params": { "markup": 0.2 } } } } }
Doc values 只能返回“简单”的字段值,如数字、日期、地理点、词项等,或者如果该字段是多值字段,则返回这些值的数组。它不能返回 JSON 对象。
缺失字段
如果映射中缺少 field
,则 doc['field']
将引发错误。在 painless
中,可以首先使用 doc.containsKey('field')
来检查以防止访问 doc
映射。不幸的是,无法在 expression
脚本中检查映射中是否存在该字段。
Doc values 和 text
字段
如果启用了 fielddata
,则 doc['field']
语法也可以用于 分析过的 text
字段,但请注意:在 text
字段上启用 fielddata 需要将所有词项加载到 JVM 堆中,这在内存和 CPU 方面都可能非常昂贵。从脚本访问 text
字段几乎没有意义。
文档 _source
编辑可以使用 _source.field_name
语法访问文档的 _source
。 _source
作为 map-of-maps 加载,因此可以访问对象字段中的属性,例如,_source.name.first
。
首选 doc-values 而不是 _source
访问 _source
字段比使用 doc-values 慢得多。_source 字段针对每个结果返回多个字段进行了优化,而 doc values 针对访问许多文档中特定字段的值进行了优化。
当从搜索结果为前十个命中生成 脚本字段 时,使用 _source
是有意义的,但对于其他搜索和聚合用例,始终首选使用 doc values。
例如
resp = client.indices.create( index="my-index-000001", mappings={ "properties": { "first_name": { "type": "text" }, "last_name": { "type": "text" } } }, ) print(resp) resp1 = client.index( index="my-index-000001", id="1", refresh=True, document={ "first_name": "Barry", "last_name": "White" }, ) print(resp1) resp2 = client.search( index="my-index-000001", script_fields={ "full_name": { "script": { "lang": "painless", "source": "params._source.first_name + ' ' + params._source.last_name" } } }, ) print(resp2)
response = client.indices.create( index: 'my-index-000001', body: { mappings: { properties: { first_name: { type: 'text' }, last_name: { type: 'text' } } } } ) puts response response = client.index( index: 'my-index-000001', id: 1, refresh: true, body: { first_name: 'Barry', last_name: 'White' } ) puts response response = client.search( index: 'my-index-000001', body: { script_fields: { full_name: { script: { lang: 'painless', source: "params._source.first_name + ' ' + params._source.last_name" } } } } ) puts response
const response = await client.indices.create({ index: "my-index-000001", mappings: { properties: { first_name: { type: "text", }, last_name: { type: "text", }, }, }, }); console.log(response); const response1 = await client.index({ index: "my-index-000001", id: 1, refresh: "true", document: { first_name: "Barry", last_name: "White", }, }); console.log(response1); const response2 = await client.search({ index: "my-index-000001", script_fields: { full_name: { script: { lang: "painless", source: "params._source.first_name + ' ' + params._source.last_name", }, }, }, }); console.log(response2);
PUT my-index-000001 { "mappings": { "properties": { "first_name": { "type": "text" }, "last_name": { "type": "text" } } } } PUT my-index-000001/_doc/1?refresh { "first_name": "Barry", "last_name": "White" } GET my-index-000001/_search { "script_fields": { "full_name": { "script": { "lang": "painless", "source": "params._source.first_name + ' ' + params._source.last_name" } } } }
存储字段
编辑存储字段 — 在映射中显式标记为 "store": true
的字段 — 可以使用 _fields['field_name'].value
或 _fields['field_name']
语法进行访问
resp = client.indices.create( index="my-index-000001", mappings={ "properties": { "full_name": { "type": "text", "store": True }, "title": { "type": "text", "store": True } } }, ) print(resp) resp1 = client.index( index="my-index-000001", id="1", refresh=True, document={ "full_name": "Alice Ball", "title": "Professor" }, ) print(resp1) resp2 = client.search( index="my-index-000001", script_fields={ "name_with_title": { "script": { "lang": "painless", "source": "params._fields['title'].value + ' ' + params._fields['full_name'].value" } } }, ) print(resp2)
response = client.indices.create( index: 'my-index-000001', body: { mappings: { properties: { full_name: { type: 'text', store: true }, title: { type: 'text', store: true } } } } ) puts response response = client.index( index: 'my-index-000001', id: 1, refresh: true, body: { full_name: 'Alice Ball', title: 'Professor' } ) puts response response = client.search( index: 'my-index-000001', body: { script_fields: { name_with_title: { script: { lang: 'painless', source: "params._fields['title'].value + ' ' + params._fields['full_name'].value" } } } } ) puts response
const response = await client.indices.create({ index: "my-index-000001", mappings: { properties: { full_name: { type: "text", store: true, }, title: { type: "text", store: true, }, }, }, }); console.log(response); const response1 = await client.index({ index: "my-index-000001", id: 1, refresh: "true", document: { full_name: "Alice Ball", title: "Professor", }, }); console.log(response1); const response2 = await client.search({ index: "my-index-000001", script_fields: { name_with_title: { script: { lang: "painless", source: "params._fields['title'].value + ' ' + params._fields['full_name'].value", }, }, }, }); console.log(response2);
PUT my-index-000001 { "mappings": { "properties": { "full_name": { "type": "text", "store": true }, "title": { "type": "text", "store": true } } } } PUT my-index-000001/_doc/1?refresh { "full_name": "Alice Ball", "title": "Professor" } GET my-index-000001/_search { "script_fields": { "name_with_title": { "script": { "lang": "painless", "source": "params._fields['title'].value + ' ' + params._fields['full_name'].value" } } } }
存储 vs _source
_source
字段只是一个特殊的存储字段,因此性能与其他存储字段类似。_source
提供对已索引的原始文档主体的访问(包括区分 null
值与空字段、单值数组与普通标量等的能力)。
唯一真正有意义使用存储字段而不是 _source
字段的情况是,当 _source
非常大时,访问几个小的存储字段而不是整个 _source
的成本更低。