提取用于创建/更新文档日期的管道

时间:2018-07-12 10:51:41

标签: elasticsearch

我正在尝试实现类似于Mysql的行为,即通过ES管道为我索引的每个文档在元数据上添加insertd_at / updated_at。

我的管道就像:

{
  "description": "Adds createdAt and updatedAt style timestamps",
  "processors": [
    {
      "set": {
        "field": "_source.indexed_at",
        "value": "{{_ingest.timestamp}}",
        "override": false
      }
    },
    {
      "set": {
        "field": "_source.updated_at",
        "value": "{{_ingest.timestamp}}",
        "override": true
      }
    }
  ]
}

我想没有映射只尝试添加一个文档:

POST test_pipelines/doc/1?pipeline=timestamps
{
  "foo": "bar"
}

管道成功创建了indexed_atupdated_at

{
  "_index": "test_pipelines",
  "_type": "doc",
  "_id": "1",
  "_score": 1,
  "_source": {
    "indexed_at": "2018-07-12T10:47:27.957Z",
    "updated_at": "2018-07-12T10:47:27.957Z",
    "foo": "bar"
  }
}

但是,如果我尝试更新文档1,则字段indexed_at每次都会更改为与文档更新的日期相同。

更新请求示例:

POST test_pipelines/doc/1?pipeline=timestamps
{
  "foo": "bor"
}

有什么方法可以告诉处理器不要更新indexed_at字段?

1 个答案:

答案 0 :(得分:3)

发生这种情况的原因是,set处理器将仅在您要发送的文档的上下文中运行,而不是在存储的文档(如果有)中运行。因此,override在这里无效,因为您发送的文档既不包含indexed_at也不包含updated_at,这就是在每次调用时都设置两个字段的原因。

第二次PUT文档时,您没有更新它,实际上是从头开始为它重新编制索引(即,您覆盖了您发送的第一个版本)。提取管道do not work with update operations。例如,如果您尝试以下更新调用,它将失败。

POST test_pipelines/doc/1/_update?pipeline=timestamps
{
  "doc": {
    "foo": "bor"
  }
}

如果您要坚持使用摄取管道,那么使其生效的唯一方法是首先GET文档,然后更新所需的字段。例如,

# 1. index the document the first time
PUT test_pipelines/doc/1?pipeline=timestamps
{
  "foo": "bar"
}

# 2. GET the indexed document
GET test_pipelines/doc/1

# 3. update the foo field and index it again
PUT test_pipelines/doc/1?pipeline=timestamps
{
  "indexed_at": "2018-07-20T05:08:52.293Z",
  "updated_at": "2018-07-20T05:08:52.293Z",
  "foo": "bor"
}

# 4. When you GET the document the second time, you'll see your pipeline worked
GET test_pipelines/doc/1

这将返回:

{
  "indexed_at": "2018-07-20T05:08:52.293Z",
  "updated_at": "2018-07-20T05:08:53.345Z",
  "foo": "bor"
}

我绝对同意这确实很麻烦,但是我上面给出的链接列举了更新操作不支持管道的所有原因。

使它按您喜欢的方式(没有管道)工作的另一种方法是使用脚本化的upsert操作(其工作方式与上述步骤2和3相同,即在单个原子操作中对文档进行GET和PUT),并且也可以与您的批量通话配合使用。基本上是这样的。首先,您需要存储一个将用于索引和更新操作的脚本:

POST _scripts/update-doc
{
  "script": {
    "lang": "painless",
    "source": "ctx._source.foo = params.foo; ctx._source.updated_at = new Date(); if (ctx._source.indexed_at == null) ctx._source.indexed_at = ctx._source.updated_at;"
  }
}

然后,您可以像这样第一次在文档中建立索引:

POST test_pipelines/doc/1/_update
{
  "script": {
    "id": "update-doc",
    "params": {
      "foo": "bar"
    }
  },
  "scripted_upsert": true,
  "upsert": {}
}

索引文档将如下所示:

{
    "updated_at": "2018-07-20T05:57:40.510Z",
    "indexed_at": "2018-07-20T05:57:40.510Z",
    "foo": "bar"
}

更新文档时,您可以使用完全相同的调用:

POST test_pipelines/doc/1/_update
{
  "script": {
    "id": "update-doc",
    "params": {
      "foo": "bor"             <--- only this changes
    }
  },
  "scripted_upsert": true,
  "upsert": {}
}

更新后的文档将如下所示,正是您想要的:

{
    "updated_at": "2018-07-20T05:58:42.825Z",
    "indexed_at": "2018-07-20T05:57:40.510Z",
    "foo": "bor"
}