Skip to content

Commit 04a5518

Browse files
Fix count aggregation on optional fields (#92)
* Fix count aggregation on optional fields
1 parent 111185c commit 04a5518

File tree

6 files changed

+61
-25
lines changed

6 files changed

+61
-25
lines changed

document-store/src/integrationTest/java/org/hypertrace/core/documentstore/mongo/MongoQueryExecutorIntegrationTest.java

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -308,6 +308,20 @@ public void testAggregateSimple() throws IOException {
308308
assertSizeEqual(query, "mongo/count_response.json");
309309
}
310310

311+
@Test
312+
public void testOptionalFieldCount() throws IOException {
313+
Query query =
314+
Query.builder()
315+
.addSelection(
316+
AggregateExpression.of(COUNT, IdentifierExpression.of("props.seller.name")),
317+
"count")
318+
.build();
319+
320+
Iterator<Document> resultDocs = collection.aggregate(query);
321+
assertDocsEqual(resultDocs, "mongo/optional_field_count_response.json");
322+
assertSizeEqual(query, "mongo/optional_field_count_response.json");
323+
}
324+
311325
@Test
312326
public void testAggregateWithDuplicateSelections() throws IOException {
313327
Query query =
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
[
2+
{
3+
"count": 4
4+
}
5+
]

document-store/src/main/java/org/hypertrace/core/documentstore/mongo/parser/MongoAggregateExpressionParser.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
import static java.util.Collections.unmodifiableMap;
44
import static org.hypertrace.core.documentstore.expression.operators.AggregationOperator.AVG;
5+
import static org.hypertrace.core.documentstore.expression.operators.AggregationOperator.COUNT;
56
import static org.hypertrace.core.documentstore.expression.operators.AggregationOperator.DISTINCT;
67
import static org.hypertrace.core.documentstore.expression.operators.AggregationOperator.MAX;
78
import static org.hypertrace.core.documentstore.expression.operators.AggregationOperator.MIN;
@@ -26,6 +27,7 @@ final class MongoAggregateExpressionParser extends MongoSelectTypeExpressionPars
2627
put(SUM, "$sum");
2728
put(MIN, "$min");
2829
put(MAX, "$max");
30+
put(COUNT, "$push");
2931
}
3032
});
3133

document-store/src/main/java/org/hypertrace/core/documentstore/mongo/query/transformer/MongoSelectionsAddingTransformation.java

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package org.hypertrace.core.documentstore.mongo.query.transformer;
22

