是否可以在 Elasticsearch Painless 脚本中转换 JSON 数据,并对其执行进一步的操作?

Is is possible to transform JSON data in an Elasticseach Painless script, and perform further operations on it?

我们有大量 JSON-formatted 文档可供搜索以查找模式和历史趋势。 Elasticsearch 似乎非常适合解决这个问题。第一个技巧是文档是 collections 数万个 "nested" 文档(带有 header)。第二个技巧是这些嵌套文档代表不同类型的数据。

为了适应这一点,所有值字段都已 "encoded" 作为字符串数组,因此单个整数值已存储在 JSON 中作为“[\”1\ "]",一个 table 的浮点数被展平为 "[\"123.45\",\"678.9\",...]" 等等。 (我们还有不需要转换的字符串数组。)虽然这很尴尬,但考虑到 Elasticsearch 中涉及的所有其他内容似乎都有效,我认为这是一个很好的折衷方案。

这里的特殊问题是这些存储的数据值可能代表一个位域,我们可能需要从中检查一位的状态。由于此字段将存储为 single-element 字符串数组,如“[\”14657\”],我们需要将其转换为单个整数,然后 bit-shift 多次转换为所需的位(或应用掩码,如果此类功能可用)。

使用 Elasticsearch,我看到我可以嵌入 "Painless" 脚本,但是示例各不相同,而且我一直没能找到一个说明如何隐藏 arbitrary-length string-array 数据字段到适当的类型,以便进一步比较。这是我的查询脚本。

{
  "_source" : false,
  "from" : 0, "size" : 10,
  "query": {
    "nested": {
      "path": "Variables",
      "query": {
        "bool": {
          "must": {
            "match": {"Variables.Designation": "Big_Long_Variable_Name"}
          },
          "must_not": {
            "match": {"Variables.Data": "[0]"}
          },
          "filter": {
            "script": {
              "script": {
                "source":
                "
                  def vals = doc['Variables.Data'];
                  return vals[0] != params.setting;
                ",
                "params": {
                  "setting": 3
                }
              }
            }
          }
        }
      },
      "inner_hits": {
        "_source": "Variables.Data"
      }
    }
  }
}

我需要以某种方式将 vals 变量转换为整数数组,选取第一个值,进行一些位运算,然后与 return 进行比较是真还是假。在此示例中,我希望能够将 "setting" 设置为等于我要检查 on/off.

的位位置

我已经完成了 Elasticsearch 的练习,发现我需要将我的 Variables.Data 字段设为关键字,以便我可以在其中搜索特定值。我意识到这偏离了 Elasticsearch 的意图,但出于其他原因,我仍然认为这可能是最佳解决方案。我创建了一个新索引,并重新导入了我的测试文档,索引大小增加了大约 30%。这是我愿意做出的妥协,如果我能让它发挥作用的话。

我在 Painless 中有什么工具可以完成这项工作? (或者,我是不是很想用这个工具来做这件事?)

我建议您尽可能(甚至在不提供的情况下)在 elasticsearch 提供的类型中对您的数据进行编码,以充分利用无痛。例如,对于位字符串,您可以将它们编码为 1 和 0 的数组,以便使用 Painless 进行更轻松的操作。

无痛,在我看来,还是很原始的。很难调试。很难读。很难维护。而且,在 Painless 中拥有大量功能是一个可怕的想法。

要回答您的问题,您基本上需要使用 painless 解析数组字符串并将其置于可用数据类型之一中,以便进行您想要的比较。例如,对于列表,您可以使用 split 函数之类的东西,然后手动将结果中的每个项目大小写为 int、float、string 等...

在将其添加到您的脚本字段之前,使用执行 API 测试小位:

POST /_scripts/painless/_execute
{
  "script": {
    "source": """
    ArrayList arr = []; //to start with
    // use arr.add(INDEX, VALUE) to add after parsing
    """,
    "params": {
      "foo": 100.0,
      "bar": 1000.0
    }
  }
}

另一方面,如果您将数据保存在 ElasticSearch 提供的数据类型中(请注意,ElasticSearch 支持在文档中保存列表),那么在 Painless 中完成此任务会容易得多。

例如,与其将 my_doc.foo = "[\"123.45\",\"678.9\",...]" 作为稍后要解析的字符串,不如将其另存为原生的浮点数列表,例如 my_doc.foo = [123.45, 678.9, ...]?

这样,您就可以避免解析文本文档所需的不必要的 Painless 代码。