Spec-Zone.ru › Elasticsearch 8
›Elasticsearch Руководство [8.17] ›REST API ›API документов

API обновления

Новая справка по API

Для получения самых свежих данных об API обратитесь к API документов.

Обновляет документ с помощью указанного скрипта.

Запрос

POST /<index>/_update/<_id>

Предварительные требования

  • Если включены функции безопасности Elasticsearch, у вас должна быть index или write привилегия индекса для целевого индекса или псевдонима индекса.

Описание

Позволяет выполнять обновление документов с помощью скриптов. Скрипт может обновлять, удалять или игнорировать изменение документа. API обновления также поддерживает передачу частичного документа, который объединяется с существующим документом. Чтобы полностью заменить существующий документ, используйте index API.

Эта операция:

  1. Получает документ (вместе с фрагментом) из индекса.
  2. Выполняет указанный скрипт.
  3. Индексирует результат.

Документ все равно должен быть повторно индексирован, но использование update устраняет некоторые сетевые запросы и снижает вероятность конфликтов версий между операциями GET и индексации.

Поле _source должно быть включено для использования update. В дополнение к _source, вы можете получить доступ к следующим переменным через карту ctx: _index, _type, _id, _version, _routing и _now (текущая метка времени).

Параметры пути

<index>
(Обязательно, строка) Название целевого индекса. По умолчанию индекс создается автоматически, если он не существует. Дополнительную информацию см. в Автоматическое создание потоков данных и индексов.
<_id>
(Обязательно, строка) Уникальный идентификатор документа, который необходимо обновить.

Параметры запроса

if_seq_no
(Необязательно, целое число) Выполнить операцию только в том случае, если у документа есть этот номер последовательности. См. Оптимистический контроль конкуретности.
if_primary_term
(Необязательно, целое число) Выполнить операцию только в том случае, если у документа есть этот основной термин. См. Оптимистический контроль конкуретности.
lang
(Необязательно, строка) Язык скрипта. По умолчанию: painless.
require_alias
(Необязательно, логическое значение) Если true, целевой объект должен быть псевдонимом индекса алиасом индекса. По умолчанию false.
refresh
(Необязательно, перечисление) Если true, Elasticsearch обновляет затронутые фрагменты, чтобы сделать эту операцию видимой для поиска. Если wait_for, ожидать обновления, чтобы сделать операцию видимой для поиска; если false, ничего не делать с обновлениями. Допустимые значения: true, false, wait_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"
    }
  }
}

Также можно удалить тег из списка тегов. Функция Painless для remove тега принимает индекс элемента массива, который нужно удалить. Для предотвращения возможной ошибки во время выполнения, сначала необходимо убедиться, что тег существует. Если в списке есть дубликаты тега, этот скрипт удаляет только одно его вхождение.

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

Если оба doc и script указаны, то doc игнорируется. Если вы указываете скриптовое обновление, включите поля, которые вы хотите обновить, в скрипт.

Обнаружение обновлений noop

По умолчанию обновления, которые ничего не меняют, обнаруживают, что они ничего не меняют, и возвращают "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 позволяет обновить существующий документ или вставить новый, если он не существует, в одном запросе.

В этом примере, если продукт с ID 1 существует, его цена будет обновлена до 100. Если продукт не существует, будет вставлен новый документ с ID 1 и ценой 50.

resp = client.update(
    index="test",
    id="1",
    doc={
        "product_price": 100
    },
    upsert={
        "product_price": 50
    },
)
print(resp)
const response = await client.update({
  index: "test",
  id: 1,
  doc: {
    product_price: 100,
  },
  upsert: {
    product_price: 50,
  },
});
console.log(response);
POST /test/_update/1
{
  "doc": {
    "product_price": 100
  },
  "upsert": {
    "product_price": 50
  }
}
Скриптовый 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 плюс документа upsert, вы можете установить doc_as_upsert в true, чтобы использовать содержимое doc в качестве значения upsert:

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 не поддерживается.

© 2023-2025 Elasticsearch
As of September 2024, Elasticsearch is available under a choice of three licenses: the Server Side Public License (SSPL), the Elastic License, or the AGPLv3 (OSI approved).
Elasticsearch and the Elasticsearch logo are trademarks of Elasticsearch B.V., registered in the U.S. and in other countries.
https://www.elastic.co/guide/en/elasticsearch/reference/8.17/docs-update.html

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API