访问文档字段和特殊变量

编辑

访问文档字段和特殊变量编辑

根据脚本的使用位置,它将可以访问某些特殊变量和文档字段。

更新脚本编辑

更新通过查询更新重新索引 API 中使用的脚本将可以访问 ctx 变量,该变量公开以下内容:

ctx._source

访问文档的 _source 字段

ctx.op

应应用于文档的操作:indexdelete

ctx._index

访问 文档元数据字段,其中一些可能是只读的。

这些脚本无法访问 doc 变量,必须使用 ctx 来访问它们操作的文档。

搜索和聚合脚本编辑

除了 脚本字段(每个搜索命中执行一次)之外,在搜索和聚合中使用的脚本将针对每个可能匹配查询或聚合的文档执行一次。根据您拥有的文档数量,这可能意味着数百万或数十亿次执行:这些脚本需要快速!

可以使用 文档值 _source 字段存储字段 从脚本访问字段值,下面将解释每个字段。

在脚本中访问文档的评分编辑

function_score 查询基于脚本的排序聚合 中使用的脚本可以访问 _score 变量,该变量表示文档的当前相关性评分。

以下是在 function_score 查询 中使用脚本更改每个文档的相关性 _score 的示例

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
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']"
        }
      }
    }
  }
}

文档值编辑

到目前为止,从脚本访问字段值的最快、最有效的方式是使用 doc['field_name'] 语法,该语法从 文档值 中检索字段值。文档值是列式字段值存储,默认情况下在所有字段上启用,除了 已分析的 text 字段

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
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
        }
      }
    }
  }
}

文档值只能返回“简单”字段值,例如数字、日期、地理点、术语等,或者如果字段是多值的,则返回这些值的数组。它不能返回 JSON 对象。

缺少字段

如果映射中缺少 field,则 doc['field'] 将抛出错误。在 painless 中,可以使用 doc.containsKey('field') 先进行检查,以防止访问 doc 映射。不幸的是,在 expression 脚本中,无法检查映射中是否存在字段。

文档值和 text 字段

如果启用了 fielddata,则 doc['field'] 语法也可以用于 已分析的 text 字段,但 注意:在 text 字段上启用 fielddata 需要将所有术语加载到 JVM 堆中,这在内存和 CPU 方面都可能非常昂贵。从脚本访问 text 字段很少有意义。

文档 _source编辑

可以使用 _source.field_name 语法访问文档 _source_source 作为映射的映射加载,因此可以像 _source.name.first 那样访问对象字段中的属性。

优先使用文档值而不是 _source

访问 _source 字段比使用文档值慢得多。_source 字段针对每个结果返回多个字段进行了优化,而文档值针对在许多文档中访问特定字段的值进行了优化。

在为搜索结果的前十个命中生成 脚本字段 时,使用 _source 是有意义的,但对于其他搜索和聚合用例,始终优先使用文档值。

例如

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
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'] 语法访问

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
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 字段只是一个特殊的存储字段,因此性能与其他存储字段类似。 _source 提供对已索引的原始文档正文的访问(包括能够区分 null 值和空字段、单值数组和普通标量等)。

真正有意义地使用存储字段而不是 _source 字段的唯一时间是当 _source 非常大,访问几个小的存储字段比访问整个 _source 成本更低时。