Skip to content

Commit 78dc7c1

Browse files
authored
Introduce Flat Collection Types (#259)
1 parent 4516200 commit 78dc7c1

23 files changed

+904
-338
lines changed

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

Lines changed: 293 additions & 270 deletions
Large diffs are not rendered by default.

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+
}

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

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,11 @@
1717
* Expression representing either an identifier/column name
1818
*
1919
* <p>Example: IdentifierExpression.of("col1");
20+
*
21+
* <p>For flat relational collections, you can optionally provide a {@link DataType} to enable
22+
* type-safe query generation without runtime type inference:
23+
*
24+
* <p>Example: IdentifierExpression.ofInt("price");
2025
*/
2126
@Value
2227
@NonFinal
@@ -25,12 +30,59 @@ public class IdentifierExpression
2530
implements GroupTypeExpression, SelectTypeExpression, SortTypeExpression {
2631

2732
String name;
33+
// Type information of this identifier for flat collections, this is optional to maintain backward
34+
// compatibility
35+
DataType dataType;
36+
37+
IdentifierExpression(String name) {
38+
this.name = name;
39+
this.dataType = DataType.UNSPECIFIED;
40+
}
2841

2942
public static IdentifierExpression of(final String name) {
3043
Preconditions.checkArgument(name != null && !name.isBlank(), "name is null or blank");
3144
return new IdentifierExpression(name);
3245
}
3346

47+
static IdentifierExpression of(final String name, final DataType dataType) {
48+
Preconditions.checkArgument(name != null && !name.isBlank(), "name is null or blank");
49+
return new IdentifierExpression(name, dataType);
50+
}
51+
52+
public static IdentifierExpression ofString(final String name) {
53+
return of(name, DataType.STRING);
54+
}
55+
56+
public static IdentifierExpression ofInt(final String name) {
57+
return of(name, DataType.INTEGER);
58+
}
59+
60+
public static IdentifierExpression ofLong(final String name) {
61+
return of(name, DataType.LONG);
62+
}
63+
64+
public static IdentifierExpression ofFloat(final String name) {
65+
return of(name, DataType.FLOAT);
66+
}
67+
68+
public static IdentifierExpression ofDouble(final String name) {
69+
return of(name, DataType.DOUBLE);
70+
}
71+
72+
public static IdentifierExpression ofBoolean(final String name) {
73+
return of(name, DataType.BOOLEAN);
74+
}
75+
76+
// Timestamp with time-zone information. For example: 2004-10-19 10:23:54+02. For more info, see:
77+
// https://www.postgresql.org/docs/current/datatype-datetime.html
78+
public static IdentifierExpression ofTimestampTz(final String name) {
79+
return of(name, DataType.TIMESTAMPTZ);
80+
}
81+
82+
public static IdentifierExpression ofDate(final String name) {
83+
return of(name, DataType.DATE);
84+
}
85+
3486
@Override
3587
public <T> T accept(final GroupTypeExpressionVisitor visitor) {
3688
return visitor.visit(this);

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,5 +9,6 @@ public enum JsonFieldType {
99
NUMBER_ARRAY,
1010
BOOLEAN_ARRAY,
1111
OBJECT_ARRAY,
12-
OBJECT
12+
OBJECT,
13+
UNSPECIFIED
1314
}

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ public class JsonIdentifierExpression extends IdentifierExpression {
2121

2222
String columnName; // e.g., "customAttr" (the top-level JSONB column)
2323
List<String> jsonPath; // e.g., ["myAttribute", "nestedField"]
24-
JsonFieldType fieldType; // Optional: PRIMITIVE or ARRAY for optimization
24+
JsonFieldType fieldType;
2525

2626
public static JsonIdentifierExpression of(final String columnName) {
2727
throw new IllegalArgumentException(

document-store/src/main/java/org/hypertrace/core/documentstore/postgres/query/v1/parser/filter/nonjson/field/PostgresArrayTypeExtractor.java

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
import org.hypertrace.core.documentstore.expression.impl.AggregateExpression;
44
import org.hypertrace.core.documentstore.expression.impl.AliasedIdentifierExpression;
55
import org.hypertrace.core.documentstore.expression.impl.ArrayIdentifierExpression;
6-
import org.hypertrace.core.documentstore.expression.impl.ArrayType;
76
import org.hypertrace.core.documentstore.expression.impl.ConstantExpression;
87
import org.hypertrace.core.documentstore.expression.impl.ConstantExpression.DocumentConstantExpression;
98
import org.hypertrace.core.documentstore.expression.impl.FunctionExpression;
@@ -21,9 +20,8 @@
2120
* <p>Returns:
2221
*
2322
* <ul>
24-
* <li>The PostgreSQL array type string (e.g., "text[]", "integer[]") if {@link ArrayType} is
25-
* specified
26-
* <li>{@code null} if {@link ArrayIdentifierExpression} is used without an explicit type
23+
* <li>The PostgreSQL array type string (e.g., "text[]", "integer[]")
24+
* <li>{@code null} if {@link ArrayIdentifierExpression} has UNSPECIFIED type
2725
* </ul>
2826
*/
2927
public class PostgresArrayTypeExtractor implements SelectTypeExpressionVisitor {
@@ -32,7 +30,8 @@ public PostgresArrayTypeExtractor() {}
3230

3331
@Override
3432
public String visit(ArrayIdentifierExpression expression) {
35-
return expression.getArrayType().map(ArrayType::getPostgresType).orElse(null);
33+
PostgresDataType pgType = PostgresDataType.fromDataType(expression.getElementDataType());
34+
return pgType == PostgresDataType.UNKNOWN ? null : pgType.getArraySqlType();
3635
}
3736

3837
@Override
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
package org.hypertrace.core.documentstore.postgres.query.v1.parser.filter.nonjson.field;
2+
3+
import org.hypertrace.core.documentstore.expression.impl.DataType;
4+
5+
/**
6+
* PostgreSQL-specific data types with their SQL type strings.
7+
*
8+
* <p>This enum maps generic {@link DataType} values to PostgreSQL-specific type strings used in SQL
9+
* queries for type casting.
10+
*/
11+
public enum PostgresDataType {
12+
TEXT("text"),
13+
INTEGER("integer"),
14+
BIGINT("bigint"),
15+
REAL("real"),
16+
DOUBLE_PRECISION("double precision"),
17+
BOOLEAN("boolean"),
18+
TIMESTAMPTZ("timestamptz"),
19+
DATE("date"),
20+
UNKNOWN("unknown");
21+
22+
private final String sqlType;
23+
24+
PostgresDataType(String sqlType) {
25+
this.sqlType = sqlType;
26+
}
27+
28+
public String getSqlType() {
29+
return sqlType;
30+
}
31+
32+
public String getArraySqlType() {
33+
return sqlType + "[]";
34+
}
35+
36+
/**
37+
* Maps a generic DataType to its PostgreSQL equivalent.
38+
*
39+
* @param dataType the generic data type
40+
* @return the corresponding PostgresDataType, or null if UNSPECIFIED
41+
* @throws IllegalArgumentException if the DataType is unknown
42+
*/
43+
public static PostgresDataType fromDataType(DataType dataType) {
44+
switch (dataType) {
45+
case UNSPECIFIED:
46+
return UNKNOWN;
47+
case STRING:
48+
return TEXT;
49+
case INTEGER:
50+
return INTEGER;
51+
case LONG:
52+
return BIGINT;
53+
case FLOAT:
54+
return REAL;
55+
case DOUBLE:
56+
return DOUBLE_PRECISION;
57+
case BOOLEAN:
58+
return BOOLEAN;
59+
case TIMESTAMPTZ:
60+
return TIMESTAMPTZ;
61+
case DATE:
62+
return DATE;
63+
default:
64+
throw new IllegalArgumentException("Unknown DataType: " + dataType);
65+
}
66+
}
67+
}

0 commit comments

Comments
 (0)