Skip to content

Commit

Permalink
Filter post unwind (#79)
Browse files Browse the repository at this point in the history
  • Loading branch information
Rishabh Singh authored Apr 13, 2022
1 parent a6c55e1 commit ef9124f
Show file tree
Hide file tree
Showing 6 changed files with 297 additions and 13 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -536,6 +536,53 @@ public void testUnnestAndAggregate_preserveEmptyFalse() throws IOException {
assertDocsEqual(iterator, "mongo/unwind_not_preserving_empty_array_response.json");
}

@Test
public void testUnnest() throws IOException {
org.hypertrace.core.documentstore.query.Query query =
org.hypertrace.core.documentstore.query.Query.builder()
.addSelection(IdentifierExpression.of("item"))
.addSelection(IdentifierExpression.of("sales.city"))
.addSelection(IdentifierExpression.of("sales.medium"))
.addFromClause(UnnestExpression.of(IdentifierExpression.of("sales"), true))
.addFromClause(UnnestExpression.of(IdentifierExpression.of("sales.medium"), true))
.addSort(IdentifierExpression.of("item"), DESC)
.addSort(IdentifierExpression.of("sales.city"), DESC)
.addSort(IdentifierExpression.of("sales.medium.volume"), DESC)
.addSort(IdentifierExpression.of("sales.medium.type"), DESC)
.build();

Iterator<Document> iterator = collection.aggregate(query);
assertDocsEqual(iterator, "mongo/unwind_response.json");
}

@Test
public void testFilterAndUnnest() throws IOException {
RelationalExpression relationalExpression =
RelationalExpression.of(
IdentifierExpression.of("sales.city"), EQ, ConstantExpression.of("delhi"));

org.hypertrace.core.documentstore.query.Query query =
org.hypertrace.core.documentstore.query.Query.builder()
.addSelection(IdentifierExpression.of("item"))
.addSelection(IdentifierExpression.of("sales.city"))
.addSelection(IdentifierExpression.of("sales.medium"))
.addFromClause(
UnnestExpression.builder()
.identifierExpression(IdentifierExpression.of("sales"))
.preserveNullAndEmptyArrays(true)
.filterTypeExpression(relationalExpression)
.build())
.addFromClause(UnnestExpression.of(IdentifierExpression.of("sales.medium"), true))
.addSort(IdentifierExpression.of("item"), DESC)
.addSort(IdentifierExpression.of("sales.city"), DESC)
.addSort(IdentifierExpression.of("sales.medium.volume"), DESC)
.addSort(IdentifierExpression.of("sales.medium.type"), DESC)
.build();

Iterator<Document> iterator = collection.aggregate(query);
assertDocsEqual(iterator, "mongo/unwind_filter_response.json");
}

private static void assertDocsEqual(Iterator<Document> documents, String filePath)
throws IOException {
String fileContent = readFileFromResource(filePath).orElseThrow();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
[
{
"item": "Soap",
"sales": {
"city": "delhi",
"medium": {
"type": "online",
"volume": 1000
}
}
},
{
"item": "Soap",
"sales": {
"city": "delhi",
"medium": {
"type": "distributionChannel",
"volume": 1000
}
}
},
{
"item": "Soap",
"sales": {
"city": "delhi",
"medium": {
"type": "retail",
"volume": 500
}
}
},
{
"item": "Shampoo",
"sales": {
"city": "delhi",
"medium": {
"type": "distributionChannel",
"volume": 3000
}
}
},
{
"item": "Shampoo",
"sales": {
"city": "delhi",
"medium": {
"type": "online",
"volume": 1000
}
}
},
{
"item": "Shampoo",
"sales": {
"city": "delhi",
"medium": {
"type": "retail",
"volume": 500
}
}
},
{
"item": "Mirror",
"sales": {
"city": "delhi"
}
}
]
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
[
{
"item": "Soap",
"sales": {
"city": "pune",
"medium": {
"type": "online",
"volume": 2000
}
}
},
{
"item": "Soap",
"sales": {
"city": "pune",
"medium": {
"type": "distributionChannel",
"volume": 300
}
}
},
{
"item": "Soap",
"sales": {
"city": "delhi",
"medium": {
"type": "online",
"volume": 1000
}
}
},
{
"item": "Soap",
"sales": {
"city": "delhi",
"medium": {
"type": "distributionChannel",
"volume": 1000
}
}
},
{
"item": "Soap",
"sales": {
"city": "delhi",
"medium": {
"type": "retail",
"volume": 500
}
}
},
{
"item": "Soap"
},
{
"item": "Soap"
},
{
"item": "Shampoo",
"sales": {
"city": "mumbai",
"medium": {
"type": "online",
"volume": 5000
}
}
},
{
"item": "Shampoo",
"sales": {
"city": "mumbai",
"medium": {
"type": "distributionChannel",
"volume": 700
}
}
},
{
"item": "Shampoo",
"sales": {
"city": "mumbai",
"medium": {
"type": "retail",
"volume": 500
}
}
},
{
"item": "Shampoo",
"sales": {
"city": "delhi",
"medium": {
"type": "distributionChannel",
"volume": 3000
}
}
},
{
"item": "Shampoo",
"sales": {
"city": "delhi",
"medium": {
"type": "online",
"volume": 1000
}
}
},
{
"item": "Shampoo",
"sales": {
"city": "delhi",
"medium": {
"type": "retail",
"volume": 500
}
}
},
{
"item": "Shampoo"
},
{
"item": "Mirror",
"sales": {
"city": "delhi"
}
},
{
"item": "Comb"
},
{
"item": "Comb"
}
]
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@
import com.google.common.base.Preconditions;
import lombok.AccessLevel;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Value;
import org.hypertrace.core.documentstore.expression.type.FilterTypeExpression;
import org.hypertrace.core.documentstore.expression.type.FromTypeExpression;
import org.hypertrace.core.documentstore.parser.FromTypeExpressionVisitor;

Expand All @@ -14,17 +16,26 @@
* UnnestExpression.of(IdentifierExpression.of("array_col")) </code>
*/
@Value
@Builder
@AllArgsConstructor(access = AccessLevel.PRIVATE)
public class UnnestExpression implements FromTypeExpression {

IdentifierExpression identifierExpression;

boolean preserveNullAndEmptyArrays;
FilterTypeExpression filterTypeExpression;

public static UnnestExpression of(
final IdentifierExpression identifierExpression, boolean preserveNullAndEmptyArrays) {
Preconditions.checkArgument(identifierExpression != null, "expression is null");
return new UnnestExpression(identifierExpression, preserveNullAndEmptyArrays);
return new UnnestExpression(identifierExpression, preserveNullAndEmptyArrays, null);
}

public static class UnnestExpressionBuilder {
public UnnestExpression build() {
Preconditions.checkArgument(identifierExpression != null, "expression is null");
return new UnnestExpression(
identifierExpression, preserveNullAndEmptyArrays, filterTypeExpression);
}
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,15 @@ public Map<String, Object> visit(final RelationalExpression expression) {
public static BasicDBObject getFilterClause(
final Query query, final Function<Query, Optional<FilterTypeExpression>> filterProvider) {
BasicDBObject filters = getFilter(query, filterProvider);
return convertFilterToClause(filters);
}

public static BasicDBObject getFilterClause(FilterTypeExpression filterTypeExpression) {
BasicDBObject filters = getFilter(filterTypeExpression);
return convertFilterToClause(filters);
}

private static BasicDBObject convertFilterToClause(BasicDBObject filters) {
return filters.isEmpty() ? new BasicDBObject() : new BasicDBObject(FILTER_CLAUSE, filters);
}

Expand All @@ -40,8 +49,12 @@ public static BasicDBObject getFilter(
return new BasicDBObject();
}

return getFilter(filterOptional.get());
}

private static BasicDBObject getFilter(FilterTypeExpression filterTypeExpression) {
FilterTypeExpressionVisitor parser = new MongoFilterTypeExpressionParser();
Map<String, Object> filter = filterOptional.get().accept(parser);
Map<String, Object> filter = filterTypeExpression.accept(parser);
return new BasicDBObject(filter);
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package org.hypertrace.core.documentstore.mongo.parser;

import com.mongodb.BasicDBObject;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
Expand All @@ -19,25 +20,36 @@ public class MongoFromTypeExpressionParser implements FromTypeExpressionVisitor

@SuppressWarnings("unchecked")
@Override
public BasicDBObject visit(UnnestExpression unnestExpression) {
public List<BasicDBObject> visit(UnnestExpression unnestExpression) {
String parsedIdentifierExpression =
mongoIdentifierPrefixingParser.visit(unnestExpression.getIdentifierExpression());
return new BasicDBObject(
UNWIND_OPERATOR,
Map.of(
PATH_KEY,
parsedIdentifierExpression,
PRESERVE_NULL_AND_EMPTY_ARRAYS,
unnestExpression.isPreserveNullAndEmptyArrays()));
List<BasicDBObject> objects = new ArrayList<>();
objects.add(
new BasicDBObject(
UNWIND_OPERATOR,
Map.of(
PATH_KEY,
parsedIdentifierExpression,
PRESERVE_NULL_AND_EMPTY_ARRAYS,
unnestExpression.isPreserveNullAndEmptyArrays())));

if (null != unnestExpression.getFilterTypeExpression()) {
objects.add(
MongoFilterTypeExpressionParser.getFilterClause(
unnestExpression.getFilterTypeExpression()));
}

return objects;
}

public static List<BasicDBObject> getFromClauses(final Query query) {
MongoFromTypeExpressionParser mongoFromTypeExpressionParser =
new MongoFromTypeExpressionParser();
return query.getFromTypeExpressions().stream()
.map(
.flatMap(
fromTypeExpression ->
(BasicDBObject) fromTypeExpression.accept(mongoFromTypeExpressionParser))
((List<BasicDBObject>) fromTypeExpression.accept(mongoFromTypeExpressionParser))
.stream())
.collect(Collectors.toList());
}
}

0 comments on commit ef9124f

Please sign in to comment.