Skip to content

Commit d0541f9

Browse files
author
Rishabh Singh
authored
Add config preserving empty array while unwinding (#75)
1 parent 774e1ae commit d0541f9

File tree

7 files changed

+70
-11
lines changed

7 files changed

+70
-11
lines changed

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

Lines changed: 31 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -498,15 +498,44 @@ public void testUnnestAndAggregate() throws IOException {
498498
.addSelection(
499499
AggregateExpression.of(SUM, IdentifierExpression.of("sales.medium.volume")),
500500
"totalSales")
501-
.addFromClause(UnnestExpression.of(IdentifierExpression.of("sales")))
502-
.addFromClause(UnnestExpression.of(IdentifierExpression.of("sales.medium")))
501+
// we don't want to consider entries where sales data is missing
502+
.addFromClause(UnnestExpression.of(IdentifierExpression.of("sales"), false))
503+
.addFromClause(UnnestExpression.of(IdentifierExpression.of("sales.medium"), false))
503504
.addSort(IdentifierExpression.of("totalSales"), DESC)
504505
.build();
505506

506507
Iterator<Document> iterator = collection.aggregate(query);
507508
assertDocsEqual(iterator, "mongo/aggregate_on_nested_array_reponse.json");
508509
}
509510

511+
@Test
512+
public void testUnnestAndAggregate_preserveEmptyTrue() throws IOException {
513+
// include all documents in the result irrespective of `sales` field
514+
org.hypertrace.core.documentstore.query.Query query =
515+
org.hypertrace.core.documentstore.query.Query.builder()
516+
.addSelection(AggregateExpression.of(COUNT, IdentifierExpression.of("item")), "count")
517+
.addFromClause(UnnestExpression.of(IdentifierExpression.of("sales"), true))
518+
.addFromClause(UnnestExpression.of(IdentifierExpression.of("sales.medium"), true))
519+
.build();
520+
521+
Iterator<Document> iterator = collection.aggregate(query);
522+
assertDocsEqual(iterator, "mongo/unwind_preserving_empty_array_response.json");
523+
}
524+
525+
@Test
526+
public void testUnnestAndAggregate_preserveEmptyFalse() throws IOException {
527+
// consider only those documents where sales field is missing
528+
org.hypertrace.core.documentstore.query.Query query =
529+
org.hypertrace.core.documentstore.query.Query.builder()
530+
.addSelection(AggregateExpression.of(COUNT, IdentifierExpression.of("item")), "count")
531+
.addFromClause(UnnestExpression.of(IdentifierExpression.of("sales"), false))
532+
.addFromClause(UnnestExpression.of(IdentifierExpression.of("sales.medium"), true))
533+
.build();
534+
535+
Iterator<Document> iterator = collection.aggregate(query);
536+
assertDocsEqual(iterator, "mongo/unwind_not_preserving_empty_array_response.json");
537+
}
538+
510539
private static void assertDocsEqual(Iterator<Document> documents, String filePath)
511540
throws IOException {
512541
String fileContent = readFileFromResource(filePath).orElseThrow();

document-store/src/integrationTest/resources/mongo/collection_data.json

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,13 @@
5454
"item": "Mirror",
5555
"price": 20,
5656
"quantity": 1,
57-
"date": "2014-03-01T09:00:00Z"
57+
"date": "2014-03-01T09:00:00Z",
58+
"sales": [
59+
{
60+
"city": "delhi",
61+
"medium": []
62+
}
63+
]
5864
},
5965
{
6066
"_id": 3,
@@ -115,7 +121,8 @@
115121
"item": "Shampoo",
116122
"price": 5,
117123
"quantity": 20,
118-
"date": "2014-04-04T11:21:39.736Z"
124+
"date": "2014-04-04T11:21:39.736Z",
125+
"sales": []
119126
},
120127
{
121128
"_id": 5,
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
[
2+
{
3+
"count": 12
4+
}
5+
]
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
[
2+
{
3+
"count": 17
4+
}
5+
]

document-store/src/main/java/org/hypertrace/core/documentstore/expression/impl/UnnestExpression.java

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,12 @@ public class UnnestExpression implements FromTypeExpression {
1919

2020
IdentifierExpression identifierExpression;
2121

22-
public static UnnestExpression of(final IdentifierExpression identifierExpression) {
22+
boolean preserveNullAndEmptyArrays;
23+
24+
public static UnnestExpression of(
25+
final IdentifierExpression identifierExpression, boolean preserveNullAndEmptyArrays) {
2326
Preconditions.checkArgument(identifierExpression != null, "expression is null");
24-
return new UnnestExpression(identifierExpression);
27+
return new UnnestExpression(identifierExpression, preserveNullAndEmptyArrays);
2528
}
2629

2730
@Override

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

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
public class MongoFromTypeExpressionParser implements FromTypeExpressionVisitor {
1212

1313
private static final String PATH_KEY = "path";
14+
public static final String PRESERVE_NULL_AND_EMPTY_ARRAYS = "preserveNullAndEmptyArrays";
1415
private static final String UNWIND_OPERATOR = "$unwind";
1516

1617
private static final MongoIdentifierPrefixingParser mongoIdentifierPrefixingParser =
@@ -21,7 +22,13 @@ public class MongoFromTypeExpressionParser implements FromTypeExpressionVisitor
2122
public BasicDBObject visit(UnnestExpression unnestExpression) {
2223
String parsedIdentifierExpression =
2324
mongoIdentifierPrefixingParser.visit(unnestExpression.getIdentifierExpression());
24-
return new BasicDBObject(UNWIND_OPERATOR, Map.of(PATH_KEY, parsedIdentifierExpression));
25+
return new BasicDBObject(
26+
UNWIND_OPERATOR,
27+
Map.of(
28+
PATH_KEY,
29+
parsedIdentifierExpression,
30+
PRESERVE_NULL_AND_EMPTY_ARRAYS,
31+
unnestExpression.isPreserveNullAndEmptyArrays()));
2532
}
2633

2734
public static List<BasicDBObject> getFromClauses(final Query query) {

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

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -603,15 +603,18 @@ public void testUnwindAndGroup() {
603603
RelationalExpression.of(
604604
IdentifierExpression.of("class"), LTE, ConstantExpression.of(10)))
605605
.addAggregation(IdentifierExpression.of("class.students.courses"))
606-
.addFromClause(UnnestExpression.of(IdentifierExpression.of("class.students")))
607-
.addFromClause(UnnestExpression.of(IdentifierExpression.of("class.students.courses")))
606+
.addFromClause(UnnestExpression.of(IdentifierExpression.of("class.students"), true))
607+
.addFromClause(
608+
UnnestExpression.of(IdentifierExpression.of("class.students.courses"), true))
608609
.build();
609610

610611
List<BasicDBObject> pipeline =
611612
List.of(
612613
BasicDBObject.parse("{\"$match\": {\"class\": {\"$lte\": 10}}}"),
613-
BasicDBObject.parse("{\"$unwind\": {\"path\": \"$class.students\"}}"),
614-
BasicDBObject.parse("{\"$unwind\": {\"path\": \"$class.students.courses\"}}"),
614+
BasicDBObject.parse(
615+
"{\"$unwind\": {\"path\": \"$class.students\", \"preserveNullAndEmptyArrays\": true}}"),
616+
BasicDBObject.parse(
617+
"{\"$unwind\": {\"path\": \"$class.students.courses\", \"preserveNullAndEmptyArrays\": true}}"),
615618
BasicDBObject.parse(
616619
"{\"$group\": {\"_id\": {\"class\\\\u002estudents\\\\u002ecourses\": \"$class.students.courses\"}}}"));
617620

0 commit comments

Comments
 (0)