Skip to content

Commit

Permalink
fix : adding support for contains operator (#151)
Browse files Browse the repository at this point in the history
1. Added support for CONTAINS operator in Postgres
2. Added a new NOT_CONTAINS operator with implementation for both Postgres and MongoDB

---------

Co-authored-by: Suresh Prakash <[email protected]>
  • Loading branch information
kotharironak and suresh-prakash authored May 19, 2023
1 parent 0956b46 commit 2a8b182
Show file tree
Hide file tree
Showing 20 changed files with 411 additions and 5 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,15 @@
import static org.hypertrace.core.documentstore.expression.operators.FunctionOperator.MULTIPLY;
import static org.hypertrace.core.documentstore.expression.operators.LogicalOperator.AND;
import static org.hypertrace.core.documentstore.expression.operators.LogicalOperator.OR;
import static org.hypertrace.core.documentstore.expression.operators.RelationalOperator.CONTAINS;
import static org.hypertrace.core.documentstore.expression.operators.RelationalOperator.EQ;
import static org.hypertrace.core.documentstore.expression.operators.RelationalOperator.GT;
import static org.hypertrace.core.documentstore.expression.operators.RelationalOperator.GTE;
import static org.hypertrace.core.documentstore.expression.operators.RelationalOperator.IN;
import static org.hypertrace.core.documentstore.expression.operators.RelationalOperator.LT;
import static org.hypertrace.core.documentstore.expression.operators.RelationalOperator.LTE;
import static org.hypertrace.core.documentstore.expression.operators.RelationalOperator.NEQ;
import static org.hypertrace.core.documentstore.expression.operators.RelationalOperator.NOT_CONTAINS;
import static org.hypertrace.core.documentstore.expression.operators.RelationalOperator.NOT_IN;
import static org.hypertrace.core.documentstore.expression.operators.SortOrder.ASC;
import static org.hypertrace.core.documentstore.expression.operators.SortOrder.DESC;
Expand Down Expand Up @@ -1483,6 +1485,58 @@ public void testUnnestWithRegularFilterAndNullAndEmptyPreservedAtFirstLevel(Stri
dataStoreName, iterator, "query/unwind_preserve_with_regular_filter_first_level.json", 3);
}

@ParameterizedTest
@ArgumentsSource(AllProvider.class)
void testContainsAndUnnestFilters(String dataStoreName) throws IOException {
Collection collection = getCollection(dataStoreName);

org.hypertrace.core.documentstore.query.Query query =
org.hypertrace.core.documentstore.query.Query.builder()
.addSelection(IdentifierExpression.of("item"))
.addSelection(IdentifierExpression.of("sales.medium"))
.addFromClause(
UnnestExpression.builder()
.identifierExpression(IdentifierExpression.of("sales"))
.preserveNullAndEmptyArrays(false)
.filterTypeExpression(
RelationalExpression.of(
IdentifierExpression.of("sales.medium"),
CONTAINS,
ConstantExpression.of(
new JSONDocument("{\"type\": \"retail\",\"volume\": 500}"))))
.build())
.build();
Iterator<Document> iterator = collection.aggregate(query);
assertDocsAndSizeEqual(
dataStoreName, iterator, "query/unwind_contains_filter_response.json", 3);
}

@ParameterizedTest
@ArgumentsSource(AllProvider.class)
void testNotContainsAndUnnestFilters(String dataStoreName) throws IOException {
Collection collection = getCollection(dataStoreName);

org.hypertrace.core.documentstore.query.Query query =
org.hypertrace.core.documentstore.query.Query.builder()
.addSelection(IdentifierExpression.of("item"))
.addSelection(IdentifierExpression.of("sales.medium"))
.addFromClause(
UnnestExpression.builder()
.identifierExpression(IdentifierExpression.of("sales"))
.preserveNullAndEmptyArrays(false)
.filterTypeExpression(
RelationalExpression.of(
IdentifierExpression.of("sales.medium"),
NOT_CONTAINS,
ConstantExpression.of(
new JSONDocument("{\"type\": \"retail\",\"volume\": 500}"))))
.build())
.build();
Iterator<Document> iterator = collection.aggregate(query);
assertDocsAndSizeEqual(
dataStoreName, iterator, "query/unwind_not_contains_filter_response.json", 2);
}

