|
35 | 35 | import java.io.IOException; |
36 | 36 | import java.util.ArrayList; |
37 | 37 | import java.util.Collections; |
| 38 | +import java.util.HashMap; |
38 | 39 | import java.util.LinkedList; |
39 | 40 | import java.util.List; |
40 | 41 | import java.util.Map; |
@@ -85,97 +86,93 @@ public InternalAggregation reduce(InternalAggregation aggregation, ReduceContext |
85 | 86 | (InternalMultiBucketAggregation<InternalMultiBucketAggregation, InternalMultiBucketAggregation.InternalBucket>) aggregation; |
86 | 87 | List<? extends InternalMultiBucketAggregation.InternalBucket> buckets = originalAgg.getBuckets(); |
87 | 88 | int bucketsCount = buckets.size(); |
88 | | - int offset = reduceContext.isFinalReduce() ? from : 0; |
89 | 89 | int currentSize = size == null ? bucketsCount : size; |
90 | 90 |
|
91 | | - if (offset >= bucketsCount) { |
| 91 | + if (from >= bucketsCount) { |
92 | 92 | return originalAgg.create(Collections.emptyList()); |
93 | 93 | } |
94 | 94 |
|
95 | | - int resultSize = Math.min(currentSize, bucketsCount - offset); |
96 | | - |
97 | 95 | // If no sorting needs to take place, we just truncate and return |
98 | 96 | if (sorts.size() == 0) { |
99 | | - return originalAgg.create(truncate(buckets, offset, currentSize)); |
| 97 | + return originalAgg.create(new ArrayList<>(buckets.subList(from, Math.min(from + currentSize, bucketsCount)))); |
100 | 98 | } |
101 | 99 |
|
102 | | - int queueSize = Math.min(offset + currentSize, bucketsCount); |
| 100 | + int queueSize = Math.min(from + currentSize, bucketsCount); |
103 | 101 | PriorityQueue<ComparableBucket> ordered = new TopNPriorityQueue(queueSize); |
104 | 102 | for (InternalMultiBucketAggregation.InternalBucket bucket : buckets) { |
105 | | - ordered.insertWithOverflow(new ComparableBucket(originalAgg, bucket)); |
| 103 | + ComparableBucket comparableBucket = new ComparableBucket(originalAgg, bucket); |
| 104 | + if (comparableBucket.skip() == false) { |
| 105 | + ordered.insertWithOverflow(new ComparableBucket(originalAgg, bucket)); |
| 106 | + } |
106 | 107 | } |
107 | 108 |
|
| 109 | + int resultSize = Math.max(ordered.size() - from, 0); |
| 110 | + |
108 | 111 | // Popping from the priority queue returns the least element. The elements we want to skip due to offset would pop last. |
109 | 112 | // Thus, we just have to pop as many elements as we expect in results and store them in reverse order. |
110 | 113 | LinkedList<InternalMultiBucketAggregation.InternalBucket> newBuckets = new LinkedList<>(); |
111 | | - for (int i = resultSize - 1; i >= 0; --i) { |
112 | | - ComparableBucket comparableBucket = ordered.pop(); |
113 | | - if (comparableBucket.skip == false) { |
114 | | - newBuckets.addFirst(comparableBucket.getInternalBucket()); |
115 | | - } |
| 114 | + for (int i = 0; i < resultSize; ++i) { |
| 115 | + newBuckets.addFirst(ordered.pop().internalBucket); |
116 | 116 | } |
117 | 117 | return originalAgg.create(newBuckets); |
118 | 118 | } |
119 | 119 |
|
120 | | - private static List<InternalMultiBucketAggregation.InternalBucket> truncate( |
121 | | - List<? extends InternalMultiBucketAggregation.InternalBucket> buckets, int offset, int size) { |
122 | | - |
123 | | - List<InternalMultiBucketAggregation.InternalBucket> truncated = new ArrayList<>(size); |
124 | | - for (int i = offset; i < offset + size; ++i) { |
125 | | - truncated.add(buckets.get(i)); |
126 | | - } |
127 | | - return truncated; |
128 | | - } |
129 | | - |
130 | 120 | private class ComparableBucket implements Comparable<ComparableBucket> { |
131 | 121 |
|
132 | 122 | private final MultiBucketsAggregation parentAgg; |
133 | 123 | private final InternalMultiBucketAggregation.InternalBucket internalBucket; |
134 | | - |
135 | | - /** |
136 | | - * Whether the bucket should be skipped due to the gap policy |
137 | | - */ |
138 | | - private boolean skip = false; |
| 124 | + private final Map<FieldSortBuilder, Comparable<Object>> sortValues; |
139 | 125 |
|
140 | 126 | private ComparableBucket(MultiBucketsAggregation parentAgg, InternalMultiBucketAggregation.InternalBucket internalBucket) { |
141 | 127 | this.parentAgg = parentAgg; |
142 | 128 | this.internalBucket = internalBucket; |
| 129 | + this.sortValues = resolveAndCacheSortValues(); |
143 | 130 | } |
144 | 131 |
|
145 | | - private InternalMultiBucketAggregation.InternalBucket getInternalBucket() { |
146 | | - return internalBucket; |
147 | | - } |
148 | | - |
149 | | - @Override |
150 | | - public int compareTo(ComparableBucket that) { |
| 132 | + private final Map<FieldSortBuilder, Comparable<Object>> resolveAndCacheSortValues() { |
| 133 | + Map<FieldSortBuilder, Comparable<Object>> resolved = new HashMap<>(); |
151 | 134 | for (FieldSortBuilder sort : sorts) { |
152 | 135 | String sortField = sort.getFieldName(); |
153 | | - int compareResult = "_key".equals(sortField) ? compareKeys(this, that) : comparePathValues(sortField, this, that); |
154 | | - if (compareResult != 0) { |
155 | | - return sort.order() == SortOrder.DESC ? compareResult : -compareResult; |
| 136 | + if ("_key".equals(sortField)) { |
| 137 | + resolved.put(sort, (Comparable<Object>) internalBucket.getKey()); |
| 138 | + } else { |
| 139 | + Double bucketValue = BucketHelpers.resolveBucketValue(parentAgg, internalBucket, sortField, gapPolicy); |
| 140 | + if (GapPolicy.SKIP == gapPolicy && Double.isNaN(bucketValue)) { |
| 141 | + continue; |
| 142 | + } |
| 143 | + resolved.put(sort, (Comparable<Object>) (Object) bucketValue); |
156 | 144 | } |
157 | 145 | } |
158 | | - return 0; |
| 146 | + return resolved; |
159 | 147 | } |
160 | 148 |
|
161 | | - private int compareKeys(ComparableBucket b1, ComparableBucket b2) { |
162 | | - Comparable<Object> b1Key = (Comparable<Object>) b1.internalBucket.getKey(); |
163 | | - Comparable<Object> b2Key = (Comparable<Object>) b2.internalBucket.getKey(); |
164 | | - return b1Key.compareTo(b2Key); |
| 149 | + /** |
| 150 | + * Whether the bucket should be skipped due to the gap policy |
| 151 | + */ |
| 152 | + private boolean skip() { |
| 153 | + return sortValues.isEmpty(); |
165 | 154 | } |
166 | 155 |
|
167 | | - private int comparePathValues(String sortField, ComparableBucket b1, ComparableBucket b2) { |
168 | | - Double b1Value = BucketHelpers.resolveBucketValue(parentAgg, b1.internalBucket, sortField, gapPolicy); |
169 | | - Double b2Value = BucketHelpers.resolveBucketValue(parentAgg, b2.internalBucket, sortField, gapPolicy); |
170 | | - if (GapPolicy.SKIP == gapPolicy) { |
171 | | - if (Double.isNaN(b1Value)) { |
172 | | - b1.skip = true; |
| 156 | + @Override |
| 157 | + public int compareTo(ComparableBucket that) { |
| 158 | + int compareResult = 0; |
| 159 | + for (FieldSortBuilder sort : sorts) { |
| 160 | + Comparable<Object> thisValue = this.sortValues.get(sort); |
| 161 | + Comparable<Object> thatValue = that.sortValues.get(sort); |
| 162 | + if (thisValue == null && thatValue == null) { |
| 163 | + continue; |
| 164 | + } else if (thisValue == null) { |
| 165 | + return -1; |
| 166 | + } else if (thatValue == null) { |
| 167 | + return 1; |
| 168 | + } else { |
| 169 | + compareResult = sort.order() == SortOrder.DESC ? thisValue.compareTo(thatValue) : -thisValue.compareTo(thatValue); |
173 | 170 | } |
174 | | - if (Double.isNaN(b2Value)) { |
175 | | - b2.skip = true; |
| 171 | + if (compareResult != 0) { |
| 172 | + break; |
176 | 173 | } |
177 | 174 | } |
178 | | - return b1Value.compareTo(b2Value); |
| 175 | + return compareResult; |
179 | 176 | } |
180 | 177 | } |
181 | 178 |
|
|
0 commit comments