КатегорииElasticsearch

Elasticsearch — Урок 4.2 Обновление данных

В этом уроке мы обсудим различные способы обновления существующих документов. Внутренне обновление всегда является удалением и повторным индексом. Вы можете обновить весь документ (заменить исходный документ) или обновить одно поле или добавить новое поле или обновить поле с помощью сценариев, например, увеличить счетчик.

Обновление с использованием всего документа

Когда вы индексируете документ с существующим идентификатором, он заменит текущий документ новым документом. Как показано ниже, мы можем обновить документа используя индетификатор:

 PUT example4/person/1
 {
   "id": 1,
   "name": "name update 1",
   "age": 55,
   "gender": "M",
   "email": "user1@gmail.com",
   "last_modified_date": "2017-02-15"
 }

Ответ:

{
   "_index": "example4",
   "_type": "person",
   "_id": "1",
   "_version": 2,
   "result": "updated",
   "_shards": {
     "total": 2,
     "successful": 1,
     "failed": 0
   },
   "created": false
 }

Из ответа видно что result равен updated, _version (версия) равна 2 и что документ не был сейчас создан (created: false).

Частичные обновления

В предыдущем разделе мы обсудили, как обновить весь документ. В этом разделе мы обсудим, как обновить только одно или два поля в документе. Elasticsearch предоставляет API обновления, чтобы частично обновить существующий документ. API-интерфейс обновления сначала извлекает старый документ, а затем использует _source документа для применения изменений, удаляет старый документ и индексирует документ в качестве нового. Обновляемые поля указываются в теле запроса в поле doc.

Чтобы работали частичные обновления _source должно быть включено, по умолчанию так и есть, но лучше проверить. Что такое _source смотрите раздел Хранение оригинального документа

Предположим, мы хотим обновить только имя человека и не беспокоиться о каком-либо другом поле в документе. Пример:

 POST example4/person/1/_update
 {
   "doc": {
     "name": "name udpate 2"
   }
 }

Ответ следующий:

{
 "_index": "example4",
 "_type": "person",
 "_id": "1",
 "_version": 3,
 "result": "updated",
 "_shards": {
   "total": 2,
   "successful": 1,
   "failed": 0
 }
}

Как видно в ответе версия увеличилась и теперь равна 3.

Сценарий обновления

Сценарии обновления будут полезны, если вы хотите обновить документ на основе условий. Без данной возможности вам необходимо сначала получить документ, проверить условия в документе, применить изменения и переиндексировать документ. Частичные обновления извлекают документ из осколка, рекурсивно применяют изменения и повторно индексируют документы, избегая сетевых обходов.

Сценарий обновления также можно использовать для увеличения счетчика. Приложению не нужно беспокоиться о текущем значении. Мы можем увеличить поле возраста в документе лица, используя скрипт, как показано ниже:

POST example4/person/1/_update
{
  "script": "ctx._source.age+=1"
}

Elasticsearch поддерживает множество языков сценариев для выполнения встроенных скриптов, язык сценариев по умолчанию — Painless. Предположим, мы хотим классифицировать документ, который мы проиндексировали ранее, по взрослым и подросткам. Мы можем использовать встроенный скрипт, как показано ниже, чтобы проверить возраст человека и добавить новое поле person_type. Следующая команда обновит документ на основе сценария:

POST example4/person/1/_update
 {
   "script": {
     "inline": "if (ctx._source.age > params.age) { ctx._source.person_type = 'adult' } else { ctx._source.person_type = 'teenager' }",
     "params": {
       "age": 19
     }
   }
 }

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

{
   "id": 2,
   "name": "name update 1",
   "age": "55",
   "gender": "M",
   "email": "user2@gmail.com",
   "last_modified_date": "2017-02-18",
   "person_type": "adult"
 }

Из ответа можно увидеть, что в документе появилось новое поле person_type = adult (взрослый).

Upsert

При частичном обновлении документа, если документ еще не существует, обновление завершится неудачно. Если вы хотите создать новый документ, если документ не существует, вы можете установить doc_as_upsert флаг в true. Установка upsert в true приведет к созданию нового документа с полями в поле doc. Приведем пример:

 POST example4/person/3/_update
 {
   "doc": {
     "name": "user3"
   },
   "doc_as_upsert": true
 }

В предыдущем примере, поскольку документ id 3 не существует, создается новый документ. Теперь давайте посмотрим на него. Ответ следующий:

  {
   "_index": "example4",
   "_type": "person",
   "_id": "3",
   "_version": 1,
   "found": true,
   "_source": {
     "name": "user3"
   }
 }

Из ответа видно, что новый документ содержит только поле name.

NOOP

В предыдущем разделе мы обновили документ id 2, где имени присвоили значение «name update 2»:

 POST example4/person/2/_update
 {
   "doc": {
     "name": "name update 2"
   }
 }

Если вы попытаетесь снова запустить обновление, операция будет проигнорирована, так как изменений нет. Ответ будет содержать result (результат) равный noop, как показано здесь:

{
   "_index": "example4",
   "_type": "person",
   "_id": "2",
   "_version": 1,
   "result": "noop",
   "_shards": {
     "total": 0,
     "successful": 0,
     "failed": 0
   }
 }

Вы можете отключить это поведение, установив detect_noop в false:

POST example4/person/2/_update 
{ 
  "doc" : {
     "name" : "name update 2"
  },
  "detect_noop" : "false"
}

С detect_noop установленным значением false, произойдет обновление документа не взирая что данные уже есть в документе. Ответ следующий:

{
   "_index": "example4",
   "_type": "person",
   "_id": "2",
   "_version": 2,
   "result": "updated",
   "_shards": {
     "total": 2,
     "successful": 1,
     "failed": 0
   }
 }

Что происходит при обновлении документа?

В предыдущем разделе мы обсудили, что происходит, когда вы индексируете документ. В этом разделе мы обсудим, как обрабатываются обновления документов и почему обновление является дорогостоящей операцией.

Как обсуждалось ранее, когда документ индексируется, он сохраняется в сегменте. По дизайну сегмент, после создания, не может быть изменен. Будучи неизменным, вы получаете несколько преимуществ. Например, как только файл сегмента считывается в кеш файловой системы, он может жить там вечно, поскольку он не изменяется, и Lucene не нужно беспокоиться о блокировке файла для любых изменений. Но если сегмент не может быть изменен, как мы можем обновить существующий документ? Чтобы выполнить обновление, сначала существующий документ будет мягко удален, а обновленный документ будет проиндексирован как новый документ.

Слияние сегментов

По умолчанию процесс обновления создает новый сегмент каждую секунду. Это приведет к созданию множества сегментов. Поскольку поиск по осколку должен проходить через все сегменты в осколке, наличие большого количества сегментов замедлит работу поиска.

Сегменты также нуждаются в большом количестве ресурсов, таких как обработчики файлов, процессор, дисковое пространство и память. Чтобы уменьшить количество сегментов, Lucene объединяет сегменты аналогичного размера в более крупный сегмент. При слиянии сегментов документы, помеченные как удаленные, не копируются в объединенный сегмент. Пока сегменты объединяться, документ физически не удаляется с диска:

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

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *