两个集合的共同Elasticsearch

时间:2019-07-23 18:05:29

标签: elasticsearch aggregation elasticsearch-aggregation elasticsearch-py

我想查找顶级作者和顶级合著者的通用文档计数,它们是索引中源的书目数据字段内的字段。

我当前正在做的是:

1。计算排名前10位的作者的汇总(A,B,C,D .....)。

2。计算排名前10位的合著者(X,Y,Z,...)的汇总。

3。计算相交点的文档计数,例如这些对之间的通用文档计数:

[(A,X),(B,Y)....]。 <-----结果

我尝试了子存储桶聚合,但是它给了我: [A :(与A对应的前10位,B :(与B对应的前10位).....]。

1 个答案:

答案 0 :(得分:1)

好,因此,从上面的评论继续作为答案,使其更易于阅读且不受字符限制。

  

评论

     

我认为您不能使用管道聚合来实现它。

     

我想在客户端处理的内容并不多。仅20条记录(作者10条,合著者10条),这将是简单的汇总查询。

     

另一种选择是在两个字段中都获得前十名,并且还可以进行简单的agg查询。

     

但是,如果您真的需要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名合著者的交集。这是所有要素的交集,然后获得top10。

第三个选项是编写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