Skip to content

Commit ef9124f

Browse files
author
Rishabh Singh
authored
Filter post unwind (#79)
1 parent a6c55e1 commit ef9124f

File tree

6 files changed

+297
-13
lines changed

6 files changed

+297
-13
lines changed

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

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -536,6 +536,53 @@ public void testUnnestAndAggregate_preserveEmptyFalse() throws IOException {
536536
assertDocsEqual(iterator, "mongo/unwind_not_preserving_empty_array_response.json");
537537
}
538538

539+
@Test
540+
public void testUnnest() throws IOException {
541+
org.hypertrace.core.documentstore.query.Query query =
542+
org.hypertrace.core.documentstore.query.Query.builder()
543+
.addSelection(IdentifierExpression.of("item"))
544+
.addSelection(IdentifierExpression.of("sales.city"))
545+
.addSelection(IdentifierExpression.of("sales.medium"))
546+
.addFromClause(UnnestExpression.of(IdentifierExpression.of("sales"), true))
547+
.addFromClause(UnnestExpression.of(IdentifierExpression.of("sales.medium"), true))
548+
.addSort(IdentifierExpression.of("item"), DESC)
549+
.addSort(IdentifierExpression.of("sales.city"), DESC)
550+
.addSort(IdentifierExpression.of("sales.medium.volume"), DESC)
551+
.addSort(IdentifierExpression.of("sales.medium.type"), DESC)
552+
.build();
553+
554+
Iterator<Document> iterator = collection.aggregate(query);
555+
assertDocsEqual(iterator, "mongo/unwind_response.json");
556+
}
557+
558+
@Test
559+
public void testFilterAndUnnest() throws IOException {
560+
RelationalExpression relationalExpression =
561+
RelationalExpression.of(
562+
IdentifierExpression.of("sales.city"), EQ, ConstantExpression.of("delhi"));
563+
564+
org.hypertrace.core.documentstore.query.Query query =
565+
org.hypertrace.core.documentstore.query.Query.builder()
566+
.addSelection(IdentifierExpression.of("item"))
567+
.addSelection(IdentifierExpression.of("sales.city"))
568+
.addSelection(IdentifierExpression.of("sales.medium"))
569+
.addFromClause(
570+
UnnestExpression.builder()
571+
.identifierExpression(IdentifierExpression.of("sales"))
572+
.preserveNullAndEmptyArrays(true)
573+
.filterTypeExpression(relationalExpression)
574+
.build())
575+
.addFromClause(UnnestExpression.of(IdentifierExpression.of("sales.medium"), true))
576+
.addSort(IdentifierExpression.of("item"), DESC)
577+
.addSort(IdentifierExpression.of("sales.city"), DESC)
578+
.addSort(IdentifierExpression.of("sales.medium.volume"), DESC)
579+
.addSort(IdentifierExpression.of("sales.medium.type"), DESC)
580+
.build();
581+
582+
Iterator<Document> iterator = collection.aggregate(query);
583+
assertDocsEqual(iterator, "mongo/unwind_filter_response.json");
584+
}
585+
539586
private static void assertDocsEqual(Iterator<Document> documents, String filePath)
540587
throws IOException {
541588
String fileContent = readFileFromResource(filePath).orElseThrow();
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
[
2+
{
3+
"item": "Soap",
4+
"sales": {
5+
"city": "delhi",
6+
"medium": {
7+
"type": "online",
8+
"volume": 1000
9+
}
10+
}
11+
},
12+
{
13+
"item": "Soap",
14+
"sales": {
15+
"city": "delhi",
16+
"medium": {
17+
"type": "distributionChannel",
18+
"volume": 1000
19+
}
20+
}
21+
},
22+
{
23+
"item": "Soap",
24+
"sales": {
25+
"city": "delhi",
26+
"medium": {
27+
"type": "retail",
28+
"volume": 500
29+
}
30+
}
31+
},
32+
{
33+
"item": "Shampoo",
34+
"sales": {
35+
"city": "delhi",
36+
"medium": {
37+
"type": "distributionChannel",
38+
"volume": 3000
39+
}
40+
}
41+
},
42+
{
43+
"item": "Shampoo",
44+
"sales": {
45+
"city": "delhi",
46+
"medium": {
47+
"type": "online",
48+
"volume": 1000
49+
}
50+
}
51+
},
52+
{
53+
"item": "Shampoo",
54+
"sales": {
55+
"city": "delhi",
56+
"medium": {
57+
"type": "retail",
58+
"volume": 500
59+
}
60+
}
61+
},
62+
{
63+
"item": "Mirror",
64+
"sales": {
65+
"city": "delhi"
66+
}
67+
}
68+
]
Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
[
2+
{
3+
"item": "Soap",
4+
"sales": {
5+
"city": "pune",
6+
"medium": {
7+
"type": "online",
8+
"volume": 2000
9+
}
10+
}
11+
},
12+
{
13+
"item": "Soap",
14+
"sales": {
15+
"city": "pune",
16+
"medium": {
17+
"type": "distributionChannel",
18+
"volume": 300
19+
}
20+
}
21+
},
22+
{
23+
"item": "Soap",
24+
"sales": {
25+
"city": "delhi",
26+
"medium": {
27+
"type": "online",
28+
"volume": 1000
29+
}
30+
}
31+
},
32+
{
33+
"item": "Soap",
34+
"sales": {
35+
"city": "delhi",
36+
"medium": {
37+
"type": "distributionChannel",
38+
"volume": 1000
39+
}
40+
}
41+
},
42+
{
43+
"item": "Soap",
44+
"sales": {
45+
"city": "delhi",
46+
"medium": {
47+
"type": "retail",
48+
"volume": 500
49+
}
50+
}
51+
},
52+
{
53+
"item": "Soap"
54+
},
55+
{
56+
"item": "Soap"
57+
},
58+
{
59+
"item": "Shampoo",
60+
"sales": {
61+
"city": "mumbai",
62+
"medium": {
63+
"type": "online",
64+
"volume": 5000
65+
}
66+
}
67+
},
68+
{
69+
"item": "Shampoo",
70+
"sales": {
71+
"city": "mumbai",
72+
"medium": {
73+
"type": "distributionChannel",
74+
"volume": 700
75+
}
76+
}
77+
},
78+
{
79+
"item": "Shampoo",
80+
"sales": {
81+
"city": "mumbai",
82+
"medium": {
83+
"type": "retail",
84+
"volume": 500
85+
}
86+
}
87+
},
88+
{
89+
"item": "Shampoo",
90+
"sales": {
91+
"city": "delhi",
92+
"medium": {
93+
"type": "distributionChannel",
94+
"volume": 3000
95+
}
96+
}
97+
},
98+
{
99+
"item": "Shampoo",
100+
"sales": {
101+
"city": "delhi",
102+
"medium": {
103+
"type": "online",
104+
"volume": 1000
105+
}
106+
}
107+
},
108+
{
109+
"item": "Shampoo",
110+
"sales": {
111+
"city": "delhi",
112+
"medium": {
113+
"type": "retail",
114+
"volume": 500
115+
}
116+
}
117+
},
118+
{
119+
"item": "Shampoo"
120+
},
121+
{
122+
"item": "Mirror",
123+
"sales": {
124+
"city": "delhi"
125+
}
126+
},
127+
{
128+
"item": "Comb"
129+
},
130+
{
131+
"item": "Comb"
132+
}
133+
]

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

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,9 @@
33
import com.google.common.base.Preconditions;
44
import lombok.AccessLevel;
55
import lombok.AllArgsConstructor;
6+
import lombok.Builder;
67
import lombok.Value;
8+
import org.hypertrace.core.documentstore.expression.type.FilterTypeExpression;
79
import org.hypertrace.core.documentstore.expression.type.FromTypeExpression;
810
import org.hypertrace.core.documentstore.parser.FromTypeExpressionVisitor;
911

@@ -14,17 +16,26 @@
1416
* UnnestExpression.of(IdentifierExpression.of("array_col")) </code>
1517
*/
1618
@Value
19+
@Builder
1720
@AllArgsConstructor(access = AccessLevel.PRIVATE)
1821
public class UnnestExpression implements FromTypeExpression {
1922

2023
IdentifierExpression identifierExpression;
21-
2224
boolean preserveNullAndEmptyArrays;
25+
FilterTypeExpression filterTypeExpression;
2326

2427
public static UnnestExpression of(
2528
final IdentifierExpression identifierExpression, boolean preserveNullAndEmptyArrays) {
2629
Preconditions.checkArgument(identifierExpression != null, "expression is null");
27-
return new UnnestExpression(identifierExpression, preserveNullAndEmptyArrays);
30+
return new UnnestExpression(identifierExpression, preserveNullAndEmptyArrays, null);
31+
}
32+
33+
public static class UnnestExpressionBuilder {
34+
public UnnestExpression build() {
35+
Preconditions.checkArgument(identifierExpression != null, "expression is null");
36+
return new UnnestExpression(
37+
identifierExpression, preserveNullAndEmptyArrays, filterTypeExpression);
38+
}
2839
}
2940

3041
@Override

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

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,15 @@ public Map<String, Object> visit(final RelationalExpression expression) {
2929
public static BasicDBObject getFilterClause(
3030
final Query query, final Function<Query, Optional<FilterTypeExpression>> filterProvider) {
3131
BasicDBObject filters = getFilter(query, filterProvider);
32+
return convertFilterToClause(filters);
33+
}
34+
35+
public static BasicDBObject getFilterClause(FilterTypeExpression filterTypeExpression) {
36+
BasicDBObject filters = getFilter(filterTypeExpression);
37+
return convertFilterToClause(filters);
38+
}
39+
40+
private static BasicDBObject convertFilterToClause(BasicDBObject filters) {
3241
return filters.isEmpty() ? new BasicDBObject() : new BasicDBObject(FILTER_CLAUSE, filters);
3342
}
3443

@@ -40,8 +49,12 @@ public static BasicDBObject getFilter(
4049
return new BasicDBObject();
4150
}
4251

52+
return getFilter(filterOptional.get());
53+
}
54+
55+
private static BasicDBObject getFilter(FilterTypeExpression filterTypeExpression) {
4356
FilterTypeExpressionVisitor parser = new MongoFilterTypeExpressionParser();
44-
Map<String, Object> filter = filterOptional.get().accept(parser);
57+
Map<String, Object> filter = filterTypeExpression.accept(parser);
4558
return new BasicDBObject(filter);
4659
}
4760
}

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

Lines changed: 22 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package org.hypertrace.core.documentstore.mongo.parser;
22

33
import com.mongodb.BasicDBObject;
4+
import java.util.ArrayList;
45
import java.util.List;
56
import java.util.Map;
67
import java.util.stream.Collectors;
@@ -19,25 +20,36 @@ public class MongoFromTypeExpressionParser implements FromTypeExpressionVisitor
1920

2021
@SuppressWarnings("unchecked")
2122
@Override
22-
public BasicDBObject visit(UnnestExpression unnestExpression) {
23+
public List<BasicDBObject> visit(UnnestExpression unnestExpression) {
2324
String parsedIdentifierExpression =
2425
mongoIdentifierPrefixingParser.visit(unnestExpression.getIdentifierExpression());
25-
return new BasicDBObject(
26-
UNWIND_OPERATOR,
27-
Map.of(
28-
PATH_KEY,
29-
parsedIdentifierExpression,
30-
PRESERVE_NULL_AND_EMPTY_ARRAYS,
31-
unnestExpression.isPreserveNullAndEmptyArrays()));
26+
List<BasicDBObject> objects = new ArrayList<>();
27+
objects.add(
28+
new BasicDBObject(
29+
UNWIND_OPERATOR,
30+
Map.of(
31+
PATH_KEY,
32+
parsedIdentifierExpression,
33+
PRESERVE_NULL_AND_EMPTY_ARRAYS,
34+
unnestExpression.isPreserveNullAndEmptyArrays())));
35+
36+
if (null != unnestExpression.getFilterTypeExpression()) {
37+
objects.add(
38+
MongoFilterTypeExpressionParser.getFilterClause(
39+
unnestExpression.getFilterTypeExpression()));
40+
}
41+
42+
return objects;
3243
}
3344

3445
public static List<BasicDBObject> getFromClauses(final Query query) {
3546
MongoFromTypeExpressionParser mongoFromTypeExpressionParser =
3647
new MongoFromTypeExpressionParser();
3748
return query.getFromTypeExpressions().stream()
38-
.map(
49+
.flatMap(
3950
fromTypeExpression ->
40-
(BasicDBObject) fromTypeExpression.accept(mongoFromTypeExpressionParser))
51+
((List<BasicDBObject>) fromTypeExpression.accept(mongoFromTypeExpressionParser))
52+
.stream())
4153
.collect(Collectors.toList());
4254
}
4355
}

0 commit comments

Comments
 (0)