ElasticSearch 在脚本中访问嵌套文档 - 空指针异常

ElasticSearch Accessing Nested Documents in Script - Null Pointer Exception

要点:尝试使用 painless 在嵌套文档上编写自定义过滤器。想在没有嵌套文档超过null_pointer_exception

的时候写错误检查

我有这样的映射(经过简化和混淆)

{
  "video_entry" : {
    "aliases" : { },
    "mappings" : {
      "properties" : {
       
        "captions_added" : {
          "type" : "boolean"
        },
        "category" : {
          "type" : "keyword"
        },
           
        "is_votable" : {
          "type" : "boolean"
        },
      
        "members" : {
          "type" : "nested",
          "properties" : {
            "country" : {
              "type" : "keyword",
            },
            "date_of_birth" : {
              "type" : "date",
            }
        }
   }
}

每个 video_entry 文档可以有 0 个或多个 members 嵌套文档。

示例文档

{
   "captions_added": true,
   "category"      : "Mental Health",
   "is_votable:    : true,
   "members": [
        {"country": "Denmark", "date_of_birth": "1998-04-04T00:00:00"},
        {"country": "Denmark", "date_of_birth": "1999-05-05T00:00:00"}
   ]

}

如果存在一个或多个嵌套文档,我们希望编写一些无痛脚本来检查所有嵌套文档中的某些字段。我的脚本适用于一些文档的映射,但是当我在更大的文档集上尝试它时,尽管可能进行了所有空检查,但我还是得到了空指针异常。我尝试了各种访问模式、错误检查机制,但我遇到了异常。

POST /video_entry/_search
{
  "query": {
   "script": {
     "script": {
       "source": """
          // various NULL checks that I already tried
          // also tried short circuiting on finding null values
          if (!params['_source'].empty && params['_source'].containsKey('members')) {


              def total = 0;
          
          
              for (item in params._source.members) {
                // custom logic here
                // if above logic holds true 
                // total += 1; 
              } 
          
              return total > 3;
         }
         
         return true;
          
       """,
       "lang": "painless"
     }
   }
  }
}

我尝试过的其他语句

if (params._source == null) {
    return true;
}

if (params._source.members == null) {
    return true;
}

if (!ctx._source.contains('members')) {
    return true;
}

if (!params['_source'].empty && params['_source'].containsKey('members') && 
     params['_source'].members.value != null) {
    
    // logic here

}

if (doc.containsKey('members')) {
  for (mem in params._source.members) {
  }

}

错误信息

&& params._source.members",
                 ^---- HERE"

 "caused_by" : {
            "type" : "null_pointer_exception",
            "reason" : null
          }

我研究了更改结构(扁平化文档)和 must_not as indicated in this answer 的用法。它们不适合我们的用例,因为我们需要合并更多自定义逻辑。

不同的教程使用 ctxdoc,有些使用 params。更让人困惑的是 Debug.explain(doc.members)Debug.explain(params._source.members) return 空洞的回复,我很难弄清楚类型。


要点:尝试使用 painless 在嵌套文档上编写自定义过滤器。想在没有嵌套文档超过null_pointer_exception

的时候写错误检查

感谢任何帮助。

TLDr;

Elastic 展平对象。这样

{
  "group" : "fans",
  "user" : [ 
    {
      "first" : "John",
      "last" :  "Smith"
    },
    {
      "first" : "Alice",
      "last" :  "White"
    }
  ]
}

变成:

{
  "group" :        "fans",
  "user.first" : [ "alice", "john" ],
  "user.last" :  [ "smith", "white" ]
}

要访问 members 内部值,您需要使用 doc['members.<field>'] 引用它,因为 members 不会单独存在。

详情

如您所知,Elastic 以自己的方式处理内部文档。 [doc]

因此您需要相应地引用它们。

以下是我为让它发挥作用所做的工作。 btw,我一直在用kibana的Dev tools

PUT /so_test/

PUT /so_test/_mapping
{
  "properties" : {
    "captions_added" : {
      "type" : "boolean"
    },
    "category" : {
      "type" : "keyword"
    },
    "is_votable" : {
      "type" : "boolean"
    },
    "members" : {
      "properties" : {
        "country" : {
          "type" : "keyword"
        },
        "date_of_birth" : {
          "type" : "date"
        }
      }
    }
  }
}

POST /so_test/_doc/
{
   "captions_added": true,
   "category"      : "Mental Health",
   "is_votable"    : true,
   "members": [
        {"country": "Denmark", "date_of_birth": "1998-04-04T00:00:00"},
        {"country": "Denmark", "date_of_birth": "1999-05-05T00:00:00"}
   ]
}

PUT /so_test/_doc/
{
   "captions_added": true,
   "category"      : "Mental breakdown",
   "is_votable"    : true,
   "members": []
}

POST /so_test/_doc/
{
   "captions_added": true,
   "category"      : "Mental success",
   "is_votable"    : true,
   "members": [
        {"country": "France", "date_of_birth": "1998-04-04T00:00:00"},
        {"country": "Japan", "date_of_birth": "1999-05-05T00:00:00"}
   ]
}

然后我做了这个查询(它只是一个 bool 过滤器,但我想让它适用于您自己的用例应该不会太困难)

GET /so_test/_search
{
  "query":{
    "bool": {
      "filter": {
        "script": {
          "script": {
            "lang": "painless",
            "source": """
            def flag = false;
            
            // /!\ notice how the field is referenced /!\
            if(doc['members.country'].size() != 0)
            {
              for (item in doc['members.country']) {
                if (item == params.country){
                  flag = true
                }
              } 
            }
            return flag;
            """,
            "params": {
              "country": "Japan"
            }
          }
        }
      }
    }
  }
}

顺便说一句,你说你对 painless 的上下文有点困惑。您可以在文档中找到有关它的详细信息。 [doc]

在这种情况下,filter context 就是我们要查看的那个。