访问文档字段和特殊变量
Elastic Stack Serverless
根据脚本的使用位置,它将有权访问某些特殊变量和文档字段。
在 update、update-by-query 或 reindex API 中使用的脚本将有权访问 ctx
变量,该变量公开
ctx._source
- 访问文档
_source
字段。 ctx.op
- 应应用于文档的操作:
index
或delete
。 ctx._index
等- 访问 文档元数据字段,其中一些可能是只读的。
这些脚本无权访问 doc
变量,必须使用 ctx
才能访问它们操作的文档。
除了 脚本字段(每个搜索命中执行一次)之外,在搜索和聚合中使用的脚本将为可能匹配查询或聚合的每个文档执行一次。 根据您拥有的文档数量,这可能意味着数百万或数十亿次执行:这些脚本需要快速!
可以使用 doc-values、_source
字段或 存储字段从脚本访问字段值,以下将对它们进行说明。
在 function_score
查询中、在 基于脚本的排序中、或在 聚合中使用的脚本可以访问 _score
变量,该变量表示文档的当前相关性得分。
这是一个在 function_score
查询中使用脚本来更改每个文档的相关性 _score
的示例
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
变量,该变量提供有关子查询中词项的统计信息。
在以下示例中,_termStats
在 script_score
查询中使用,以检索 text
字段中词项 quick
、brown
和 fox
的平均词频
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
计算查询中词项的平均文档频率。
_termStats
提供以下函数来处理词项统计信息
uniqueTermsCount
:返回查询中唯一词项的总数。 此值在所有文档中均相同。matchedTermsCount
:返回当前文档中匹配的查询词项的计数。docFreq
:提供查询中词项的文档频率统计信息,指示有多少文档包含每个词项。 此值在所有文档中均一致。totalTermFreq
:提供所有文档中词项的总频率,表示每个词项在整个语料库中出现的频率。 此值在所有文档中均一致。termFreq
:返回当前文档中查询词项的频率,显示每个词项在该文档中出现的频率。
docFreq
、termFreq
和 totalTermFreq
函数返回表示子查询的所有词项的统计信息的对象。
统计信息提供对以下方法的支持
getAverage()
:返回指标的平均值。 getMin()
:返回指标的最小值。 getMax()
:返回指标的最大值。 getSum()
:返回指标值的总和。 getCount()
:返回指标计算中包含的词项的计数。
仅在使用 Painless 脚本语言时,_termStats
变量才可用。
从脚本访问字段值最快、最有效的方式是使用 doc['field_name']
语法,该语法从 doc values 中检索字段值。 Doc values 是一种列式字段值存储,默认情况下在所有字段上启用,但 analyzed text
fields 除外。
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
脚本中检查映射中是否存在该字段。
如果启用了 fielddata
,则 doc['field']
语法也可用于 analyzed text
fields,但请注意:在 text
字段上启用 fielddata 需要将所有词项加载到 JVM 堆中,这在内存和 CPU 方面都可能非常昂贵。 从脚本访问 text
字段很少有意义。
可以使用 _source.field_name
语法访问文档 _source
。 _source
作为映射映射加载,因此可以访问对象字段中的属性,例如 _source.name.first
。
访问 _source
字段比使用 doc-values 慢得多。 _source 字段针对每个结果返回多个字段进行了优化,而 doc values 针对访问许多文档中特定字段的值进行了优化。
在为搜索结果中的前十个命中生成 脚本字段时使用 _source
是有意义的,但对于其他搜索和聚合用例,始终首选使用 doc values。
例如
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']
语法访问
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"
}
}
}
}
_source
字段只是一个特殊的存储字段,因此性能与其他存储字段相似。 _source
提供对已索引的原始文档主体的访问(包括区分 null
值和空字段、单值数组和普通标量的能力等)。
真正有意义使用存储字段而不是 _source
字段的唯一情况是,当 _source
非常大并且访问一些小的存储字段而不是整个 _source
的成本更低时。