更新 API

编辑

使用指定的脚本更新文档。

请求

编辑

POST /<索引>/_update/<_id>

先决条件

编辑
  • 如果启用了 Elasticsearch 安全功能,您必须具有目标索引或索引别名的 indexwrite 索引权限

描述

编辑

允许您编写脚本来更新文档。该脚本可以更新、删除或跳过修改文档。更新 API 还支持传递部分文档,该文档会合并到现有文档中。要完全替换现有文档,请使用 index API

此操作

  1. 从索引中获取文档(与分片共址)。
  2. 运行指定的脚本。
  3. 索引结果。

文档仍然需要重新索引,但使用 update 可以减少一些网络往返,并降低 GET 和索引操作之间版本冲突的可能性。

必须启用 _source 字段才能使用 update。除了 _source 之外,您还可以通过 ctx 映射访问以下变量:_index_type_id_version_routing_now (当前时间戳)。

路径参数

编辑
<索引>
(必需,字符串)目标索引的名称。默认情况下,如果索引不存在,则会自动创建该索引。有关更多信息,请参阅 自动创建数据流和索引
<_id>
(必需,字符串)要更新的文档的唯一标识符。

查询参数

编辑
if_seq_no
(可选,整数)仅当文档具有此序列号时才执行操作。请参阅 乐观并发控制
if_primary_term
(可选,整数)仅当文档具有此主项时才执行操作。请参阅 乐观并发控制
lang
(可选,字符串)脚本语言。默认值:painless
require_alias
(可选,布尔值)如果为 true,则目标必须是索引别名。默认为 false
refresh
(可选,枚举)如果为 true,Elasticsearch 将刷新受影响的分片以使此操作对搜索可见;如果为 wait_for,则等待刷新以使此操作对搜索可见;如果为 false,则不进行任何刷新操作。有效值:truefalsewait_for。默认值:false
retry_on_conflict
(可选,整数)指定发生冲突时应重试操作的次数。默认值:0。
routing
(可选,字符串)用于将操作路由到特定分片的自定义值。
_source
(可选,列表)设置为 true 以启用源检索(默认值:false)。您还可以指定要检索的字段的逗号分隔列表。
_source_excludes
(可选,列表)指定要排除的源字段。
_source_includes
(可选,列表)指定要检索的源字段。
timeout

(可选,时间单位)等待以下操作的时间段

默认为 1m(一分钟)。这保证了 Elasticsearch 在失败之前至少等待超时时间。实际等待时间可能会更长,尤其是在发生多次等待时。

wait_for_active_shards

(可选,字符串)在继续操作之前必须处于活动状态的每个分片的副本数。设置为 all 或任何非负整数,最大为索引中每个分片的副本总数(number_of_replicas+1)。默认为 1,表示仅等待每个主分片处于活动状态。

请参阅 活动分片

示例

编辑

首先,让我们索引一个简单的文档

resp = client.index(
    index="test",
    id="1",
    document={
        "counter": 1,
        "tags": [
            "red"
        ]
    },
)
print(resp)
response = client.index(
  index: 'test',
  id: 1,
  body: {
    counter: 1,
    tags: [
      'red'
    ]
  }
)
puts response
res, err := es.Index(
	"test",
	strings.NewReader(`{
	  "counter": 1,
	  "tags": [
	    "red"
	  ]
	}`),
	es.Index.WithDocumentID("1"),
	es.Index.WithPretty(),
)
fmt.Println(res, err)
const response = await client.index({
  index: "test",
  id: 1,
  document: {
    counter: 1,
    tags: ["red"],
  },
});
console.log(response);
PUT test/_doc/1
{
  "counter" : 1,
  "tags" : ["red"]
}

要增加计数器,您可以提交一个包含以下脚本的更新请求

resp = client.update(
    index="test",
    id="1",
    script={
        "source": "ctx._source.counter += params.count",
        "lang": "painless",
        "params": {
            "count": 4
        }
    },
)
print(resp)
response = client.update(
  index: 'test',
  id: 1,
  body: {
    script: {
      source: 'ctx._source.counter += params.count',
      lang: 'painless',
      params: {
        count: 4
      }
    }
  }
)
puts response
res, err := es.Update(
	"test",
	"1",
	strings.NewReader(`{
	  "script": {
	    "source": "ctx._source.counter += params.count",
	    "lang": "painless",
	    "params": {
	      "count": 4
	    }
	  }
	}`),
	es.Update.WithPretty(),
)
fmt.Println(res, err)
const response = await client.update({
  index: "test",
  id: 1,
  script: {
    source: "ctx._source.counter += params.count",
    lang: "painless",
    params: {
      count: 4,
    },
  },
});
console.log(response);
POST test/_update/1
{
  "script" : {
    "source": "ctx._source.counter += params.count",
    "lang": "painless",
    "params" : {
      "count" : 4
    }
  }
}

