首页 > 解决方案 > 比较嵌套字段的总和

问题描述

我正在尝试创建将嵌套属性与非嵌套属性进行比较的请求

映射:

{
    "mappings": {
        "publishers": {
            "properties": {
                "Accounts": {
                    "type": "nested",
                    "properties": {
                        "FollowersCount": {
                            "type": "long"
                        }
                    }
                },
                "TotalSubscribers": {
                    "type": "long"
                }
            }
        }
    }
}

我想查找 Accounts.FollowersCount 的总和不等于 TotalSubscribers 的文档。

怎么做?

PS:我正在尝试创建这样的东西:

{
    "_source": [
        "TotalSubscribers",
        "Accounts.FollowersCount"
    ],
    "query": {
        "bool": {
            "filter": {
                "script": {
                    "script": {
                        "source": "int total = 0; for (int i = 0; i < doc['Accounts'].length; ++i) {total += doc['Accounts'][i].FollowersCount;} return total == doc['TotalSubscribers'];",
                        "lang": "painless"
                    }
                }
            }
        }
    }
}

但它不起作用。当我将脚本移动到嵌套块中时,同样的问题。然后我无法访问 TotalSubscribers

更新 1:ES 版本 = 6.8

请求1

{
    "query": {
        "term": {
            "PublisherId": 349438
        }
    },
    "script": {
        "source": "ctx._source.TotalSubscribers = ctx._source.Accounts.stream().map(a -> a.FollowersCount).collect(Collectors.summingInt(Integer::intValue));"
    }
}

错误:

{
    "error": {
        "root_cause": [
            {
                "type": "script_exception",
                "reason": "runtime error",
                "script_stack": [
                    "java.util.stream.Collectors.lambda$summingInt$11(Collectors.java:467)",
                    "java.util.stream.ReduceOps$3ReducingSink.accept(ReduceOps.java:169)",
                    "java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:193)",
                    "java.util.ArrayList$ArrayListSpliterator.forEachRemaining(ArrayList.java:1382)",
                    "java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:482)",
                    "java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:472)",
                    "java.util.stream.ReduceOps$ReduceOp.evaluateSequential(ReduceOps.java:708)",
                    "java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234)",
                    "java.util.stream.ReferencePipeline.collect(ReferencePipeline.java:499)",
                    "a -> a.FollowersCount).collect(Collectors.summingInt(Integer::intValue));",
                    "                                                     ^---- HERE"
                ],
                "script": "ctx._source.TotalSubscribers = ctx._source.Accounts.stream().map(a -> a.FollowersCount).collect(Collectors.summingInt(Integer::intValue));",
                "lang": "painless"
            }
        ],
        "type": "script_exception",
        "reason": "runtime error",
        "script_stack": [
            "java.util.stream.Collectors.lambda$summingInt$11(Collectors.java:467)",
            "java.util.stream.ReduceOps$3ReducingSink.accept(ReduceOps.java:169)",
            "java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:193)",
            "java.util.ArrayList$ArrayListSpliterator.forEachRemaining(ArrayList.java:1382)",
            "java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:482)",
            "java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:472)",
            "java.util.stream.ReduceOps$ReduceOp.evaluateSequential(ReduceOps.java:708)",
            "java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234)",
            "java.util.stream.ReferencePipeline.collect(ReferencePipeline.java:499)",
            "a -> a.FollowersCount).collect(Collectors.summingInt(Integer::intValue));",
            "                                                     ^---- HERE"
        ],
        "script": "ctx._source.TotalSubscribers = ctx._source.Accounts.stream().map(a -> a.FollowersCount).collect(Collectors.summingInt(Integer::intValue));",
        "lang": "painless",
        "caused_by": {
            "type": "null_pointer_exception",
            "reason": null
        }
    },
    "status": 500
}

请求2

{
    "query": {
        "bool": {
            "must": [
                {
                    "term": {
                        "PublisherId": 349438
                    }
                }
            ]
        }
    },
    "script": {
        "source": "int total = 0; for(int i=0;i<=ctx._source.Accounts.size()-1;i++){total += ctx._source.Accounts[i].FollowersCount} ctx._source['TotalSubscribers'] = total"
    }
}

也有错误,靠近“total += ctx._source.Accounts[i].FollowersCount

请求3

{
    "query": {
        "bool": {
            "must": [
                {
                    "term": {
                        "PublisherId": 349438
                    }
                }
            ]
        }
    },
    "script": {
        "source": "int total = 0; for(int i=0;i<=ctx._source.Accounts.size()-1;i++){total += i} ctx._source['TotalSubscribers'] = total"
    }
}

工作正常,更新后 totalSubscribers == count Account items。但是需要获取 Accounts 属性,而不是我...

标签: elasticsearch

解决方案


这可以通过script查询来解决,但由于无法访问script查询中的嵌套文档,因此您必须以不同的方式进行。在这些情况下,在顶层添加一个包含 的总和的新字段总是容易得多FollowersCount,您可以将其命名为TotalFollowersCount

首先,您需要使用_update_by_query端点创建该字段,如下所示:

POST your-index/_update_by_query
{
  "script": {
    "source": """
    ctx._source.TotalFollowersCount = ctx._source.Accounts
      .stream()
      .map(a -> a.FollowersCount ?: 0)
      .collect(Collectors.summingInt(Integer::intValue));
    """
  }
}

然后您可以使用script查询来查找有问题的文档:

GET your-index/_search
{
  "query": {
    "script": {
      "script": "doc.TotalFollowersCount.value != doc.TotalSubscribers.value"
    }
  }
}

推荐阅读