From df6fedf792a7bbe6faf24564f42ac592318dbf3d Mon Sep 17 00:00:00 2001 From: Sukhamjot-Singh Date: Tue, 9 Sep 2025 17:24:49 +0530 Subject: [PATCH 1/3] Adding logic and fixing order --- ...ongoRelationalFilterParserFactoryImpl.java | 16 +++++++++++- ...andardExprNotInRelationalFilterParser.java | 26 +++++++++++++++++++ 2 files changed, 41 insertions(+), 1 deletion(-) create mode 100644 document-store/src/main/java/org/hypertrace/core/documentstore/mongo/query/parser/filter/MongoStandardExprNotInRelationalFilterParser.java diff --git a/document-store/src/main/java/org/hypertrace/core/documentstore/mongo/query/parser/filter/MongoRelationalFilterParserFactoryImpl.java b/document-store/src/main/java/org/hypertrace/core/documentstore/mongo/query/parser/filter/MongoRelationalFilterParserFactoryImpl.java index 22f4d8cf9..d08a62cbc 100644 --- a/document-store/src/main/java/org/hypertrace/core/documentstore/mongo/query/parser/filter/MongoRelationalFilterParserFactoryImpl.java +++ b/document-store/src/main/java/org/hypertrace/core/documentstore/mongo/query/parser/filter/MongoRelationalFilterParserFactoryImpl.java @@ -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 MongoStandardExprNotInRelationalFilterParser(); + } else if (OUTSIDE_EXPR.equals(context.location())) { + return new MongoStandardNonExprRelationalFilterParser(); + } else { + throw new UnsupportedOperationException("Unsupported location: " + context.location()); + } case CONTAINS: return new MongoContainsRelationalFilterParser(); diff --git a/document-store/src/main/java/org/hypertrace/core/documentstore/mongo/query/parser/filter/MongoStandardExprNotInRelationalFilterParser.java b/document-store/src/main/java/org/hypertrace/core/documentstore/mongo/query/parser/filter/MongoStandardExprNotInRelationalFilterParser.java new file mode 100644 index 000000000..dc26d21a2 --- /dev/null +++ b/document-store/src/main/java/org/hypertrace/core/documentstore/mongo/query/parser/filter/MongoStandardExprNotInRelationalFilterParser.java @@ -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 MongoStandardExprNotInRelationalFilterParser 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 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})); + } +} From 8768d6e5635b431bd4703f8aa5ace75de20f6356 Mon Sep 17 00:00:00 2001 From: Sukhamjot-Singh Date: Tue, 9 Sep 2025 23:08:03 +0530 Subject: [PATCH 2/3] Added test --- .../ArrayFiltersQueryIntegrationTest.java | 52 +++++++++++++++++++ .../environment_ids_subset.json | 18 +++++++ .../environment_scope_test.json | 42 +++++++++++++++ ...MongoNotInExprRelationalFilterParser.java} | 2 +- ...ongoRelationalFilterParserFactoryImpl.java | 2 +- 5 files changed, 114 insertions(+), 2 deletions(-) create mode 100644 document-store/src/integrationTest/resources/query/array_operators/environment_ids_subset.json create mode 100644 document-store/src/integrationTest/resources/query/array_operators/environment_scope_test.json rename document-store/src/main/java/org/hypertrace/core/documentstore/mongo/query/parser/filter/{MongoStandardExprNotInRelationalFilterParser.java => MongoNotInExprRelationalFilterParser.java} (91%) diff --git a/document-store/src/integrationTest/java/org/hypertrace/core/documentstore/ArrayFiltersQueryIntegrationTest.java b/document-store/src/integrationTest/java/org/hypertrace/core/documentstore/ArrayFiltersQueryIntegrationTest.java index e6361f213..c6af6f22a 100644 --- a/document-store/src/integrationTest/java/org/hypertrace/core/documentstore/ArrayFiltersQueryIntegrationTest.java +++ b/document-store/src/integrationTest/java/org/hypertrace/core/documentstore/ArrayFiltersQueryIntegrationTest.java @@ -296,6 +296,58 @@ 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(MongoProvider.class) + void getDocumentsWithEnvironmentIdsSubsetOfGivenList(final String dataStoreName) + throws JSONException, IOException { + // Create a temporary collection for this test + final String testCollectionName = "environment_scope_test"; + final Datastore datastore = datastoreMap.get(dataStoreName); + + // Setup test data + final Map 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 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 documents = collection.aggregate(query); + final String expected = readResource("environment_ids_subset.json"); + final String actual = iteratorToJson(documents); + + // Cleanup + datastore.deleteCollection(testCollectionName); + + JSONAssert.assertEquals(expected, actual, JSONCompareMode.LENIENT); + } + private String readResource(final String fileName) { try { return new String( diff --git a/document-store/src/integrationTest/resources/query/array_operators/environment_ids_subset.json b/document-store/src/integrationTest/resources/query/array_operators/environment_ids_subset.json new file mode 100644 index 000000000..199378982 --- /dev/null +++ b/document-store/src/integrationTest/resources/query/array_operators/environment_ids_subset.json @@ -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" + } +] diff --git a/document-store/src/integrationTest/resources/query/array_operators/environment_scope_test.json b/document-store/src/integrationTest/resources/query/array_operators/environment_scope_test.json new file mode 100644 index 000000000..e10df4d2b --- /dev/null +++ b/document-store/src/integrationTest/resources/query/array_operators/environment_scope_test.json @@ -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" + } +] diff --git a/document-store/src/main/java/org/hypertrace/core/documentstore/mongo/query/parser/filter/MongoStandardExprNotInRelationalFilterParser.java b/document-store/src/main/java/org/hypertrace/core/documentstore/mongo/query/parser/filter/MongoNotInExprRelationalFilterParser.java similarity index 91% rename from document-store/src/main/java/org/hypertrace/core/documentstore/mongo/query/parser/filter/MongoStandardExprNotInRelationalFilterParser.java rename to document-store/src/main/java/org/hypertrace/core/documentstore/mongo/query/parser/filter/MongoNotInExprRelationalFilterParser.java index dc26d21a2..8fd04bdd1 100644 --- a/document-store/src/main/java/org/hypertrace/core/documentstore/mongo/query/parser/filter/MongoStandardExprNotInRelationalFilterParser.java +++ b/document-store/src/main/java/org/hypertrace/core/documentstore/mongo/query/parser/filter/MongoNotInExprRelationalFilterParser.java @@ -10,7 +10,7 @@ * IN" functionality by combining the $not and $in operators. */ @AllArgsConstructor -public class MongoStandardExprNotInRelationalFilterParser implements MongoRelationalFilterParser { +public class MongoNotInExprRelationalFilterParser implements MongoRelationalFilterParser { private static final String NOT_OP = "$not"; private static final String IN_OP = "$in"; private static final MongoStandardRelationalOperatorMapping mapping = diff --git a/document-store/src/main/java/org/hypertrace/core/documentstore/mongo/query/parser/filter/MongoRelationalFilterParserFactoryImpl.java b/document-store/src/main/java/org/hypertrace/core/documentstore/mongo/query/parser/filter/MongoRelationalFilterParserFactoryImpl.java index d08a62cbc..4381086c7 100644 --- a/document-store/src/main/java/org/hypertrace/core/documentstore/mongo/query/parser/filter/MongoRelationalFilterParserFactoryImpl.java +++ b/document-store/src/main/java/org/hypertrace/core/documentstore/mongo/query/parser/filter/MongoRelationalFilterParserFactoryImpl.java @@ -36,7 +36,7 @@ public MongoRelationalFilterParser parser( case NOT_IN: if (INSIDE_EXPR.equals(context.location())) { - return new MongoStandardExprNotInRelationalFilterParser(); + return new MongoNotInExprRelationalFilterParser(); } else if (OUTSIDE_EXPR.equals(context.location())) { return new MongoStandardNonExprRelationalFilterParser(); } else { From d53cc4e0d6e5d70a58086ebf625b1b5719cea56c Mon Sep 17 00:00:00 2001 From: Sukhamjot-Singh Date: Mon, 13 Oct 2025 17:15:17 +0530 Subject: [PATCH 3/3] postgres fix --- .../documentstore/ArrayFiltersQueryIntegrationTest.java | 6 +----- .../v1/vistors/PostgresFilterTypeExpressionVisitor.java | 2 +- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/document-store/src/integrationTest/java/org/hypertrace/core/documentstore/ArrayFiltersQueryIntegrationTest.java b/document-store/src/integrationTest/java/org/hypertrace/core/documentstore/ArrayFiltersQueryIntegrationTest.java index c6af6f22a..318bafa67 100644 --- a/document-store/src/integrationTest/java/org/hypertrace/core/documentstore/ArrayFiltersQueryIntegrationTest.java +++ b/document-store/src/integrationTest/java/org/hypertrace/core/documentstore/ArrayFiltersQueryIntegrationTest.java @@ -301,14 +301,11 @@ void getSolarSystemsWithNoneOfThePlanetsHavingBothWaterAndOxygenTogether( * environment IDs. */ @ParameterizedTest - @ArgumentsSource(MongoProvider.class) + @ArgumentsSource(AllProvider.class) void getDocumentsWithEnvironmentIdsSubsetOfGivenList(final String dataStoreName) throws JSONException, IOException { - // Create a temporary collection for this test final String testCollectionName = "environment_scope_test"; final Datastore datastore = datastoreMap.get(dataStoreName); - - // Setup test data final Map testDocuments = Utils.buildDocumentsFromResource("query/array_operators/environment_scope_test.json"); datastore.deleteCollection(testCollectionName); @@ -342,7 +339,6 @@ void getDocumentsWithEnvironmentIdsSubsetOfGivenList(final String dataStoreName) final String expected = readResource("environment_ids_subset.json"); final String actual = iteratorToJson(documents); - // Cleanup datastore.deleteCollection(testCollectionName); JSONAssert.assertEquals(expected, actual, JSONCompareMode.LENIENT); diff --git a/document-store/src/main/java/org/hypertrace/core/documentstore/postgres/query/v1/vistors/PostgresFilterTypeExpressionVisitor.java b/document-store/src/main/java/org/hypertrace/core/documentstore/postgres/query/v1/vistors/PostgresFilterTypeExpressionVisitor.java index 7875119e6..2d9896a16 100644 --- a/document-store/src/main/java/org/hypertrace/core/documentstore/postgres/query/v1/vistors/PostgresFilterTypeExpressionVisitor.java +++ b/document-store/src/main/java/org/hypertrace/core/documentstore/postgres/query/v1/vistors/PostgresFilterTypeExpressionVisitor.java @@ -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 =