好的,所以从上面的 cmets 继续作为答案,使其更易于阅读并且没有字符限制。
评论
我不认为你可以使用管道聚合来实现它。
我猜在客户端处理的并不多。只有 20 条记录(作者 10 条,共同作者 10 条),这将是简单的聚合查询。
另一种选择是在两个字段中都获得前 10 名以及简单的聚合查询。
但是如果你真的需要 ES 端两个 top10 的交集,请使用Scripted Metric Aggregation。你可以把你的逻辑放在代码中
第一个选项很简单:
GET index_name/_search
{
"size": 0,
"aggs": {
"firstname_dupes": {
"terms": {
"field": "authorFullName.keyword",
"size": 10
}
},
"lastname_dupes": {
"terms": {
"field": "coauthorFullName.keyword",
"size": 10
}
}
}
}
然后在客户端对结果进行交集。
第二看起来像:
GET index_name/_search
{
"size": 0,
"aggs": {
"name_dupes": {
"terms": {
"script": {
"source": "return [doc['authorFullName.keyword'].value,doc['coauthorFullName.keyword'].value]"
}
, "size": 10
}
}
}
}
但这并不是前 10 位作者和前 10 位合著者的真正交集。这是所有的交集,然后进入前10。
第三个选项是写Scripted Metric Aggregation。没有时间花在算法方面(应该优化),但它可能看起来像这个。当然,java技能会帮助你。还要确保您了解脚本化指标聚合执行的所有阶段以及使用它时可能遇到的性能问题。
GET index_name/_search
{
"size": 0,
"query" : {
"match_all" : {}
},
"aggs": {
"profit": {
"scripted_metric": {
"init_script" : "state.fnames = [:];state.lnames = [:];",
"map_script" :
"""
def key = doc['authorFullName.keyword'];
def value = '';
if (key != null && key.value != null) {
value = state.fnames[key.value];
if(value==null) value = 0;
state.fnames[key.value] = value+1
}
key = doc['coauthorFullName.keyword'];
if (key != null && key.value != null) {
value = state.lnames[key.value];
if(value==null) value = 0;
state.lnames[key.value] = value+1
}
""",
"combine_script" : "return state",
"reduce_script" :
"""
def intersection = [];
def f10_global = new HashSet();
def l10_global = new HashSet();
for (state in states) {
def f10_local = state.fnames.entrySet().stream().sorted(Collections.reverseOrder(Map.Entry.comparingByValue())).limit(10).map(e->e.getKey()).collect(Collectors.toList());
def l10_local = state.lnames.entrySet().stream().sorted(Collections.reverseOrder(Map.Entry.comparingByValue())).limit(10).map(e->e.getKey()).collect(Collectors.toList());
for(name in f10_local){f10_global.add(name);}
for(name in l10_local){l10_global.add(name);}
}
for(name in f10_global){
if(l10_global.contains(name)) intersection.add(name);
}
return intersection;
"""
}
}
}
}
请注意,这里的查询假设您在这些属性上有 keyword。如果不只是根据您的情况调整它们。
更新
PS,刚刚注意到您提到您需要通用计数,而不是通用名称。不知道是什么情况,但不要使用map(e->e.getKey()),而是使用map(e->e.getValue().toString())。类似问题见the other answer