Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -296,6 +296,54 @@ void getSolarSystemsWithNoneOfThePlanetsHavingBothWaterAndOxygenTogether(
JSONAssert.assertEquals(expected, actual, JSONCompareMode.LENIENT);
}

/**
* Tests returning documents where environment IDs are a subset of the provided allowed
* environment IDs.
*/
@ParameterizedTest
@ArgumentsSource(AllProvider.class)
void getDocumentsWithEnvironmentIdsSubsetOfGivenList(final String dataStoreName)
throws JSONException, IOException {
final String testCollectionName = "environment_scope_test";
final Datastore datastore = datastoreMap.get(dataStoreName);
final Map<Key, Document> testDocuments =
Utils.buildDocumentsFromResource("query/array_operators/environment_scope_test.json");
datastore.deleteCollection(testCollectionName);
datastore.createCollection(testCollectionName, null);
final Collection collection = datastore.getCollection(testCollectionName);
collection.bulkUpsert(testDocuments);

final java.util.List<String> environmentIds = java.util.List.of("env-1", "env-2", "env-3");

final Query query =
Query.builder()
.setFilter(
and(
RelationalExpression.of(
IdentifierExpression.of("scope.environmentScope.environmentIds"),
RelationalOperator.EXISTS,
ConstantExpression.of(true)),
not(
ArrayRelationalFilterExpression.builder()
.operator(ANY)
.filter(
RelationalExpression.of(
IdentifierExpression.of(
"scope.environmentScope.environmentIds"),
RelationalOperator.NOT_IN,
ConstantExpression.ofStrings(environmentIds)))
.build())))
.build();

final Iterator<Document> documents = collection.aggregate(query);
final String expected = readResource("environment_ids_subset.json");
final String actual = iteratorToJson(documents);

datastore.deleteCollection(testCollectionName);

JSONAssert.assertEquals(expected, actual, JSONCompareMode.LENIENT);
}

private String readResource(final String fileName) {
try {
return new String(
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
[
{
"scope": {
"environmentScope": {
"environmentIds": ["env-1", "env-2"]
}
},
"name": "Document A"
},
{
"scope": {
"environmentScope": {
"environmentIds": ["env-1", "env-2", "env-3"]
}
},
"name": "Document B"
}
]
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
[
{
"_id": 1,
"name": "Document A",
"scope": {
"environmentScope": {
"environmentIds": ["env-1", "env-2"]
}
}
},
{
"_id": 2,
"name": "Document B",
"scope": {
"environmentScope": {
"environmentIds": ["env-1", "env-2", "env-3"]
}
}
},
{
"_id": 3,
"name": "Document C",
"scope": {
"environmentScope": {
"environmentIds": ["env-1", "env-4"]
}
}
},
{
"_id": 4,
"name": "Document D",
"scope": {
"environmentScope": {
"environmentIds": ["env-5", "env-6"]
}
}
},
{
"_id": 5,
"name": "Document E"
}
]
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package org.hypertrace.core.documentstore.mongo.query.parser.filter;

import java.util.Map;
import lombok.AllArgsConstructor;
import org.hypertrace.core.documentstore.expression.impl.RelationalExpression;
import org.hypertrace.core.documentstore.mongo.query.parser.filter.MongoRelationalFilterParserFactory.MongoRelationalFilterContext;

/**
* Note: MongoDB does not have a native "NOT IN" aggregation operator. This parser simulates "NOT
* IN" functionality by combining the $not and $in operators.
*/
@AllArgsConstructor
public class MongoNotInExprRelationalFilterParser implements MongoRelationalFilterParser {
private static final String NOT_OP = "$not";
private static final String IN_OP = "$in";
private static final MongoStandardRelationalOperatorMapping mapping =
new MongoStandardRelationalOperatorMapping();

@Override
public Map<String, Object> parse(
final RelationalExpression expression, final MongoRelationalFilterContext context) {
final String parsedLhs = expression.getLhs().accept(context.lhsParser());
final Object parsedRhs = expression.getRhs().accept(context.rhsParser());
return Map.of(NOT_OP, Map.of(IN_OP, new Object[] {parsedLhs, parsedRhs}));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,22 @@ public MongoRelationalFilterParser parser(
}

case IN:
if (INSIDE_EXPR.equals(context.location())) {
return new MongoStandardExprRelationalFilterParser();
} else if (OUTSIDE_EXPR.equals(context.location())) {
return new MongoStandardNonExprRelationalFilterParser();
} else {
throw new UnsupportedOperationException("Unsupported location: " + context.location());
}

case NOT_IN:
return new MongoStandardNonExprRelationalFilterParser();
if (INSIDE_EXPR.equals(context.location())) {
return new MongoNotInExprRelationalFilterParser();
} else if (OUTSIDE_EXPR.equals(context.location())) {
return new MongoStandardNonExprRelationalFilterParser();
} else {
throw new UnsupportedOperationException("Unsupported location: " + context.location());
}

case CONTAINS:
return new MongoContainsRelationalFilterParser();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -181,7 +181,7 @@ private String getFilterStringForAnyOperator(final ArrayRelationalFilterExpressi
.accept(new PostgresIdentifierExpressionVisitor(postgresQueryParser));

// If the field name is 'elements.inner', alias becomes 'elements_dot_inner'
final String alias = encodeAliasForNestedField(identifierName);
final String alias = encodeAliasForNestedField(identifierName).toLowerCase();

// Any LHS field name (elements) is to be prefixed with current alias (elements_dot_inner)
final PostgresWrappingFilterVisitorProvider visitorProvider =
Expand Down
Loading