行动是治愈恐惧的良药,而犹豫、拖延将不断滋养恐惧。

ES先去重后聚合如何实现

Elasticsearch | 作者 caster_QL | 发布于2022年01月18日 | 阅读数:2559

求教大佬们:ES如何先聚合取TOP1(或者去重),然后再对结果进行另一个字段的聚合
数据示例:
a,10,bj
a,11,bj
b,12,bj
b,13,bj
c,14,sh
c,15,sh
先取出 a,11,bj   b,13,bj   c,15,sh
在将这三条按城市聚合,最后获得 bj,2 sh,1
我是想把第一次聚合后每个桶内拿出一条,好比100个桶,共拿出100条。然后再根据另一个字段对这100条聚合
已邀请:

Ombres

赞同来自: caster_QL

我自己测过一种去重检索的思路(实际上是去重取top1),可能有一些前提。
去重检索后,然后对你要的字段进行聚合就可以了..
 
以field1为例
1.必须把有相同的field1的数据放到同一个shard中,也就是用field1做route
2.field1需要使用index sort,也就是有序的
3.shard中需要合并成一个段,index sort是段内有序的,多个段的话可能出现乱序的问题
 
然后说思路,就是取disi的时候,遍历所有该field1的所有term,每个term取第一个docid,添加到disi里面
 
我还是直接贴代码吧
  static class DistinctQuery extends Query {

String field = null;

public DistinctQuery(String field) {
this.field = field;
}

@Override
public String toString(String field) {
return null;
}

@Override
public boolean equals(Object obj) {
return false;
}

@Override
public int hashCode() {
return 0;
}

@Override
public Weight createWeight(IndexSearcher searcher, ScoreMode scoreMode, float boost) throws IOException {

return new ConstantScoreWeight(this, boost) {

@Override
public boolean isCacheable(LeafReaderContext ctx) {
return false;
}

@Override
public Scorer scorer(LeafReaderContext context) throws IOException {
BinaryDocValues values = DocValues.getBinary(context.reader(), field);
if(values.advanceExact(1)){
values.binaryValue();
}

final DocIdSetIterator disi = getDocIdSetIterator(context);

return new ConstantScoreScorer(this, 0 ,scoreMode, disi);
}

};
}

private DocIdSetIterator getDocIdSetIterator(LeafReaderContext context) throws IOException {
FixedBitSet bits = new FixedBitSet(context.reader().maxDoc());
if (field != null) {
TermsEnum termsEnum = context.reader().terms(field).iterator();
if (termsEnum != null) {
while (termsEnum.next() != null) {
termsEnum.term();
PostingsEnum pe = termsEnum.postings(null, PostingsEnum.NONE);
if (pe.nextDoc() != DocIdSetIterator.NO_MORE_DOCS) {
bits.set(pe.docID());
}
}
}
}
return new BitSetIterator(bits, 0);
}
}

要回复问题请先登录注册