API elasticsearch node.js удаляет объект из массива в документе, используя безболезненный скрипт, приводит к тому, что индекс массива выходит за границы

Я хочу удалить элементы (объект) из массива в документе в elasticsearch, однако всякий раз, когда я пытаюсь запустить свой скрипт обновления, используя безболезненно, я получаю исключение Array Index Out of Bounds.

Я использую javascript пакет npm elasticsearch для поиска в elasticsearch соответствующих документов, которые затем возвращают меня такие данные как:

"_index": "centres",
"_type": "doc",
"_id": "51bc77d1-b514-4f4e-85fa-412def6829f5",
"_score": 1,
"_source": {
    "id": "cbaa7daa-f1a2-4ac3-8d7c-fc981245d21c",
    "name": "Five House",
    "openDays": [
        {
            "title": "new open Day",
            "endDate": "2022-03-22T00:00:00.000Z",
            "id": "82be934b-eeb1-419c-96ed-a58808b30df7"
        },
        {
            "title": "last open Day",
            "endDate": "2020-12-24T00:00:00.000Z",
            "id": "8cc339b9-d2f8-4252-b68a-ed0a49cbfabd"
        }
    ]
}

Затем я хочу просмотреть и удалить определенные элементы из массива openDays. Я создал массив элементов, которые хочу удалить, поэтому для приведенного выше примера:

[
  {
    id: '51bc77d1-b514-4f4e-85fa-412def6829f5',
    indexes: [
        {
            "title": "last open Day",
            "endDate": "2020-12-24T00:00:00.000Z",
            "id": "8cc339b9-d2f8-4252-b68a-ed0a49cbfabd"
        }
    ]
  }
]

Затем я пытаюсь запустить обновление через клиент узла elasticsearch следующим образом:

for (const centre of updates) {
    if (centre.indexes.length) {
        await Promise.all(centre.indexes.map(async (theIndex) => {
            const updated = await client.update({
                index: 'centres',
                type: 'doc',
                id: centre.id,
                body: {
                    script: {
                        lang: 'painless',
                        source: "ctx._source.openDays.remove(ctx._source.openDays.indexOf('openDayID'))",
                        params: {
                            "openDayID": theIndex.id
                        }
                    }
                }
            }).catch((err) => {throw err;});
        }))
            .catch((err) => {throw err;});

        await client.indices.refresh({ index: 'centres' }).catch((err) => { throw err;});
    }
}

Однако, когда я запускаю это, он возвращает 400 с ошибкой array_index_out_of_bounds_exception:

  -> POST http://localhost:9200/centres/doc/51bc77d1-b514-4f4e-85fa-412def6829f5/_update
  {
    "script": {
      "lang": "painless",
      "source": "ctx._source.openDays.remove(ctx._source.openDays.indexOf(\u0027openDayID\u0027))",
      "params": {
        "openDayID": "8cc339b9-d2f8-4252-b68a-ed0a49cbfabd"
      }
    }
  }
  <- 400
  {
    "error": {
      "root_cause": [
        {
          "type": "remote_transport_exception",
          "reason": "[oSsa7mn][172.17.0.2:9300][indices:data/write/update[s]]"
        }
      ],
      "type": "illegal_argument_exception",
      "reason": "failed to execute script",
      "caused_by": {
        "type": "script_exception",
        "reason": "runtime error",
        "script_stack": [],
        "script": "ctx._source.openDays.remove(ctx._source.openDays.indexOf(\u0027openDayID\u0027))",
        "lang": "painless",
        "caused_by": {
          "type": "array_index_out_of_bounds_exception",
          "reason": null
        }
      }
    },
    "status": 400
  }

Я не совсем уверен, где я ошибаюсь. Правильно ли я использую безболезненный скрипт indexOf? Позволяет ли indexOf искать свойства объектов в массивах?


person Jarede    schedule 16.04.2021    source источник


Ответы (1)


Я наткнулся на этот вопрос и ответ: Elasticsearch: получить индекс объекта с помощью скрипта Painless

Тело сценария обновления необходимо изменить следующим образом:

Promise.all(...
const inline = `
    def openDayID = '${theIndex.id}'; 
    def openDays = ctx._source.openDays;
    def openDayIndex = -1;
    for (int i = 0; i < openDays.length; i++)
    { 
        if (openDays[i].id == openDayID) 
        { 
            openDayIndex = i;  
        } 
    }
    if (openDayIndex != -1) {
        ctx._source.openDays.remove(openDayIndex);
    }
`;
const updated = await client.update({
index: 'centres',
type: 'doc',
id: centre.id,
body: {
    script: {
        lang: 'painless',
        inline: inline,
    },
}
}).catch((err) => {throw err;});

await client.indices.refresh({ index: 'centres' }).catch((err) => { throw err;});
})).catch(... //end of Promise.all

Я не уверен в безболезненном написании сценариев, поэтому, скорее всего, есть лучшие способы написать это, например. ломается, как только индекс идентификатора найден.

Мне также пришлось переместить оператор обновления в Promise.all, поскольку, если вы пытаетесь удалить более одного элемента из массива объектов, вы будете изменять документ и индекс. Вероятно, есть и лучший способ справиться с этим.

person Jarede    schedule 18.04.2021