elasticsearch node.js API 使用无痛脚本从文档中的数组中删除对象导致数组索引超出范围

elasticsearch node.js API remove an object from an array on a document using painless script results in array Index Out of Bounds

我想从 elasticsearch 文档的数组中删除项目(一个对象),但是每当我尝试 运行 我的更新脚本使用 painless 时,我都会收到数组索引越界异常。

我正在使用 javascript elasticsearch npm package 在 elasticsearch 中搜索相关文档,然后 returns 我的数据如下:

"_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;});
    }
}

当我 运行 虽然如此时,它 returns 一个 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 是否允许搜索数组中对象的属性?

我偶然发现了这个问题和答案:Elasticsearch: Get object index with Painless script

更新脚本的主体需要像这样更改:

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

我不熟悉无痛脚本,所以很可能有更好的编写方法,例如一旦找到 ID 的索引就会中断。

我还必须将刷新语句移到 Promise.all 中,因为如果您尝试从对象数组中删除多个项目,您将更改文档并更改索引.可能还有更好的方法来处理这个问题。

'openDayID' 应该是 params.openDayID

并使用 removeIf:

"ctx._source.openDays.removeIf(el -> (el.id == params.openDayID))"