getFieldType() {
+ return Optional.ofNullable(fieldType);
}
/**
diff --git a/document-store/src/main/java/org/hypertrace/core/documentstore/postgres/query/v1/parser/filter/PostgresInParserSelector.java b/document-store/src/main/java/org/hypertrace/core/documentstore/postgres/query/v1/parser/filter/PostgresInParserSelector.java
index a7a86460..74d1776d 100644
--- a/document-store/src/main/java/org/hypertrace/core/documentstore/postgres/query/v1/parser/filter/PostgresInParserSelector.java
+++ b/document-store/src/main/java/org/hypertrace/core/documentstore/postgres/query/v1/parser/filter/PostgresInParserSelector.java
@@ -7,6 +7,7 @@
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.impl.JsonFieldType;
import org.hypertrace.core.documentstore.expression.impl.JsonIdentifierExpression;
import org.hypertrace.core.documentstore.parser.SelectTypeExpressionVisitor;
import org.hypertrace.core.documentstore.postgres.query.v1.parser.filter.nonjson.field.PostgresInRelationalFilterParserArrayField;
@@ -14,8 +15,13 @@
class PostgresInParserSelector implements SelectTypeExpressionVisitor {
+ // Parsers for different expression types
private static final PostgresInRelationalFilterParserInterface jsonFieldInFilterParser =
- new PostgresInRelationalFilterParser();
+ new PostgresInRelationalFilterParser(); // Fallback for JSON without type info
+ private static final PostgresInRelationalFilterParserInterface jsonPrimitiveInFilterParser =
+ new PostgresInRelationalFilterParserJsonPrimitive(); // Optimized for JSON primitives
+ private static final PostgresInRelationalFilterParserInterface jsonArrayInFilterParser =
+ new PostgresInRelationalFilterParserJsonArray(); // Optimized for JSON arrays
private static final PostgresInRelationalFilterParserInterface scalarFieldInFilterParser =
new PostgresInRelationalFilterParserScalarField();
private static final PostgresInRelationalFilterParserInterface arrayFieldInFilterParser =
@@ -29,7 +35,28 @@ class PostgresInParserSelector implements SelectTypeExpressionVisitor {
@Override
public PostgresInRelationalFilterParserInterface visit(JsonIdentifierExpression expression) {
- return jsonFieldInFilterParser;
+ // JsonFieldType is required for optimized SQL generation
+ JsonFieldType fieldType = getFieldType(expression);
+
+ switch (fieldType) {
+ case STRING:
+ case NUMBER:
+ case BOOLEAN:
+ // Primitives: use ->> (extract as text) with appropriate casting
+ return jsonPrimitiveInFilterParser;
+ case STRING_ARRAY:
+ case NUMBER_ARRAY:
+ case BOOLEAN_ARRAY:
+ case OBJECT_ARRAY:
+ // Typed arrays: use -> with @> and typed jsonb_build_array
+ return jsonArrayInFilterParser;
+ case OBJECT:
+ // Objects: use -> with @> (future: needs separate parser)
+ throw new UnsupportedOperationException(
+ "IN operator on OBJECT type is not yet supported. Use primitive or array types.");
+ default:
+ throw new IllegalArgumentException("Unsupported JsonFieldType: " + fieldType);
+ }
}
@Override
@@ -68,4 +95,14 @@ public PostgresInRelationalFilterParserInterface visit(FunctionExpression expres
public PostgresInRelationalFilterParserInterface visit(AliasedIdentifierExpression expression) {
return isFlatCollection ? scalarFieldInFilterParser : jsonFieldInFilterParser;
}
+
+ private static JsonFieldType getFieldType(JsonIdentifierExpression expression) {
+ return expression
+ .getFieldType()
+ .orElseThrow(
+ () ->
+ new IllegalArgumentException(
+ "JsonFieldType must be specified for JsonIdentifierExpression in IN operations. "
+ + "Use JsonIdentifierExpression.of(column, JsonFieldType.*, path...)"));
+ }
}
diff --git a/document-store/src/main/java/org/hypertrace/core/documentstore/postgres/query/v1/parser/filter/PostgresInRelationalFilterParserJsonArray.java b/document-store/src/main/java/org/hypertrace/core/documentstore/postgres/query/v1/parser/filter/PostgresInRelationalFilterParserJsonArray.java
new file mode 100644
index 00000000..a0b8b2ad
--- /dev/null
+++ b/document-store/src/main/java/org/hypertrace/core/documentstore/postgres/query/v1/parser/filter/PostgresInRelationalFilterParserJsonArray.java
@@ -0,0 +1,96 @@
+package org.hypertrace.core.documentstore.postgres.query.v1.parser.filter;
+
+import java.util.stream.Collectors;
+import java.util.stream.StreamSupport;
+import org.hypertrace.core.documentstore.expression.impl.JsonFieldType;
+import org.hypertrace.core.documentstore.expression.impl.JsonIdentifierExpression;
+import org.hypertrace.core.documentstore.expression.impl.RelationalExpression;
+import org.hypertrace.core.documentstore.postgres.Params;
+
+/**
+ * Optimized parser for IN operations on JSON array fields with type-specific casting.
+ *
+ * Uses JSONB containment operator (@>) with typed jsonb_build_array for "contains any"
+ * semantics:
+ *
+ *
+ * STRING_ARRAY: {@code "document" -> 'tags' @> jsonb_build_array(?::text)}
+ * NUMBER_ARRAY: {@code "document" -> 'scores' @> jsonb_build_array(?::numeric)}
+ * BOOLEAN_ARRAY: {@code "document" -> 'flags' @> jsonb_build_array(?::boolean)}
+ * OBJECT_ARRAY: {@code "document" -> 'items' @> jsonb_build_array(?::jsonb)}
+ *
+ *
+ * This checks if the JSON array contains ANY of the provided values, using efficient JSONB
+ * containment instead of defensive type checking.
+ */
+public class PostgresInRelationalFilterParserJsonArray
+ implements PostgresInRelationalFilterParserInterface {
+
+ @Override
+ public String parse(
+ final RelationalExpression expression, final PostgresRelationalFilterContext context) {
+ final String parsedLhs = expression.getLhs().accept(context.lhsParser());
+ final Iterable parsedRhs = expression.getRhs().accept(context.rhsParser());
+
+ // Extract field type for typed array handling (guaranteed to be present by selector)
+ JsonIdentifierExpression jsonExpr = (JsonIdentifierExpression) expression.getLhs();
+ JsonFieldType fieldType =
+ jsonExpr
+ .getFieldType()
+ .orElseThrow(
+ () ->
+ new IllegalStateException(
+ "JsonFieldType must be present - this should have been caught by the selector"));
+
+ return prepareFilterStringForInOperator(
+ parsedLhs, parsedRhs, fieldType, context.getParamsBuilder());
+ }
+
+ private String prepareFilterStringForInOperator(
+ final String parsedLhs,
+ final Iterable parsedRhs,
+ final JsonFieldType fieldType,
+ final Params.Builder paramsBuilder) {
+
+ // Determine the appropriate type cast for jsonb_build_array elements
+ String typeCast = getTypeCastForArray(fieldType);
+
+ // For JSON arrays, we use the @> containment operator
+ // Check if ANY of the RHS values is contained in the LHS array
+ String orConditions =
+ StreamSupport.stream(parsedRhs.spliterator(), false)
+ .map(
+ value -> {
+ paramsBuilder.addObjectParam(value);
+ return String.format("%s @> jsonb_build_array(?%s)", parsedLhs, typeCast);
+ })
+ .collect(Collectors.joining(" OR "));
+
+ // Wrap in parentheses if multiple conditions
+ return StreamSupport.stream(parsedRhs.spliterator(), false).count() > 1
+ ? String.format("(%s)", orConditions)
+ : orConditions;
+ }
+
+ /**
+ * Returns the PostgreSQL type cast string for jsonb_build_array elements based on array type.
+ *
+ * @param fieldType The JSON field type (must not be null)
+ * @return Type cast string (e.g., "::text", "::numeric")
+ */
+ private String getTypeCastForArray(JsonFieldType fieldType) {
+ switch (fieldType) {
+ case STRING_ARRAY:
+ return "::text";
+ case NUMBER_ARRAY:
+ return "::numeric";
+ case BOOLEAN_ARRAY:
+ return "::boolean";
+ case OBJECT_ARRAY:
+ return "::jsonb";
+ default:
+ throw new IllegalArgumentException(
+ "Unsupported array type: " + fieldType + ". Expected *_ARRAY types.");
+ }
+ }
+}
diff --git a/document-store/src/main/java/org/hypertrace/core/documentstore/postgres/query/v1/parser/filter/PostgresInRelationalFilterParserJsonPrimitive.java b/document-store/src/main/java/org/hypertrace/core/documentstore/postgres/query/v1/parser/filter/PostgresInRelationalFilterParserJsonPrimitive.java
new file mode 100644
index 00000000..f381e745
--- /dev/null
+++ b/document-store/src/main/java/org/hypertrace/core/documentstore/postgres/query/v1/parser/filter/PostgresInRelationalFilterParserJsonPrimitive.java
@@ -0,0 +1,84 @@
+package org.hypertrace.core.documentstore.postgres.query.v1.parser.filter;
+
+import java.util.stream.Collectors;
+import java.util.stream.StreamSupport;
+import org.hypertrace.core.documentstore.expression.impl.JsonFieldType;
+import org.hypertrace.core.documentstore.expression.impl.JsonIdentifierExpression;
+import org.hypertrace.core.documentstore.expression.impl.RelationalExpression;
+import org.hypertrace.core.documentstore.postgres.Params;
+
+/**
+ * Optimized parser for IN operations on JSON primitive fields (string, number, boolean) with proper
+ * type casting.
+ *
+ * Generates efficient SQL using {@code ->>} operator with appropriate PostgreSQL casting:
+ *
+ *
+ * STRING: {@code "document" ->> 'item' IN ('Soap', 'Shampoo')}
+ * NUMBER: {@code CAST("document" ->> 'price' AS NUMERIC) IN (10, 20)}
+ * BOOLEAN: {@code CAST("document" ->> 'active' AS BOOLEAN) IN (true, false)}
+ *
+ *
+ * This is much more efficient than the defensive approach that checks both array and scalar
+ * types, and ensures correct type comparisons.
+ */
+public class PostgresInRelationalFilterParserJsonPrimitive
+ implements PostgresInRelationalFilterParserInterface {
+
+ @Override
+ public String parse(
+ final RelationalExpression expression, final PostgresRelationalFilterContext context) {
+ String parsedLhs = expression.getLhs().accept(context.lhsParser());
+ final Iterable parsedRhs = expression.getRhs().accept(context.rhsParser());
+
+ // Extract field type for proper casting (guaranteed to be present by selector)
+ JsonIdentifierExpression jsonExpr = (JsonIdentifierExpression) expression.getLhs();
+ JsonFieldType fieldType =
+ jsonExpr
+ .getFieldType()
+ .orElseThrow(
+ () ->
+ new IllegalStateException(
+ "JsonFieldType must be present - this should have been caught by the selector"));
+
+ // For JSON primitives, we need ->> (text extraction) instead of -> (jsonb extraction)
+ // The LHS parser generates: "props"->'brand' (returns JSONB)
+ // We need: "props"->>'brand' (returns TEXT)
+ // Replace the last -> with ->> for primitive type extraction
+ int lastArrowIndex = parsedLhs.lastIndexOf("->");
+ if (lastArrowIndex != -1) {
+ parsedLhs =
+ parsedLhs.substring(0, lastArrowIndex) + "->>" + parsedLhs.substring(lastArrowIndex + 2);
+ }
+
+ return prepareFilterStringForInOperator(
+ parsedLhs, parsedRhs, fieldType, context.getParamsBuilder());
+ }
+
+ private String prepareFilterStringForInOperator(
+ final String parsedLhs,
+ final Iterable parsedRhs,
+ final JsonFieldType fieldType,
+ final Params.Builder paramsBuilder) {
+
+ String placeholders =
+ StreamSupport.stream(parsedRhs.spliterator(), false)
+ .map(
+ value -> {
+ paramsBuilder.addObjectParam(value);
+ return "?";
+ })
+ .collect(Collectors.joining(", "));
+
+ // Apply appropriate casting based on field type
+ String lhsWithCast = parsedLhs;
+ if (fieldType == JsonFieldType.NUMBER) {
+ lhsWithCast = String.format("CAST(%s AS NUMERIC)", parsedLhs);
+ } else if (fieldType == JsonFieldType.BOOLEAN) {
+ lhsWithCast = String.format("CAST(%s AS BOOLEAN)", parsedLhs);
+ }
+ // STRING or null fieldType: no casting needed
+
+ return String.format("%s IN (%s)", lhsWithCast, placeholders);
+ }
+}
diff --git a/document-store/src/main/java/org/hypertrace/core/documentstore/postgres/query/v1/parser/filter/PostgresNotInParserSelector.java b/document-store/src/main/java/org/hypertrace/core/documentstore/postgres/query/v1/parser/filter/PostgresNotInParserSelector.java
index a86b249d..ee86f767 100644
--- a/document-store/src/main/java/org/hypertrace/core/documentstore/postgres/query/v1/parser/filter/PostgresNotInParserSelector.java
+++ b/document-store/src/main/java/org/hypertrace/core/documentstore/postgres/query/v1/parser/filter/PostgresNotInParserSelector.java
@@ -7,6 +7,7 @@
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.impl.JsonFieldType;
import org.hypertrace.core.documentstore.expression.impl.JsonIdentifierExpression;
import org.hypertrace.core.documentstore.parser.SelectTypeExpressionVisitor;
import org.hypertrace.core.documentstore.postgres.query.v1.parser.filter.nonjson.field.PostgresInRelationalFilterParserArrayField;
@@ -14,8 +15,13 @@
class PostgresNotInParserSelector implements SelectTypeExpressionVisitor {
+ // Parsers for different expression types
private static final PostgresInRelationalFilterParserInterface jsonFieldInFilterParser =
- new PostgresInRelationalFilterParser();
+ new PostgresInRelationalFilterParser(); // Fallback for JSON without type info
+ private static final PostgresInRelationalFilterParserInterface jsonPrimitiveInFilterParser =
+ new PostgresInRelationalFilterParserJsonPrimitive(); // Optimized for JSON primitives
+ private static final PostgresInRelationalFilterParserInterface jsonArrayInFilterParser =
+ new PostgresInRelationalFilterParserJsonArray(); // Optimized for JSON arrays
private static final PostgresInRelationalFilterParserInterface scalarFieldInFilterParser =
new PostgresInRelationalFilterParserScalarField();
private static final PostgresInRelationalFilterParserInterface arrayFieldInFilterParser =
@@ -29,7 +35,35 @@ class PostgresNotInParserSelector implements SelectTypeExpressionVisitor {
@Override
public PostgresInRelationalFilterParserInterface visit(JsonIdentifierExpression expression) {
- return jsonFieldInFilterParser;
+ // JsonFieldType is required for optimized SQL generation
+ JsonFieldType fieldType =
+ expression
+ .getFieldType()
+ .orElseThrow(
+ () ->
+ new IllegalArgumentException(
+ "JsonFieldType must be specified for JsonIdentifierExpression in NOT_IN operations. "
+ + "Use JsonIdentifierExpression.of(column, JsonFieldType.*, path...)"));
+
+ switch (fieldType) {
+ case STRING:
+ case NUMBER:
+ case BOOLEAN:
+ // Primitives: use ->> (extract as text) with appropriate casting
+ return jsonPrimitiveInFilterParser;
+ case STRING_ARRAY:
+ case NUMBER_ARRAY:
+ case BOOLEAN_ARRAY:
+ case OBJECT_ARRAY:
+ // Typed arrays: use -> with @> and typed jsonb_build_array
+ return jsonArrayInFilterParser;
+ case OBJECT:
+ // Objects: use -> with @> (future: needs separate parser)
+ throw new UnsupportedOperationException(
+ "NOT_IN operator on OBJECT type is not yet supported. Use primitive or array types.");
+ default:
+ throw new IllegalArgumentException("Unsupported JsonFieldType: " + fieldType);
+ }
}
@Override
diff --git a/document-store/src/test/java/org/hypertrace/core/documentstore/expression/impl/JsonArrayIdentifierExpressionTest.java b/document-store/src/test/java/org/hypertrace/core/documentstore/expression/impl/JsonArrayIdentifierExpressionTest.java
deleted file mode 100644
index 250888a1..00000000
--- a/document-store/src/test/java/org/hypertrace/core/documentstore/expression/impl/JsonArrayIdentifierExpressionTest.java
+++ /dev/null
@@ -1,259 +0,0 @@
-package org.hypertrace.core.documentstore.expression.impl;
-
-import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
-import static org.junit.jupiter.api.Assertions.assertEquals;
-import static org.junit.jupiter.api.Assertions.assertNotEquals;
-import static org.junit.jupiter.api.Assertions.assertThrows;
-import static org.junit.jupiter.api.Assertions.assertTrue;
-
-import java.util.Collections;
-import java.util.List;
-import org.junit.jupiter.api.Test;
-
-class JsonArrayIdentifierExpressionTest {
-
- @Test
- void testOfWithVarargs() {
- JsonArrayIdentifierExpression expression =
- JsonArrayIdentifierExpression.of("attributes", "tags");
-
- assertEquals("attributes", expression.getColumnName());
- assertEquals(List.of("tags"), expression.getJsonPath());
- }
-
- @Test
- void testOfWithMultiplePathElements() {
- JsonArrayIdentifierExpression expression =
- JsonArrayIdentifierExpression.of("attributes", "nested", "array", "field");
-
- assertEquals("attributes", expression.getColumnName());
- assertEquals(List.of("nested", "array", "field"), expression.getJsonPath());
- }
-
- @Test
- void testOfWithList() {
- JsonArrayIdentifierExpression expression =
- JsonArrayIdentifierExpression.of("attributes", List.of("certifications"));
-
- assertEquals("attributes", expression.getColumnName());
- assertEquals(List.of("certifications"), expression.getJsonPath());
- }
-
- @Test
- void testOfWithNullPathElementsThrowsException() {
- IllegalArgumentException exception =
- assertThrows(
- IllegalArgumentException.class,
- () -> JsonArrayIdentifierExpression.of("attributes", (String[]) null));
-
- assertEquals("JSON path cannot be null or empty for array field", exception.getMessage());
- }
-
- @Test
- void testOfWithEmptyPathElementsThrowsException() {
- IllegalArgumentException exception =
- assertThrows(
- IllegalArgumentException.class, () -> JsonArrayIdentifierExpression.of("attributes"));
-
- assertEquals(
- "JSON path cannot be null or empty. Use of(columnName, path...) instead.",
- exception.getMessage());
- }
-
- @Test
- void testOfWithNullListThrowsException() {
- IllegalArgumentException exception =
- assertThrows(
- IllegalArgumentException.class,
- () -> JsonArrayIdentifierExpression.of("attributes", (List) null));
-
- assertEquals("JSON path cannot be null or empty for array field", exception.getMessage());
- }
-
- @Test
- void testOfWithEmptyListThrowsException() {
- IllegalArgumentException exception =
- assertThrows(
- IllegalArgumentException.class,
- () -> JsonArrayIdentifierExpression.of("attributes", Collections.emptyList()));
-
- assertEquals("JSON path cannot be null or empty for array field", exception.getMessage());
- }
-
- @Test
- void testEqualsAndHashCode() {
- JsonArrayIdentifierExpression expr1 = JsonArrayIdentifierExpression.of("attributes", "tags");
- JsonArrayIdentifierExpression expr2 = JsonArrayIdentifierExpression.of("attributes", "tags");
-
- // Test equality
- assertEquals(expr1, expr2, "Expressions with same column and path should be equal");
-
- // Test hashCode
- assertEquals(
- expr1.hashCode(),
- expr2.hashCode(),
- "Expressions with same column and path should have same hashCode");
- }
-
- @Test
- void testNotEqualsWithDifferentPath() {
- JsonArrayIdentifierExpression expr1 = JsonArrayIdentifierExpression.of("attributes", "tags");
- JsonArrayIdentifierExpression expr2 =
- JsonArrayIdentifierExpression.of("attributes", "categories");
-
- assertNotEquals(expr1, expr2, "Expressions with different paths should not be equal");
- }
-
- @Test
- void testNotEqualsWithDifferentColumn() {
- JsonArrayIdentifierExpression expr1 = JsonArrayIdentifierExpression.of("attributes", "tags");
- JsonArrayIdentifierExpression expr2 = JsonArrayIdentifierExpression.of("props", "tags");
-
- assertNotEquals(expr1, expr2, "Expressions with different columns should not be equal");
- }
-
- @Test
- void testNotEqualsWithJsonIdentifierExpression() {
- JsonArrayIdentifierExpression arrayExpr =
- JsonArrayIdentifierExpression.of("attributes", "tags");
- JsonIdentifierExpression scalarExpr = JsonIdentifierExpression.of("attributes", "tags");
-
- // Even though they have the same column and path, they are different types
- assertNotEquals(
- arrayExpr,
- scalarExpr,
- "JsonArrayIdentifierExpression should not equal JsonIdentifierExpression");
- }
-
- @Test
- void testInheritsFromJsonIdentifierExpression() {
- JsonArrayIdentifierExpression expression =
- JsonArrayIdentifierExpression.of("attributes", "tags");
-
- // Verify it's an instance of parent class
- assertEquals(
- JsonIdentifierExpression.class,
- expression.getClass().getSuperclass(),
- "JsonArrayIdentifierExpression should extend JsonIdentifierExpression");
- }
-
- @Test
- void testValidColumnNames() {
- // Valid column names should not throw
- assertDoesNotThrow(() -> JsonArrayIdentifierExpression.of("attributes", "tags"));
- assertDoesNotThrow(() -> JsonArrayIdentifierExpression.of("_internal", "field"));
- assertDoesNotThrow(() -> JsonArrayIdentifierExpression.of("customAttr", "array"));
- assertDoesNotThrow(() -> JsonArrayIdentifierExpression.of("attr123", "field"));
- }
-
- @Test
- void testInvalidColumnNameWithSqlInjection() {
- SecurityException exception =
- assertThrows(
- SecurityException.class,
- () -> JsonArrayIdentifierExpression.of("attributes\"; DROP TABLE users; --", "tags"));
-
- assertTrue(exception.getMessage().contains("invalid"));
- }
-
- @Test
- void testInvalidColumnNameStartsWithNumber() {
- SecurityException exception =
- assertThrows(
- SecurityException.class, () -> JsonArrayIdentifierExpression.of("123attr", "tags"));
-
- assertTrue(exception.getMessage().contains("Must start with a letter or underscore"));
- }
-
- @Test
- void testInvalidColumnNameWithSpace() {
- SecurityException exception =
- assertThrows(
- SecurityException.class, () -> JsonArrayIdentifierExpression.of("my column", "tags"));
-
- assertTrue(exception.getMessage().contains("invalid"));
- }
-
- @Test
- void testValidJsonPathWithHyphen() {
- assertDoesNotThrow(() -> JsonArrayIdentifierExpression.of("attributes", "user-tags"));
- assertDoesNotThrow(() -> JsonArrayIdentifierExpression.of("attributes", "repo-urls"));
- }
-
- @Test
- void testValidJsonPathWithDot() {
- assertDoesNotThrow(() -> JsonArrayIdentifierExpression.of("attributes", "field.name"));
- assertDoesNotThrow(
- () -> JsonArrayIdentifierExpression.of("attributes", "user.preferences.tags"));
- }
-
- @Test
- void testInvalidJsonPathWithSqlInjection() {
- SecurityException exception =
- assertThrows(
- SecurityException.class,
- () -> JsonArrayIdentifierExpression.of("attributes", "tags' OR '1'='1"));
-
- assertTrue(exception.getMessage().contains("invalid characters"));
- }
-
- @Test
- void testInvalidJsonPathWithSemicolon() {
- SecurityException exception =
- assertThrows(
- SecurityException.class,
- () -> JsonArrayIdentifierExpression.of("attributes", "field; DROP"));
-
- assertTrue(exception.getMessage().contains("invalid characters"));
- }
-
- @Test
- void testJsonPathMaxDepthExceeded() {
- String[] deepPath = new String[101]; // Max is 100
- for (int i = 0; i < deepPath.length; i++) {
- deepPath[i] = "level" + i;
- }
-
- SecurityException exception =
- assertThrows(
- SecurityException.class,
- () -> JsonArrayIdentifierExpression.of("attributes", deepPath));
-
- assertTrue(exception.getMessage().contains("exceeds maximum depth"));
- }
-
- @Test
- void testJsonPathWithEmptyElement() {
- SecurityException exception =
- assertThrows(
- SecurityException.class,
- () -> JsonArrayIdentifierExpression.of("attributes", "nested", "", "field"));
-
- assertTrue(exception.getMessage().contains("null or empty"));
- }
-
- @Test
- void testMultipleInstancesWithSamePathAreEqual() {
- JsonArrayIdentifierExpression expr1 =
- JsonArrayIdentifierExpression.of("attributes", "certifications");
- JsonArrayIdentifierExpression expr2 =
- JsonArrayIdentifierExpression.of("attributes", "certifications");
- JsonArrayIdentifierExpression expr3 =
- JsonArrayIdentifierExpression.of("attributes", "certifications");
-
- assertEquals(expr1, expr2);
- assertEquals(expr2, expr3);
- assertEquals(expr1, expr3);
- assertEquals(expr1.hashCode(), expr2.hashCode());
- assertEquals(expr2.hashCode(), expr3.hashCode());
- }
-
- @Test
- void testNestedArrayPath() {
- JsonArrayIdentifierExpression expression =
- JsonArrayIdentifierExpression.of("attributes", "nested", "deep", "arrays");
-
- assertEquals("attributes", expression.getColumnName());
- assertEquals(List.of("nested", "deep", "arrays"), expression.getJsonPath());
- }
-}
diff --git a/document-store/src/test/java/org/hypertrace/core/documentstore/postgres/query/v1/parser/filter/PostgresInParserSelectorTest.java b/document-store/src/test/java/org/hypertrace/core/documentstore/postgres/query/v1/parser/filter/PostgresInParserSelectorTest.java
index 8f843a30..2bee6ea4 100644
--- a/document-store/src/test/java/org/hypertrace/core/documentstore/postgres/query/v1/parser/filter/PostgresInParserSelectorTest.java
+++ b/document-store/src/test/java/org/hypertrace/core/documentstore/postgres/query/v1/parser/filter/PostgresInParserSelectorTest.java
@@ -10,6 +10,7 @@
import org.hypertrace.core.documentstore.expression.impl.ConstantExpression;
import org.hypertrace.core.documentstore.expression.impl.FunctionExpression;
import org.hypertrace.core.documentstore.expression.impl.IdentifierExpression;
+import org.hypertrace.core.documentstore.expression.impl.JsonFieldType;
import org.hypertrace.core.documentstore.expression.impl.JsonIdentifierExpression;
import org.hypertrace.core.documentstore.expression.operators.AggregationOperator;
import org.hypertrace.core.documentstore.expression.operators.FunctionOperator;
@@ -40,10 +41,11 @@ void testVisitArrayIdentifierExpression_nestedCollection() {
@Test
void testVisitJsonIdentifierExpression() {
PostgresInParserSelector selector = new PostgresInParserSelector(true);
- JsonIdentifierExpression expr = JsonIdentifierExpression.of("customAttr", "field");
+ JsonIdentifierExpression expr =
+ JsonIdentifierExpression.of("customAttr", JsonFieldType.STRING, "field");
PostgresInRelationalFilterParserInterface result = selector.visit(expr);
assertNotNull(result);
- assertInstanceOf(PostgresInRelationalFilterParser.class, result);
+ assertInstanceOf(PostgresInRelationalFilterParserJsonPrimitive.class, result);
}
@Test
diff --git a/document-store/src/test/java/org/hypertrace/core/documentstore/postgres/query/v1/parser/filter/PostgresNotInParserSelectorTest.java b/document-store/src/test/java/org/hypertrace/core/documentstore/postgres/query/v1/parser/filter/PostgresNotInParserSelectorTest.java
index 20c4b668..418fa929 100644
--- a/document-store/src/test/java/org/hypertrace/core/documentstore/postgres/query/v1/parser/filter/PostgresNotInParserSelectorTest.java
+++ b/document-store/src/test/java/org/hypertrace/core/documentstore/postgres/query/v1/parser/filter/PostgresNotInParserSelectorTest.java
@@ -10,6 +10,7 @@
import org.hypertrace.core.documentstore.expression.impl.ConstantExpression;
import org.hypertrace.core.documentstore.expression.impl.FunctionExpression;
import org.hypertrace.core.documentstore.expression.impl.IdentifierExpression;
+import org.hypertrace.core.documentstore.expression.impl.JsonFieldType;
import org.hypertrace.core.documentstore.expression.impl.JsonIdentifierExpression;
import org.hypertrace.core.documentstore.expression.operators.AggregationOperator;
import org.hypertrace.core.documentstore.expression.operators.FunctionOperator;
@@ -40,10 +41,11 @@ void testVisitArrayIdentifierExpression_nestedCollection() {
@Test
void testVisitJsonIdentifierExpression() {
PostgresNotInParserSelector selector = new PostgresNotInParserSelector(true);
- JsonIdentifierExpression expr = JsonIdentifierExpression.of("customAttr", "field");
+ JsonIdentifierExpression expr =
+ JsonIdentifierExpression.of("customAttr", JsonFieldType.STRING, "field");
PostgresInRelationalFilterParserInterface result = selector.visit(expr);
assertNotNull(result);
- assertInstanceOf(PostgresInRelationalFilterParser.class, result);
+ assertInstanceOf(PostgresInRelationalFilterParserJsonPrimitive.class, result);
}
@Test