【问题标题】:Elasticsearch - Script Filter over a list of nested objectsElasticsearch - 对嵌套对象列表进行脚本过滤
【发布时间】:2019-07-09 16:27:31
【问题描述】:

我正试图弄清楚如何解决我在 ES 5.6 索引中遇到的这两个问题。

"mappings": {
    "my_test": {
        "properties": {
            "Employee": {
                "type": "nested",
                "properties": {
                    "Name": {
                        "type": "keyword",
                        "normalizer": "lowercase_normalizer"
                    },
                    "Surname": {
                        "type": "keyword",
                        "normalizer": "lowercase_normalizer"
                    }
                }
            }
        }
    }
}

我需要创建两个单独的脚本过滤器:

1 - 过滤员工数组大小为 == 3 的文档

2 - 过滤数组第一个元素为“Name”==“John”的文档

我试图迈出第一步,但无法遍历列表。我总是有空指针异常错误。

{
  "bool": {
    "must": {
      "nested": {
        "path": "Employee",
        "query": {
          "bool": {
            "filter": [
              {
                "script": {
                  "script" :     """

                   int array_length = 0; 
                   for(int i = 0; i < params._source['Employee'].length; i++) 
                   {                              
                    array_length +=1; 
                   } 
                   if(array_length == 3)
                   {
                     return true
                   } else 
                   {
                     return false
                   }

                     """
                }
              }
            ]
          }
        }
      }
    }
  }
}

【问题讨论】:

    标签: elasticsearch elasticsearch-painless


    【解决方案1】:

    正如 Val 所注意到的,在最新版本的 Elasticsearch 中,您无法在脚本查询中访问 _source 的文档。 但是弹性搜索允许您在“分数上下文”中访问这个_source

    因此,一种可能的解决方法(但您需要注意性能)是在查询中结合使用脚本分数和 min_score。

    您可以在此堆栈溢出帖子Query documents by sum of nested field values in elasticsearch 中找到此行为的示例。

    在你的情况下,这样的查询可以完成这项工作:

    POST <your_index>/_search
    {
      "min_score": 0.1,
      "query": {
        "function_score": {
          "query": {
            "match_all": {}
          },
          "functions": [
            {
              "script_score": {
                "script": {
                  "source": """
                  if (params["_source"]["Employee"].length === params.nbEmployee) {
                    def firstEmployee = params._source["Employee"].get(0);
                    if (firstEmployee.Name == params.name) {
                      return 1;
                    } else {
                      return 0;
                    }
                  } else {
                    return 0;
                  }
    """,
                  "params": {
                    "nbEmployee": 3,
                    "name": "John"
                  }
                }
              }
            }
          ]
        }
      }
    }
    

    应在参数中设置员工的数量和名字,以避免此脚本的每个用例都重新编译脚本。

    但请记住,正如 Val 已经提到的,它对您的集群可能非常沉重。您应该通过在function_score query(在我的示例中为 match_all )中添加过滤器来缩小您将应用脚本的文档集。 无论如何,这不是 Elasticsearch 的使用方式,你不能指望这样一个被黑的查询有出色的表现。

    【讨论】:

    • 我不想提及这种可能性,因为它可能并不意味着这样做是正确的 ;-) 对我来说,正确的做法是定义一个足够的映射来支持用例。
    • 我同意@Val 但由于似乎不可能更改映射,这是最后的希望:p
    • 用例变化,需求不断变化,因此将所有事情都一成不变是不合理的,即没有什么是不可能的 ;-)
    【解决方案2】:

    1 - 过滤员工数组大小为 == 3 的文档

    对于第一个问题,最好的办法是添加另一个根级字段(例如NbEmployees),其中包含Employee 数组中的项目数,以便您可以使用range 查询和不是昂贵的script 查询。

    然后,每当您修改 Employee 数组时,您也会相应地更新 NbEmployees 字段。效率更高!

    2 - 过滤数组第一个元素为“Name”==“John”的文档

    关于这一点,您需要知道嵌套字段是 Lucene 中的单独(隐藏)文档,因此无法在同一个查询中一次访问所有嵌套文档。

    如果您知道需要在查询中检查第一个员工的姓名,只需添加另一个根级字段 FirstEmployeeName 并在该字段上运行查询。

    【讨论】:

    • 感谢 Val 的回答。不幸的是,我无法在映射中添加新字段。所以我需要使用脚本。关于第二点,您是说没有办法在无痛中迭代嵌套对象列表吗?
    • 您无法添加新字段,因为您的映射是用dynamic: strict/false定义的?
    • 不,我们系统的内部政策
    • 据我所知,无法访问script 查询中的_source 字段。这曾经是可能的,但这种可能性已被消除,因为它是一个很大的性能瓶颈,因为所有文档都必须通过脚本按照查询运行的顺序进行评估。如果您的文档正文较大,这可能会影响您的性能,并最终影响您的集群。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2022-12-22
    • 1970-01-01
    • 2018-06-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多