@ParameterizedTest
@ArgumentsSource(AllProvider.class)
public void testQueryV1DistinctCountWithSortingSpecs(String dataStoreName) throws IOException {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
[
{
"item":"Soap",
"sales":{
"medium":[
{
"type":"distributionChannel",
"volume":1000
},
{
"type":"retail",
"volume":500
},
{
"type":"online",
"volume":1000
}
]
}
},
{
"item":"Shampoo",
"sales":{
"medium":[
{
"type":"distributionChannel",
"volume":3000
},
{
"type":"retail",
"volume":500
},
{
"type":"online",
"volume":1000
}
]
}
},
{
"item":"Shampoo",
"sales":{
"medium":[
{
"type":"distributionChannel",
"volume":700
},
{
"type":"retail",
"volume":500
},
{
"type":"online",
"volume":5000
}
]
}
}
]
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
[
{
"item":"Soap",
"sales":{
"medium":[
{
"type":"distributionChannel",
"volume":300
},
{
"type":"online",
"volume":2000
}
]
}
},
{
"item":"Mirror",
"sales":{
"medium":[

]
}
}
]
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,10 @@
import lombok.AccessLevel;
import lombok.AllArgsConstructor;
import lombok.Value;
import lombok.experimental.NonFinal;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.hypertrace.core.documentstore.Document;
import org.hypertrace.core.documentstore.expression.type.SelectTypeExpression;
import org.hypertrace.core.documentstore.parser.SelectTypeExpressionVisitor;

Expand All @@ -19,10 +21,11 @@
* </code>
*/
@Value
@NonFinal
@AllArgsConstructor(access = AccessLevel.PRIVATE)
public class ConstantExpression implements SelectTypeExpression {

Object value;
protected Object value;

public static ConstantExpression of(final String value) {
return new ConstantExpression(value);
Expand All @@ -36,6 +39,10 @@ public static ConstantExpression of(final Boolean value) {
return new ConstantExpression(value);
}

public static ConstantExpression of(final Document value) {
return new DocumentConstantExpression(value);
}

public static ConstantExpression ofStrings(final List<String> values) {
return validateAndReturn(values);
}
Expand Down Expand Up @@ -68,4 +75,25 @@ public String toString() {
? StringUtils.wrap(value.toString(), "'")
: String.valueOf(value);
}

public static class DocumentConstantExpression extends ConstantExpression {
private DocumentConstantExpression(final Document value) {
super(value);
}

@Override
public <T> T accept(final SelectTypeExpressionVisitor visitor) {
return visitor.visit(this);
}

@Override
public Document getValue() {
return (Document) value;
}

@Override
public String toString() {
return "JSON(" + StringUtils.wrap(getValue().toJson(), "'") + ")";
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ public enum RelationalOperator {
LTE("<="),
IN("IN"),
CONTAINS("CONTAINS"),
NOT_CONTAINS("NOT CONTAINS"),
EXISTS("EXISTS"),
NOT_EXISTS("NOT EXISTS"),
LIKE("~"),
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,12 @@
package org.hypertrace.core.documentstore.mongo.query.parser;

import static org.hypertrace.core.documentstore.mongo.MongoUtils.sanitizeJsonString;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.mongodb.BasicDBObject;
import lombok.NoArgsConstructor;
import org.hypertrace.core.documentstore.expression.impl.ConstantExpression;
import org.hypertrace.core.documentstore.expression.impl.ConstantExpression.DocumentConstantExpression;

@NoArgsConstructor
final class MongoConstantExpressionParser extends MongoSelectTypeExpressionParser {
Expand All @@ -15,6 +20,16 @@ public Object visit(final ConstantExpression expression) {
return parse(expression);
}

@SuppressWarnings("unchecked")
@Override
public Object visit(final DocumentConstantExpression expression) {
try {
return BasicDBObject.parse(sanitizeJsonString(expression.getValue().toJson()));
} catch (final JsonProcessingException e) {
throw new RuntimeException(e);
}
}

Object parse(final ConstantExpression expression) {
return expression.getValue();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
import static org.hypertrace.core.documentstore.expression.operators.RelationalOperator.LT;
import static org.hypertrace.core.documentstore.expression.operators.RelationalOperator.LTE;
import static org.hypertrace.core.documentstore.expression.operators.RelationalOperator.NEQ;
import static org.hypertrace.core.documentstore.expression.operators.RelationalOperator.NOT_CONTAINS;
import static org.hypertrace.core.documentstore.expression.operators.RelationalOperator.NOT_EXISTS;
import static org.hypertrace.core.documentstore.expression.operators.RelationalOperator.NOT_IN;
import static org.hypertrace.core.documentstore.mongo.MongoUtils.PREFIX;
Expand All @@ -24,8 +25,13 @@
import org.hypertrace.core.documentstore.expression.impl.RelationalExpression;
import org.hypertrace.core.documentstore.expression.operators.RelationalOperator;
import org.hypertrace.core.documentstore.expression.type.SelectTypeExpression;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

final class MongoRelationalExpressionParser {
private static final Logger LOGGER =
LoggerFactory.getLogger(MongoRelationalExpressionParser.class);

private static final String EXPR = "$expr";
private static final String REGEX = "$regex";
private static final String OPTIONS = "$options";
Expand All @@ -46,6 +52,7 @@ final class MongoRelationalExpressionParser {
put(LTE, expressionHandler("lte"));
put(IN, handler("in"));
put(CONTAINS, handler("elemMatch"));
put(NOT_CONTAINS, notContainsHandler());
put(EXISTS, handler("exists"));
put(NOT_EXISTS, handler("exists"));
put(LIKE, likeHandler());
Expand Down Expand Up @@ -125,4 +132,14 @@ private static Map<String, Object> generateMap(
throw getUnsupportedOperationException(operator);
};
}

private static BiFunction<SelectTypeExpression, SelectTypeExpression, Map<String, Object>>
notContainsHandler() {
return (lhs, rhs) -> {
final String parsedLhs = lhs.accept(identifierParser);
final Object parsedRhs = rhs.accept(rhsParser);
return Map.of(
parsedLhs, new BasicDBObject("$not", new BasicDBObject(PREFIX + "elemMatch", parsedRhs)));
};
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import java.util.Map;
import org.hypertrace.core.documentstore.expression.impl.AggregateExpression;
import org.hypertrace.core.documentstore.expression.impl.ConstantExpression;
import org.hypertrace.core.documentstore.expression.impl.ConstantExpression.DocumentConstantExpression;
import org.hypertrace.core.documentstore.expression.impl.FunctionExpression;
import org.hypertrace.core.documentstore.expression.impl.IdentifierExpression;
import org.hypertrace.core.documentstore.parser.SelectTypeExpressionVisitor;
Expand Down Expand Up @@ -37,6 +38,11 @@ public <T> T visit(final ConstantExpression expression) {
return baseParser.visit(expression);
}

@Override
public <T> T visit(final DocumentConstantExpression expression) {
return baseParser.visit(expression);
}

@Override
public <T> T visit(final FunctionExpression expression) {
return baseParser.visit(expression);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import lombok.NoArgsConstructor;
import org.hypertrace.core.documentstore.expression.impl.AggregateExpression;
import org.hypertrace.core.documentstore.expression.impl.ConstantExpression;
import org.hypertrace.core.documentstore.expression.impl.ConstantExpression.DocumentConstantExpression;
import org.hypertrace.core.documentstore.expression.impl.FunctionExpression;
import org.hypertrace.core.documentstore.expression.impl.IdentifierExpression;

Expand All @@ -24,6 +25,11 @@ public <T> T visit(final ConstantExpression expression) {
throw getUnsupportedOperationException(expression);
}

@Override
public <T> T visit(final DocumentConstantExpression expression) {
throw getUnsupportedOperationException(expression);
}

@Override
public <T> T visit(final FunctionExpression expression) {
throw getUnsupportedOperationException(expression);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import lombok.AllArgsConstructor;
import org.hypertrace.core.documentstore.expression.impl.AggregateExpression;
import org.hypertrace.core.documentstore.expression.impl.ConstantExpression;
import org.hypertrace.core.documentstore.expression.impl.ConstantExpression.DocumentConstantExpression;
import org.hypertrace.core.documentstore.expression.impl.FunctionExpression;
import org.hypertrace.core.documentstore.expression.impl.IdentifierExpression;
import org.hypertrace.core.documentstore.expression.type.SelectTypeExpression;
Expand Down Expand Up @@ -105,6 +106,12 @@ public Optional<SelectionSpec> visit(final ConstantExpression expression) {
return Optional.empty();
}

@SuppressWarnings("unchecked")
@Override
public Optional<SelectionSpec> visit(final DocumentConstantExpression expression) {
return Optional.empty();
}

@SuppressWarnings("unchecked")
@Override
public Optional<SelectionSpec> visit(final FunctionExpression expression) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
import java.util.function.Function;
import org.hypertrace.core.documentstore.expression.impl.AggregateExpression;
import org.hypertrace.core.documentstore.expression.impl.ConstantExpression;
import org.hypertrace.core.documentstore.expression.impl.ConstantExpression.DocumentConstantExpression;
import org.hypertrace.core.documentstore.expression.impl.FunctionExpression;
import org.hypertrace.core.documentstore.expression.impl.IdentifierExpression;
import org.hypertrace.core.documentstore.expression.operators.AggregationOperator;
Expand Down Expand Up @@ -105,6 +106,12 @@ public SelectionSpec visit(final ConstantExpression expression) {
return source;
}

@SuppressWarnings("unchecked")
@Override
public SelectionSpec visit(final DocumentConstantExpression expression) {
return source;
}

@SuppressWarnings("unchecked")
@Override
public SelectionSpec visit(final FunctionExpression expression) {
Expand Down
Loading

0 comments on commit 2a8b182

Please sign in to comment.