类似地,您可以使用更新脚本将标签添加到标签列表中(这只是一个列表,因此即使标签存在也会添加)

resp = client.update(
    index="test",
    id="1",
    script={
        "source": "ctx._source.tags.add(params.tag)",
        "lang": "painless",
        "params": {
            "tag": "blue"
        }
    },
)
print(resp)
response = client.update(
  index: 'test',
  id: 1,
  body: {
    script: {
      source: 'ctx._source.tags.add(params.tag)',
      lang: 'painless',
      params: {
        tag: 'blue'
      }
    }
  }
)
puts response
res, err := es.Update(
	"test",
	"1",
	strings.NewReader(`{
	  "script": {
	    "source": "ctx._source.tags.add(params.tag)",
	    "lang": "painless",
	    "params": {
	      "tag": "blue"
	    }
	  }
	}`),
	es.Update.WithPretty(),
)
fmt.Println(res, err)
const response = await client.update({
  index: "test",
  id: 1,
  script: {
    source: "ctx._source.tags.add(params.tag)",
    lang: "painless",
    params: {
      tag: "blue",
    },
  },
});
console.log(response);
POST test/_update/1
{
  "script": {
    "source": "ctx._source.tags.add(params.tag)",
    "lang": "painless",
    "params": {
      "tag": "blue"
    }
  }
}

您还可以从标签列表中删除标签。用于 remove 标签的 Painless 函数需要删除的元素的数组索引。为了避免可能的运行时错误,您首先需要确保标签存在。如果列表包含标签的重复项,此脚本仅删除一个出现项。

resp = client.update(
    index="test",
    id="1",
    script={
        "source": "if (ctx._source.tags.contains(params.tag)) { ctx._source.tags.remove(ctx._source.tags.indexOf(params.tag)) }",
        "lang": "painless",
        "params": {
            "tag": "blue"
        }
    },
)
print(resp)
response = client.update(
  index: 'test',
  id: 1,
  body: {
    script: {
      source: 'if (ctx._source.tags.contains(params.tag)) { ctx._source.tags.remove(ctx._source.tags.indexOf(params.tag)) }',
      lang: 'painless',
      params: {
        tag: 'blue'
      }
    }
  }
)
puts response
res, err := es.Update(
	"test",
	"1",
	strings.NewReader(`{
	  "script": {
	    "source": "if (ctx._source.tags.contains(params.tag)) { ctx._source.tags.remove(ctx._source.tags.indexOf(params.tag)) }",
	    "lang": "painless",
	    "params": {
	      "tag": "blue"
	    }
	  }
	}`),
	es.Update.WithPretty(),
)
fmt.Println(res, err)
const response = await client.update({
  index: "test",
  id: 1,
  script: {
    source:
      "if (ctx._source.tags.contains(params.tag)) { ctx._source.tags.remove(ctx._source.tags.indexOf(params.tag)) }",
    lang: "painless",
    params: {
      tag: "blue",
    },
  },
});
console.log(response);
POST test/_update/1
{
  "script": {
    "source": "if (ctx._source.tags.contains(params.tag)) { ctx._source.tags.remove(ctx._source.tags.indexOf(params.tag)) }",
    "lang": "painless",
    "params": {
      "tag": "blue"
    }
  }
}

您还可以从文档中添加和删除字段。例如,此脚本添加字段 new_field

resp = client.update(
    index="test",
    id="1",
    script="ctx._source.new_field = 'value_of_new_field'",
)
print(resp)
response = client.update(
  index: 'test',
  id: 1,
  body: {
    script: "ctx._source.new_field = 'value_of_new_field'"
  }
)
puts response
res, err := es.Update(
	"test",
	"1",
	strings.NewReader(`{
	  "script": "ctx._source.new_field = 'value_of_new_field'"
	}`),
	es.Update.WithPretty(),
)
fmt.Println(res, err)
const response = await client.update({
  index: "test",
  id: 1,
  script: "ctx._source.new_field = 'value_of_new_field'",
});
console.log(response);
POST test/_update/1
{
  "script" : "ctx._source.new_field = 'value_of_new_field'"
}

相反,此脚本删除字段 new_field

