Elasticsearch:foreach 摄入处理器介绍

foreach processor 用于处理未知长度数组中的元素。这个有点和我们在编程中使用的循环是一样的。

所有处理器都可以对数组内的元素进行操作,但是如果需要以相同的方式处理数组中的所有元素,则为每个元素定义处理器将变得既麻烦又棘手,因为数组中元素的数量可能未知。因此,存在 foreach 处理器。通过指定包含数组元素的字段和定义每个元素应该发生什么的处理器,可以轻松地预处理数组字段。

foreach 处理器内部的处理器在数组元素上下文中工作,并将其放在 _ingest._value 键下的摄取元数据中。如果 array 元素是 json 对象,则它将保存该 json 对象的所有字段。如果嵌套对象是一个值,则 _ingest._value 仅保留该值。请注意,如果在 foreach 处理器之前的处理器使用 _ingest._value 键,则指定的值将不可用于 foreach 处理器内部的处理器。 foreach 处理器确实会恢复原始值,因此该值可在 foreach 处理器之后供处理器使用。

请注意,文档中的其他任何字段都可以像其他所有处理器一样进行访问和修改。该处理器只是将当前正在读取的数组元素放入_ingest._value 摄取元数据属性中,以便对其进行预处理。

如果 foreach 处理器无法处理数组中的元素,并且未指定 on_failure 处理器,则它将中止执行并保留数组不变。

 

例子

假如我们导入如下的两个文件到索引 my_index 中:

PUT my_index/_doc/1
{
  "tags": "Movies,Beauty,Music,food"
}

PUT my_index/_doc/2
{
  "tags": ""
}

在上面,我们包含有两种类型的文档:一种是一个字符串组成的标签,另外一种是标签为空。这个好比针对一些应用对视频进行画像。通过这样的标签,我们可以对视频进行分类,以便以后进行搜索。

针对 tags 为空的情况,我们想为它设置默认的分类,比如 Music。为此,我们设计如下的 ingest pipeline:

PUT _ingest/pipeline/my_pipeline
{
  "processors": [
    {
      "set": {
        "if": "ctx['tags'].empty",
        "field": "tags",
        "value": "Music"
      }
    }
  ]
}

在上面,我们使用了 "if" 条件来检查 tags 是否为空,如果为空,那么设置默认的 tags 为 Music。运行上面的 pipeline:

我们可以通过如下的方法来进行测试:

POST _ingest/pipeline/my_pipeline/_simulate
{
  "docs": [
    {
      "_source": {
        "tags": "Movies,Beauty,Music,Food"
      }
    },
    {
      "_source":{
        "tags": ""
      }
    }
  ]
}

运行上面的测试文档:

{
  "docs" : [
    {
      "doc" : {
        "_index" : "_index",
        "_type" : "_doc",
        "_id" : "_id",
        "_source" : {
          "tags" : "Movies,Beauty,Music,Food"
        },
        "_ingest" : {
          "timestamp" : "2020-09-16T06:55:36.657202Z"
        }
      }
    },
    {
      "doc" : {
        "_index" : "_index",
        "_type" : "_doc",
        "_id" : "_id",
        "_source" : {
          "tags" : "Music"
        },
        "_ingest" : {
          "timestamp" : "2020-09-16T06:55:36.657205Z"
        }
      }
    }
  ]
}

我们可以看到针对 tags 为空的文档,tags 变为 Music。

接下来,我们想对 tags 进行拆分,让它变为数组,这样更便于搜索。我们使用 split processor:

PUT _ingest/pipeline/my_pipeline
{
  "processors": [
    {
      "set": {
        "if": "ctx['tags'].empty",
        "field": "tags",
        "value": "Music"
      }
    },
    {
      "split": {
        "if": "!(ctx['tags'].empty)",
        "field": "tags",
        "separator": ","
      }
    }
  ]
}

上面的 split processor 以逗号分开上面的字符串,并形成数组。运行上面的 pipepline,并以上面的测试文档进行测试:

{
  "docs" : [
    {
      "doc" : {
        "_index" : "_index",
        "_type" : "_doc",
        "_id" : "_id",
        "_source" : {
          "tags" : [
            "Movies",
            "Beauty",
            "Music",
            "Food"
          ]
        },
        "_ingest" : {
          "timestamp" : "2020-09-16T06:58:51.956573Z"
        }
      }
    },
    {
      "doc" : {
        "_index" : "_index",
        "_type" : "_doc",
        "_id" : "_id",
        "_source" : {
          "tags" : [
            "Music"
          ]
        },
        "_ingest" : {
          "timestamp" : "2020-09-16T06:58:51.956577Z"
        }
      }
    }
  ]
}

从上面我们可以看出来,tags 现在变成为数组了。但是,我们发现,上面的第一个字母都是大写,可能我们都觉得不是很爽。我们可以使用 foreach processor 来把每个单词变为小写的。我们进一步改造为:

PUT _ingest/pipeline/my_pipeline
{
  "processors": [
    {
      "set": {
        "if": "ctx['tags'].empty",
        "field": "tags",
        "value": "Music"
      }
    },
    {
      "split": {
        "if": "!(ctx['tags'].empty)",
        "field": "tags",
        "separator": ","
      }
    },
    {
      "foreach": {
        "if": "!ctx['tags'].empty",
        "field": "tags",
        "processor": {
          "lowercase": {
            "field": "_ingest._value"
          }
        }
      }
    }
  ]
}

在上面,我们使用 foreach processor 来遍历 tags 里的所有字符串。在里面我们使用了 lowercase 这个 processor 来把所有的字母变为小写。运行上面的 pipeline,并以上面的文档来进行测试:

{
  "docs" : [
    {
      "doc" : {
        "_index" : "_index",
        "_type" : "_doc",
        "_id" : "_id",
        "_source" : {
          "tags" : [
            "movies",
            "beauty",
            "music",
            "food"
          ]
        },
        "_ingest" : {
          "_value" : null,
          "timestamp" : "2020-09-16T07:02:32.963724Z"
        }
      }
    },
    {
      "doc" : {
        "_index" : "_index",
        "_type" : "_doc",
        "_id" : "_id",
        "_source" : {
          "tags" : [
            "music"
          ]
        },
        "_ingest" : {
          "_value" : null,
          "timestamp" : "2020-09-16T07:02:32.963728Z"
        }
      }
    }
  ]
}

这一次,我们看到了所有的在 tags 里的字母都变为小写的字母了。