3+
import static org.hypertrace.core.documentstore.expression.operators.AggregationOperator.COUNT;
34
import static org.hypertrace.core.documentstore.expression.operators.AggregationOperator.DISTINCT_COUNT;
45
import static org.hypertrace.core.documentstore.expression.operators.FunctionOperator.LENGTH;
56
import static org.hypertrace.core.documentstore.mongo.MongoUtils.encodeKey;
@@ -82,9 +83,10 @@ public Optional<SelectionSpec> visit(final AggregateExpression expression) {
8283
final String encodedAlias = encodeKey(alias);
8384
final SelectTypeExpression pairingExpression;
8485

85-
if (expression.getAggregator() == DISTINCT_COUNT) {
86-
// Since MongoDB doesn't support $distinctCount in aggregations, we convert this to
87-
// $addToSet function. So, we need to project $size(set) instead of just the alias
86+
if (expression.getAggregator() == DISTINCT_COUNT || expression.getAggregator() == COUNT) {
87+
// Since MongoDB doesn't support $distinctCount and $count(optional_field) in aggregations,
88+
// we convert them to $addToSet and $push functions respectively.
89+
// So, we need to project $size(set) or $size(list) instead of just the alias in these cases.
8890
pairingExpression =
8991
FunctionExpression.builder()
9092
.operator(LENGTH)

document-store/src/main/java/org/hypertrace/core/documentstore/mongo/query/transformer/MongoSelectionsUpdatingTransformation.java

Lines changed: 5 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,18 @@
11
package org.hypertrace.core.documentstore.mongo.query.transformer;
22

33
import static java.util.Collections.unmodifiableMap;
4-
import static org.hypertrace.core.documentstore.expression.operators.AggregationOperator.COUNT;
54
import static org.hypertrace.core.documentstore.expression.operators.AggregationOperator.DISTINCT;
65
import static org.hypertrace.core.documentstore.expression.operators.AggregationOperator.DISTINCT_COUNT;
7-
import static org.hypertrace.core.documentstore.expression.operators.AggregationOperator.SUM;
86
import static org.hypertrace.core.documentstore.mongo.MongoCollection.ID_KEY;
97
import static org.hypertrace.core.documentstore.mongo.MongoUtils.FIELD_SEPARATOR;
108
import static org.hypertrace.core.documentstore.mongo.MongoUtils.encodeKey;
119

1210
import java.util.EnumMap;
11+
import java.util.HashSet;
1312
import java.util.List;
1413
import java.util.Map;
1514
import java.util.Optional;
15+
import java.util.Set;
1616
import java.util.function.Function;
1717
import org.hypertrace.core.documentstore.expression.impl.AggregateExpression;
1818
import org.hypertrace.core.documentstore.expression.impl.ConstantExpression;
@@ -72,9 +72,6 @@
7272
* supported
7373
*/
7474
final class MongoSelectionsUpdatingTransformation implements SelectTypeExpressionVisitor {
75-
private static final Function<AggregateExpression, AggregateExpression> COUNT_HANDLER =
76-
expression -> AggregateExpression.of(SUM, ConstantExpression.of(1));
77-
7875
private static final Function<AggregateExpression, AggregateExpression> DISTINCT_COUNT_HANDLER =
7976
expression -> AggregateExpression.of(DISTINCT, expression.getExpression());
8077

@@ -84,16 +81,15 @@ final class MongoSelectionsUpdatingTransformation implements SelectTypeExpressio
8481
new EnumMap<>(AggregationOperator.class) {
8582
{
8683
put(DISTINCT_COUNT, DISTINCT_COUNT_HANDLER);
87-
put(COUNT, COUNT_HANDLER);
8884
}
8985
});
9086

91-
private final List<GroupTypeExpression> groupTypeExpressions;
87+
private final Set<GroupTypeExpression> groupTypeExpressions;
9288
private final SelectionSpec source;
9389

9490
MongoSelectionsUpdatingTransformation(
9591
List<GroupTypeExpression> groupTypeExpressions, SelectionSpec source) {
96-
this.groupTypeExpressions = groupTypeExpressions;
92+
this.groupTypeExpressions = new HashSet<>(groupTypeExpressions);
9793
this.source = source;
9894
}
9995

@@ -118,16 +114,7 @@ public SelectionSpec visit(final FunctionExpression expression) {
118114
@SuppressWarnings("unchecked")
119115
@Override
120116
public SelectionSpec visit(final IdentifierExpression expression) {
121-
GroupTypeExpression matchingGroup = null;
122-
123-
for (final GroupTypeExpression group : groupTypeExpressions) {
124-
if (expression.equals(group)) {
125-
matchingGroup = group;
126-
break;
127-
}
128-
}
129-
130-
if (matchingGroup == null) {
117+
if (!groupTypeExpressions.contains(expression)) {
131118
return source;
132119
}
133120

document-store/src/test/java/org/hypertrace/core/documentstore/mongo/MongoQueryExecutorTest.java

Lines changed: 30 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -311,11 +311,37 @@ public void testSimpleAggregate() {
311311
+ " { "
312312
+ " _id: null, "
313313
+ " total: {"
314-
+ " \"$sum\": 1"
314+
+ " \"$push\": 1"
315315
+ " }"
316316
+ " }"
317317
+ "}"),
318-
BasicDBObject.parse("{" + "\"$project\": {" + " \"total\": \"$total\"" + "}" + "}"));
318+
BasicDBObject.parse(
319+
"{" + "\"$project\": {" + " \"total\": {\"$size\": \"$total\"}" + "}" + "}"));
320+
321+
testAggregation(query, pipeline);
322+
}
323+
324+
@Test
325+
public void testFieldCount() {
326+
Query query =
327+
Query.builder()
328+
.addSelection(AggregateExpression.of(COUNT, IdentifierExpression.of("path")), "total")
329+
.build();
330+
331+
List<BasicDBObject> pipeline =
332+
List.of(
333+
BasicDBObject.parse(
334+
"{"
335+
+ "\"$group\": "
336+
+ " { "
337+
+ " _id: null, "
338+
+ " total: {"
339+
+ " \"$push\": \"$path\""
340+
+ " }"
341+
+ " }"
342+
+ "}"),
343+
BasicDBObject.parse(
344+
"{" + "\"$project\": {" + " \"total\": { \"$size\": \"$total\" }" + "}" + "}"));
319345

320346
testAggregation(query, pipeline);
321347
}
@@ -339,7 +365,7 @@ public void testAggregateWithProjections() {
339365
+ " { "
340366
+ " _id: null, "
341367
+ " total: {"
342-
+ " \"$sum\": 1"
368+
+ " \"$push\": 1"
343369
+ " }"
344370
+ " }"
345371
+ "}"),
@@ -348,7 +374,7 @@ public void testAggregateWithProjections() {
348374
+ "\"$project\": "
349375
+ " {"
350376
+ " name: 1,"
351-
+ " total: \"$total\""
377+
+ " total: {\"$size\": \"$total\"}"
352378
+ " }"
353379
+ "}"));
354380

0 commit comments

Comments
 (0)