resp = client.update(
    index="test",
    id="1",
    script="ctx._source.remove('new_field')",
)
print(resp)
response = client.update(
  index: 'test',
  id: 1,
  body: {
    script: "ctx._source.remove('new_field')"
  }
)
puts response
res, err := es.Update(
	"test",
	"1",
	strings.NewReader(`{
	  "script": "ctx._source.remove('new_field')"
	}`),
	es.Update.WithPretty(),
)
fmt.Println(res, err)
const response = await client.update({
  index: "test",
  id: 1,
  script: "ctx._source.remove('new_field')",
});
console.log(response);
POST test/_update/1
{
  "script" : "ctx._source.remove('new_field')"
}

以下脚本从对象字段中删除子字段

resp = client.update(
    index="test",
    id="1",
    script="ctx._source['my-object'].remove('my-subfield')",
)
print(resp)
response = client.update(
  index: 'test',
  id: 1,
  body: {
    script: "ctx._source['my-object'].remove('my-subfield')"
  }
)
puts response
const response = await client.update({
  index: "test",
  id: 1,
  script: "ctx._source['my-object'].remove('my-subfield')",
});
console.log(response);
POST test/_update/1
{
  "script": "ctx._source['my-object'].remove('my-subfield')"
}

除了更新文档之外,您还可以更改从脚本内部执行的操作。例如,如果 tags 字段包含 green,则此请求会删除文档,否则不执行任何操作 (noop)

resp = client.update(
    index="test",
    id="1",
    script={
        "source": "if (ctx._source.tags.contains(params.tag)) { ctx.op = 'delete' } else { ctx.op = 'noop' }",
        "lang": "painless",
        "params": {
            "tag": "green"
        }
    },
)
print(resp)
response = client.update(
  index: 'test',
  id: 1,
  body: {
    script: {
      source: "if (ctx._source.tags.contains(params.tag)) { ctx.op = 'delete' } else { ctx.op = 'noop' }",
      lang: 'painless',
      params: {
        tag: 'green'
      }
    }
  }
)
puts response
const response = await client.update({
  index: "test",
  id: 1,
  script: {
    source:
      "if (ctx._source.tags.contains(params.tag)) { ctx.op = 'delete' } else { ctx.op = 'noop' }",
    lang: "painless",
    params: {
      tag: "green",
    },
  },
});
console.log(response);
POST test/_update/1
{
  "script": {
    "source": "if (ctx._source.tags.contains(params.tag)) { ctx.op = 'delete' } else { ctx.op = 'noop' }",
    "lang": "painless",
    "params": {
      "tag": "green"
    }
  }
}
更新文档的一部分
编辑

以下部分更新将新字段添加到现有文档

resp = client.update(
    index="test",
    id="1",
    doc={
        "name": "new_name"
    },
)
print(resp)
response = client.update(
  index: 'test',
  id: 1,
  body: {
    doc: {
      name: 'new_name'
    }
  }
)
puts response
res, err := es.Update(
	"test",
	"1",
	strings.NewReader(`{
	  "doc": {
	    "name": "new_name"
	  }
	}`),
	es.Update.WithPretty(),
)
fmt.Println(res, err)
const response = await client.update({
  index: "test",
  id: 1,
  doc: {
    name: "new_name",
  },
});
console.log(response);
POST test/_update/1
{
  "doc": {
    "name": "new_name"
  }
}

如果同时指定了 docscript,则会忽略 doc。如果您指定了脚本更新,请在脚本中包含要更新的字段。

检测无操作更新
编辑

默认情况下,不更改任何内容的更新会检测到它们没有更改任何内容,并返回 "result": "noop"

resp = client.update(
    index="test",
    id="1",
    doc={
        "name": "new_name"
    },
)
print(resp)
response = client.update(
  index: 'test',
  id: 1,
  body: {
    doc: {
      name: 'new_name'
    }
  }
)
puts response
res, err := es.Update(
	"test",
	"1",
	strings.NewReader(`{
	  "doc": {
	    "name": "new_name"
	  }
	}`),
	es.Update.WithPretty(),
)
fmt.Println(res, err)
const response = await client.update({
  index: "test",
  id: 1,
  doc: {
    name: "new_name",
  },
});
console.log(response);
POST test/_update/1
{
  "doc": {
    "name": "new_name"
  }
}

如果 name 的值已经是 new_name,则会忽略更新请求,并且响应中的 result 元素返回 noop

{
   "_shards": {
        "total": 0,
        "successful": 0,
        "failed": 0
   },
   "_index": "test",
   "_id": "1",
   "_version": 2,
   "_primary_term": 1,
   "_seq_no": 1,
   "result": "noop"
}

您可以通过设置 "detect_noop": false 来禁用此行为

