Skip to content

Commit 2a6ec98

Browse files
authored
Adjacency_matrix aggregation optimisation. (#46257)
Avoid pre-allocating ((N * N) - N) / 2 “BitsIntersector” objects given N filters. Most adjacency matrices will be sparse and we typically don’t need to allocate all of these objects - can save a lot of allocations when the number of filters is high. Closes #46212
1 parent a238248 commit 2a6ec98

File tree

1 file changed

+18
-35
lines changed

1 file changed

+18
-35
lines changed

server/src/main/java/org/elasticsearch/search/aggregations/bucket/adjacency/AdjacencyMatrixAggregator.java

Lines changed: 18 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -52,10 +52,6 @@
5252
/**
5353
* Aggregation for adjacency matrices.
5454
*
55-
* TODO the aggregation produces a sparse response but in the
56-
* computation it uses a non-sparse structure (an array of Bits
57-
* objects). This could be changed to a sparse structure in future.
58-
*
5955
*/
6056
public class AdjacencyMatrixAggregator extends BucketsAggregator {
6157

@@ -143,51 +139,38 @@ public AdjacencyMatrixAggregator(String name, AggregatorFactories factories, Str
143139
this.totalNumKeys = keys.length + totalNumIntersections;
144140
}
145141

146-
private static class BitsIntersector implements Bits {
147-
Bits a;
148-
Bits b;
149-
150-
BitsIntersector(Bits a, Bits b) {
151-
super();
152-
this.a = a;
153-
this.b = b;
154-
}
155-
156-
@Override
157-
public boolean get(int index) {
158-
return a.get(index) && b.get(index);
159-
}
160-
161-
@Override
162-
public int length() {
163-
return Math.min(a.length(), b.length());
164-
}
165-
166-
}
167-
168142
@Override
169143
public LeafBucketCollector getLeafCollector(LeafReaderContext ctx, final LeafBucketCollector sub) throws IOException {
170144
// no need to provide deleted docs to the filter
171-
final Bits[] bits = new Bits[filters.length + totalNumIntersections];
145+
final Bits[] bits = new Bits[filters.length];
172146
for (int i = 0; i < filters.length; ++i) {
173147
bits[i] = Lucene.asSequentialAccessBits(ctx.reader().maxDoc(), filters[i].scorerSupplier(ctx));
174148
}
175-
// Add extra Bits for intersections
176-
int pos = filters.length;
177-
for (int i = 0; i < filters.length; i++) {
178-
for (int j = i + 1; j < filters.length; j++) {
179-
bits[pos++] = new BitsIntersector(bits[i], bits[j]);
180-
}
181-
}
182-
assert pos == bits.length;
183149
return new LeafBucketCollectorBase(sub, null) {
184150
@Override
185151
public void collect(int doc, long bucket) throws IOException {
152+
// Check each of the provided filters
186153
for (int i = 0; i < bits.length; i++) {
187154
if (bits[i].get(doc)) {
188155
collectBucket(sub, doc, bucketOrd(bucket, i));
189156
}
190157
}
158+
// Check all the possible intersections of the provided filters
159+
int pos = filters.length;
160+
for (int i = 0; i < filters.length; i++) {
161+
if (bits[i].get(doc)) {
162+
for (int j = i + 1; j < filters.length; j++) {
163+
if (bits[j].get(doc)) {
164+
collectBucket(sub, doc, bucketOrd(bucket, pos));
165+
}
166+
pos++;
167+
}
168+
} else {
169+
// Skip checks on all the other filters given one half of the pairing failed
170+
pos += (filters.length - (i + 1));
171+
}
172+
}
173+
assert pos == bits.length + totalNumIntersections;
191174
}
192175
};
193176
}

0 commit comments

Comments
 (0)