Elasticsearch-脚本过滤嵌套对象列表

时间:2019-07-05 14:24:32

标签: elasticsearch elasticsearch-painless

我试图弄清楚如何解决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-过滤文档,其中数组的第一个元素具有“名称” ==“约翰”

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

{
  "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
                   }

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

2 个答案:

答案 0 :(得分:1)

  

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

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

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

  

2-过滤文档,其中数组的第一个元素具有“名称” ==“约翰”

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

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

答案 1 :(得分:1)

Val注意到,您无法在Elasticsearch的最新版本中的脚本查询中访问_source个文档。 但是elasticsearch允许您在“得分上下文”中访问此_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"
              }
            }
          }
        }
      ]
    }
  }
}

应在参数中设置Employee的名称和名字,以避免针对此脚本的每个用例重新编写脚本。

但是请记住,如Val所述,它在您的集群上可能非常繁重。您应该通过在function_score query(在我的示例中为match_all)中添加过滤器来缩小文档的适用范围。 而且无论如何,这都不是应使用Elasticsearch的方式,并且您无法期望通过这种被黑的查询获得出色的性能。