resp = client.update(
    index="test",
    id="1",
    doc={
        "name": "new_name"
    },
    detect_noop=False,
)
print(resp)
response = client.update(
  index: 'test',
  id: 1,
  body: {
    doc: {
      name: 'new_name'
    },
    detect_noop: false
  }
)
puts response
res, err := es.Update(
	"test",
	"1",
	strings.NewReader(`{
	  "doc": {
	    "name": "new_name"
	  },
	  "detect_noop": false
	}`),
	es.Update.WithPretty(),
)
fmt.Println(res, err)
const response = await client.update({
  index: "test",
  id: 1,
  doc: {
    name: "new_name",
  },
  detect_noop: false,
});
console.log(response);
POST test/_update/1
{
  "doc": {
    "name": "new_name"
  },
  "detect_noop": false
}
Upsert
编辑

如果文档尚不存在,则会将 upsert 元素的内容作为新文档插入。如果文档存在,则会执行 script

resp = client.update(
    index="test",
    id="1",
    script={
        "source": "ctx._source.counter += params.count",
        "lang": "painless",
        "params": {
            "count": 4
        }
    },
    upsert={
        "counter": 1
    },
)
print(resp)
response = client.update(
  index: 'test',
  id: 1,
  body: {
    script: {
      source: 'ctx._source.counter += params.count',
      lang: 'painless',
      params: {
        count: 4
      }
    },
    upsert: {
      counter: 1
    }
  }
)
puts response
res, err := es.Update(
	"test",
	"1",
	strings.NewReader(`{
	  "script": {
	    "source": "ctx._source.counter += params.count",
	    "lang": "painless",
	    "params": {
	      "count": 4
	    }
	  },
	  "upsert": {
	    "counter": 1
	  }
	}`),
	es.Update.WithPretty(),
)
fmt.Println(res, err)
const response = await client.update({
  index: "test",
  id: 1,
  script: {
    source: "ctx._source.counter += params.count",
    lang: "painless",
    params: {
      count: 4,
    },
  },
  upsert: {
    counter: 1,
  },
});
console.log(response);
POST test/_update/1
{
  "script": {
    "source": "ctx._source.counter += params.count",
    "lang": "painless",
    "params": {
      "count": 4
    }
  },
  "upsert": {
    "counter": 1
  }
}
脚本 Upsert
编辑

要无论文档是否存在都运行脚本,请将 scripted_upsert 设置为 true

resp = client.update(
    index="test",
    id="1",
    scripted_upsert=True,
    script={
        "source": "\n      if ( ctx.op == 'create' ) {\n        ctx._source.counter = params.count\n      } else {\n        ctx._source.counter += params.count\n      }\n    ",
        "params": {
            "count": 4
        }
    },
    upsert={},
)
print(resp)
response = client.update(
  index: 'test',
  id: 1,
  body: {
    scripted_upsert: true,
    script: {
      source: "\n      if ( ctx.op == 'create' ) {\n        ctx._source.counter = params.count\n      } else {\n        ctx._source.counter += params.count\n      }\n    ",
      params: {
        count: 4
      }
    },
    upsert: {}
  }
)
puts response
const response = await client.update({
  index: "test",
  id: 1,
  scripted_upsert: true,
  script: {
    source:
      "\n      if ( ctx.op == 'create' ) {\n        ctx._source.counter = params.count\n      } else {\n        ctx._source.counter += params.count\n      }\n    ",
    params: {
      count: 4,
    },
  },
  upsert: {},
});
console.log(response);
POST test/_update/1
{
  "scripted_upsert": true,
  "script": {
    "source": """
      if ( ctx.op == 'create' ) {
        ctx._source.counter = params.count
      } else {
        ctx._source.counter += params.count
      }
    """,
    "params": {
      "count": 4
    }
  },
  "upsert": {}
}
将文档用作 Upsert
编辑

您可以将 doc_as_upsert 设置为 true,以使用 doc 的内容作为 upsert 值,而不是发送部分 docupsert 文档

resp = client.update(
    index="test",
    id="1",
    doc={
        "name": "new_name"
    },
    doc_as_upsert=True,
)
print(resp)
response = client.update(
  index: 'test',
  id: 1,
  body: {
    doc: {
      name: 'new_name'
    },
    doc_as_upsert: true
  }
)
puts response
res, err := es.Update(
	"test",
	"1",
	strings.NewReader(`{
	  "doc": {
	    "name": "new_name"
	  },
	  "doc_as_upsert": true
	}`),
	es.Update.WithPretty(),
)
fmt.Println(res, err)
const response = await client.update({
  index: "test",
  id: 1,
  doc: {
    name: "new_name",
  },
  doc_as_upsert: true,
});
console.log(response);
POST test/_update/1
{
  "doc": {
    "name": "new_name"
  },
  "doc_as_upsert": true
}

不支持将 摄取管道doc_as_upsert 一起使用。