Skip to content

Commit 571db94

Browse files
Merge branch 'main' into replace_in_with_any_for_perf
2 parents 201b442 + 78dc7c1 commit 571db94

30 files changed

+1346
-97
lines changed

document-store/src/integrationTest/java/org/hypertrace/core/documentstore/DocStoreQueryV1Test.java

Lines changed: 23 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,6 @@
9191
import org.hypertrace.core.documentstore.expression.impl.AliasedIdentifierExpression;
9292
import org.hypertrace.core.documentstore.expression.impl.ArrayIdentifierExpression;
9393
import org.hypertrace.core.documentstore.expression.impl.ArrayRelationalFilterExpression;
94-
import org.hypertrace.core.documentstore.expression.impl.ArrayType;
9594
import org.hypertrace.core.documentstore.expression.impl.ConstantExpression;
9695
import org.hypertrace.core.documentstore.expression.impl.FunctionExpression;
9796
import org.hypertrace.core.documentstore.expression.impl.IdentifierExpression;
@@ -4384,7 +4383,7 @@ void testInStringArray(String dataStoreName) throws JsonProcessingException {
43844383
.addSelection(ArrayIdentifierExpression.of("tags"))
43854384
.setFilter(
43864385
RelationalExpression.of(
4387-
ArrayIdentifierExpression.of("tags", ArrayType.TEXT),
4386+
ArrayIdentifierExpression.ofStrings("tags"),
43884387
IN,
43894388
ConstantExpression.ofStrings(List.of("hygiene", "grooming"))))
43904389
.build();
@@ -4436,7 +4435,7 @@ void testNotInStringArray(String dataStoreName) throws JsonProcessingException {
44364435
.addSelection(ArrayIdentifierExpression.of("tags"))
44374436
.setFilter(
44384437
RelationalExpression.of(
4439-
ArrayIdentifierExpression.of("tags", ArrayType.TEXT),
4438+
ArrayIdentifierExpression.ofStrings("tags"),
44404439
NOT_IN,
44414440
ConstantExpression.ofStrings(List.of("premium", "hygiene"))))
44424441
.build();
@@ -4484,7 +4483,7 @@ void testInIntArray(String dataStoreName) throws JsonProcessingException {
44844483
.addSelection(ArrayIdentifierExpression.of("numbers"))
44854484
.setFilter(
44864485
RelationalExpression.of(
4487-
ArrayIdentifierExpression.of("numbers", ArrayType.INTEGER),
4486+
ArrayIdentifierExpression.ofInts("numbers"),
44884487
IN,
44894488
ConstantExpression.ofNumbers(List.of(1, 10, 20))))
44904489
.build();
@@ -4531,7 +4530,7 @@ void testInDoubleArray(String dataStoreName) throws JsonProcessingException {
45314530
.addSelection(ArrayIdentifierExpression.of("scores"))
45324531
.setFilter(
45334532
RelationalExpression.of(
4534-
ArrayIdentifierExpression.of("scores", ArrayType.DOUBLE_PRECISION),
4533+
ArrayIdentifierExpression.ofDoubles("scores"),
45354534
IN,
45364535
ConstantExpression.ofNumbers(List.of(3.14, 5.0))))
45374536
.build();
@@ -4578,12 +4577,11 @@ void testInWithUnnest(String dataStoreName) {
45784577
.addSelection(ArrayIdentifierExpression.of("tags"))
45794578
.addFromClause(
45804579
UnnestExpression.of(
4581-
ArrayIdentifierExpression.of("tags", ArrayType.TEXT),
4582-
preserveNullAndEmptyArrays))
4580+
ArrayIdentifierExpression.ofStrings("tags"), preserveNullAndEmptyArrays))
45834581
// Should return unnested tag elements that match 'hygiene' OR 'grooming'
45844582
.setFilter(
45854583
RelationalExpression.of(
4586-
ArrayIdentifierExpression.of("tags", ArrayType.TEXT),
4584+
ArrayIdentifierExpression.ofStrings("tags"),
45874585
IN,
45884586
ConstantExpression.ofStrings(List.of("hygiene", "grooming"))))
45894587
.build();
@@ -4616,11 +4614,10 @@ void testNotInWithUnnest(String dataStoreName) {
46164614
.addSelection(ArrayIdentifierExpression.of("tags"))
46174615
.addFromClause(
46184616
UnnestExpression.of(
4619-
ArrayIdentifierExpression.of("tags", ArrayType.TEXT),
4620-
preserveNullAndEmptyArrays))
4617+
ArrayIdentifierExpression.ofStrings("tags"), preserveNullAndEmptyArrays))
46214618
.setFilter(
46224619
RelationalExpression.of(
4623-
ArrayIdentifierExpression.of("tags", ArrayType.TEXT),
4620+
ArrayIdentifierExpression.ofStrings("tags"),
46244621
NOT_IN,
46254622
ConstantExpression.ofStrings(List.of("hygiene", "grooming"))))
46264623
.build();
@@ -4652,16 +4649,15 @@ void testEmptyWithUnnest(String dataStoreName) {
46524649
Query.builder()
46534650
.addSelection(IdentifierExpression.of("item"))
46544651
.addSelection(ArrayIdentifierExpression.of("tags"))
4655-
.addFromClause(
4656-
UnnestExpression.of(ArrayIdentifierExpression.of("tags", ArrayType.TEXT), true))
4652+
.addFromClause(UnnestExpression.of(ArrayIdentifierExpression.ofStrings("tags"), true))
46574653
// Only include tags[] that are either NULL or empty (we have one row with NULL tag
46584654
// and one with empty tag. Unnest will result in two rows with NULL for
46594655
// "tags_unnested"). Note that this behavior will change with
46604656
// preserveNulLAndEmptyArrays = false. This is because unnest won't preserve those
46614657
// rows for which the unnested column is NULL then.
46624658
.setFilter(
46634659
RelationalExpression.of(
4664-
ArrayIdentifierExpression.of("tags", ArrayType.TEXT),
4660+
ArrayIdentifierExpression.ofStrings("tags"),
46654661
NOT_EXISTS,
46664662
ConstantExpression.of("null")))
46674663
.build();
@@ -4687,13 +4683,12 @@ void testNotEmptyWithUnnest(String dataStoreName) {
46874683
Query.builder()
46884684
.addSelection(IdentifierExpression.of("item"))
46894685
.addSelection(ArrayIdentifierExpression.of("tags"))
4690-
.addFromClause(
4691-
UnnestExpression.of(ArrayIdentifierExpression.of("tags", ArrayType.TEXT), true))
4686+
.addFromClause(UnnestExpression.of(ArrayIdentifierExpression.ofStrings("tags"), true))
46924687
// Only include tags[] that have at least 1 element, all rows with NULL or empty tags
46934688
// should be excluded.
46944689
.setFilter(
46954690
RelationalExpression.of(
4696-
ArrayIdentifierExpression.of("tags", ArrayType.TEXT),
4691+
ArrayIdentifierExpression.ofStrings("tags"),
46974692
EXISTS,
46984693
ConstantExpression.of("null")))
46994694
.build();
@@ -4718,11 +4713,10 @@ void testContainsStrArrayWithUnnest(String dataStoreName) {
47184713
Query query =
47194714
Query.builder()
47204715
.addSelection(IdentifierExpression.of("item"))
4721-
.addFromClause(
4722-
UnnestExpression.of(ArrayIdentifierExpression.of("tags", ArrayType.TEXT), true))
4716+
.addFromClause(UnnestExpression.of(ArrayIdentifierExpression.ofStrings("tags"), true))
47234717
.setFilter(
47244718
RelationalExpression.of(
4725-
ArrayIdentifierExpression.of("tags", ArrayType.TEXT),
4719+
ArrayIdentifierExpression.ofStrings("tags"),
47264720
CONTAINS,
47274721
ConstantExpression.ofStrings(List.of("hygiene", "premium"))))
47284722
.build();
@@ -4745,10 +4739,10 @@ void testContainsStrArray(String dataStoreName) throws JsonProcessingException {
47454739
Query query =
47464740
Query.builder()
47474741
.addSelection(IdentifierExpression.of("item"))
4748-
.addSelection(ArrayIdentifierExpression.of("tags", ArrayType.TEXT))
4742+
.addSelection(ArrayIdentifierExpression.ofStrings("tags"))
47494743
.setFilter(
47504744
RelationalExpression.of(
4751-
ArrayIdentifierExpression.of("tags", ArrayType.TEXT),
4745+
ArrayIdentifierExpression.ofStrings("tags"),
47524746
CONTAINS,
47534747
ConstantExpression.ofStrings(List.of("hygiene", "personal-care"))))
47544748
.build();
@@ -4797,7 +4791,7 @@ void testNotContainsStrArray(String dataStoreName) throws JsonProcessingExceptio
47974791
.addSelection(ArrayIdentifierExpression.of("tags"))
47984792
.setFilter(
47994793
RelationalExpression.of(
4800-
ArrayIdentifierExpression.of("tags", ArrayType.TEXT),
4794+
ArrayIdentifierExpression.ofStrings("tags"),
48014795
NOT_CONTAINS,
48024796
ConstantExpression.ofStrings(List.of("hair-care", "personal-care"))))
48034797
.build();
@@ -4841,10 +4835,10 @@ void testContainsOnIntArray(String dataStoreName) throws JsonProcessingException
48414835
Query query =
48424836
Query.builder()
48434837
.addSelection(IdentifierExpression.of("item"))
4844-
.addSelection(ArrayIdentifierExpression.of("numbers", ArrayType.INTEGER))
4838+
.addSelection(ArrayIdentifierExpression.ofInts("numbers"))
48454839
.setFilter(
48464840
RelationalExpression.of(
4847-
ArrayIdentifierExpression.of("numbers", ArrayType.INTEGER),
4841+
ArrayIdentifierExpression.ofInts("numbers"),
48484842
CONTAINS,
48494843
ConstantExpression.ofNumbers(List.of(1, 2))))
48504844
.build();
@@ -4888,10 +4882,10 @@ void testNotContainsOnIntArray(String dataStoreName) throws JsonProcessingExcept
48884882
Query query =
48894883
Query.builder()
48904884
.addSelection(IdentifierExpression.of("item"))
4891-
.addSelection(ArrayIdentifierExpression.of("numbers", ArrayType.INTEGER))
4885+
.addSelection(ArrayIdentifierExpression.ofInts("numbers"))
48924886
.setFilter(
48934887
RelationalExpression.of(
4894-
ArrayIdentifierExpression.of("numbers", ArrayType.INTEGER),
4888+
ArrayIdentifierExpression.ofInts("numbers"),
48954889
NOT_CONTAINS,
48964890
ConstantExpression.ofNumbers(List.of(10, 20))))
48974891
.build();
@@ -4937,10 +4931,10 @@ void testContainsOnDoubleArray(String dataStoreName) throws JsonProcessingExcept
49374931
Query query =
49384932
Query.builder()
49394933
.addSelection(IdentifierExpression.of("item"))
4940-
.addSelection(ArrayIdentifierExpression.of("scores", ArrayType.DOUBLE_PRECISION))
4934+
.addSelection(ArrayIdentifierExpression.ofDoubles("scores"))
49414935
.setFilter(
49424936
RelationalExpression.of(
4943-
ArrayIdentifierExpression.of("scores", ArrayType.DOUBLE_PRECISION),
4937+
ArrayIdentifierExpression.ofDoubles("scores"),
49444938
CONTAINS,
49454939
ConstantExpression.ofNumbers(List.of(3.14, 2.71))))
49464940
.build();

document-store/src/main/java/org/hypertrace/core/documentstore/expression/impl/AliasedIdentifierExpression.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package org.hypertrace.core.documentstore.expression.impl;
22

33
import com.google.common.base.Preconditions;
4+
import lombok.EqualsAndHashCode;
45
import lombok.Value;
56
import org.hypertrace.core.documentstore.parser.SelectTypeExpressionVisitor;
67

@@ -20,6 +21,7 @@
2021
* </code> the rhs of the join condition "latest.item" can be expressed as: <code>
2122
* AliasedIdentifierExpression.builder().name("item").alias("alias1").build() </code>
2223
*/
24+
@EqualsAndHashCode(callSuper = true)
2325
@Value
2426
public class AliasedIdentifierExpression extends IdentifierExpression {
2527
String contextAlias;

document-store/src/main/java/org/hypertrace/core/documentstore/expression/impl/ArrayIdentifierExpression.java

Lines changed: 48 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
package org.hypertrace.core.documentstore.expression.impl;
22

3-
import java.util.Optional;
43
import lombok.EqualsAndHashCode;
54
import org.hypertrace.core.documentstore.parser.SelectTypeExpressionVisitor;
65

@@ -14,28 +13,66 @@
1413
@EqualsAndHashCode(callSuper = true)
1514
public class ArrayIdentifierExpression extends IdentifierExpression {
1615

17-
private final ArrayType arrayType;
16+
private final DataType arrayElementType;
1817

19-
public ArrayIdentifierExpression(String name) {
20-
this(name, null);
18+
ArrayIdentifierExpression(String name) {
19+
this(name, DataType.UNSPECIFIED);
2120
}
2221

23-
public ArrayIdentifierExpression(String name, ArrayType arrayType) {
22+
ArrayIdentifierExpression(String name, DataType arrayElementType) {
2423
super(name);
25-
this.arrayType = arrayType;
24+
this.arrayElementType = arrayElementType;
2625
}
2726

2827
public static ArrayIdentifierExpression of(String name) {
2928
return new ArrayIdentifierExpression(name);
3029
}
3130

32-
public static ArrayIdentifierExpression of(String name, ArrayType arrayType) {
33-
return new ArrayIdentifierExpression(name, arrayType);
31+
static ArrayIdentifierExpression of(String name, DataType arrayElementType) {
32+
return new ArrayIdentifierExpression(name, arrayElementType);
3433
}
3534

36-
/** Returns the array type if specified, empty otherwise */
37-
public Optional<ArrayType> getArrayType() {
38-
return Optional.ofNullable(arrayType);
35+
public static ArrayIdentifierExpression ofStrings(final String name) {
36+
return of(name, DataType.STRING);
37+
}
38+
39+
public static ArrayIdentifierExpression ofInts(final String name) {
40+
return of(name, DataType.INTEGER);
41+
}
42+
43+
public static ArrayIdentifierExpression ofLongs(final String name) {
44+
return of(name, DataType.LONG);
45+
}
46+
47+
public static ArrayIdentifierExpression ofFloats(final String name) {
48+
return of(name, DataType.FLOAT);
49+
}
50+
51+
public static ArrayIdentifierExpression ofDoubles(final String name) {
52+
return of(name, DataType.DOUBLE);
53+
}
54+
55+
public static ArrayIdentifierExpression ofBooleans(final String name) {
56+
return of(name, DataType.BOOLEAN);
57+
}
58+
59+
public static ArrayIdentifierExpression ofTimestampsTz(final String name) {
60+
return of(name, DataType.TIMESTAMPTZ);
61+
}
62+
63+
public static ArrayIdentifierExpression ofDates(final String name) {
64+
return of(name, DataType.DATE);
65+
}
66+
67+
/**
68+
* Returns the data type of array elements.
69+
*
70+
* <p>This is used by database-specific type extractors to generate appropriate type casts.
71+
*
72+
* @return The element DataType (UNSPECIFIED if no type was explicitly set)
73+
*/
74+
public DataType getElementDataType() {
75+
return arrayElementType;
3976
}
4077

4178
/**

document-store/src/main/java/org/hypertrace/core/documentstore/expression/impl/ArrayType.java

Lines changed: 0 additions & 16 deletions
This file was deleted.
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
package org.hypertrace.core.documentstore.expression.impl;
2+
3+
/**
4+
* Database-agnostic data types for explicit type annotation in queries.
5+
*
6+
* <p>This enum provides type metadata for {@link IdentifierExpression} and {@link
7+
* ArrayIdentifierExpression} fields in flat collections, enabling type-safe query generation
8+
* without runtime type inference.
9+
*
10+
* <p>These types are mapped to database-specific types at query parsing time. For example, when
11+
* generating PostgreSQL queries, {@code STRING} maps to {@code text}, {@code INTEGER} maps to
12+
* {@code int4}, etc.
13+
*
14+
* @see ArrayIdentifierExpression
15+
* @see IdentifierExpression
16+
*/
17+
public enum DataType {
18+
UNSPECIFIED,
19+
STRING,
20+
INTEGER,
21+
LONG,
22+
FLOAT,
23+
DOUBLE,
24+
BOOLEAN,
25+
// timestamp with time-zone information. For example: 2004-10-19 10:23:54+02.
26+
// For more info, see: https://www.postgresql.org/docs/current/datatype-datetime.html
27+
TIMESTAMPTZ,
28+
DATE
29+
}

0 commit comments

Comments
 (0)