1818import org .hypertrace .core .documentstore .expression .impl .ArrayRelationalFilterExpression ;
1919import org .hypertrace .core .documentstore .expression .impl .ConstantExpression ;
2020import org .hypertrace .core .documentstore .expression .impl .DocumentArrayFilterExpression ;
21+ import org .hypertrace .core .documentstore .expression .impl .JsonIdentifierExpression ;
2122import org .hypertrace .core .documentstore .expression .impl .KeyExpression ;
2223import org .hypertrace .core .documentstore .expression .impl .LogicalExpression ;
2324import org .hypertrace .core .documentstore .expression .impl .RelationalExpression ;
@@ -169,22 +170,24 @@ private String getFilterStringForAnyOperator(final ArrayRelationalFilterExpressi
169170 boolean isFlatCollection =
170171 postgresQueryParser .getPgColTransformer ().getDocumentType () == DocumentType .FLAT ;
171172
173+ boolean isJsonbArray = expression .getArraySource () instanceof JsonIdentifierExpression ;
174+
172175 // Extract the field name
173176 final String identifierName =
174177 expression
175178 .getArraySource ()
176179 .accept (new PostgresIdentifierExpressionVisitor (postgresQueryParser ));
177180
178181 final String parsedLhs ;
179- if (isFlatCollection ) {
180- // For flat collections, assume all arrays are native PostgreSQL arrays
182+ if (isFlatCollection && ! isJsonbArray ) {
183+ // For flat collections with native arrays, use direct column reference
181184 parsedLhs = postgresQueryParser .transformField (identifierName ).getPgColumn ();
182185 } else {
183- // For nested collections, use JSONB path accessor
186+ // For nested collections OR JSONB arrays in flat collections , use JSONB path accessor
184187 // Convert 'elements' to planets->'elements' where planets could be an alias for an upper
185188 // level array filter
186189 // For the first time (if 'elements' was not under any nested array, say a top-level field),
187- // use the field identifier visitor to make it document->'elements'
190+ // use the field identifier visitor to make it document->'elements' or props->'colors'
188191 final PostgresIdentifierExpressionVisitor identifierVisitor =
189192 new PostgresIdentifierExpressionVisitor (postgresQueryParser );
190193 final PostgresSelectTypeExpressionVisitor arrayPathVisitor =
@@ -206,18 +209,18 @@ private String getFilterStringForAnyOperator(final ArrayRelationalFilterExpressi
206209 .getFilter ()
207210 .accept (new PostgresFilterTypeExpressionVisitor (postgresQueryParser , visitorProvider ));
208211
209- if (isFlatCollection ) {
212+ if (isFlatCollection && ! isJsonbArray ) {
210213 // todo: For array filters, UNNEST is not the most optimal way as it won't use the index.
211214 // Perhaps, we should use ANY or @> ARRAY operator
212215
213- // For flat collections, assume all arrays are native and use unnest()
216+ // For flat collections with native arrays (e.g., tags), use unnest()
214217 // Infer array type from filter to properly cast empty array
215218 String arrayTypeCast = inferArrayTypeCastFromFilter (expression .getFilter ());
216219 return String .format (
217220 "EXISTS (SELECT 1 FROM unnest(COALESCE(%s, ARRAY[]%s)) AS \" %s\" WHERE %s)" ,
218221 parsedLhs , arrayTypeCast , alias , parsedFilter );
219222 } else {
220- // For nested collections with JSONB arrays, use jsonb_array_elements()
223+ // For nested collections OR JSONB arrays in flat collections , use jsonb_array_elements()
221224 return String .format (
222225 "EXISTS (SELECT 1 FROM jsonb_array_elements(COALESCE(%s, '[]'::jsonb)) AS \" %s\" WHERE %s)" ,
223226 parsedLhs , alias , parsedFilter );
@@ -284,18 +287,20 @@ private String getFilterStringForAnyOperator(final DocumentArrayFilterExpression
284287 boolean isFlatCollection =
285288 postgresQueryParser .getPgColTransformer ().getDocumentType () == DocumentType .FLAT ;
286289
290+ boolean isJsonbArray = expression .getArraySource () instanceof JsonIdentifierExpression ;
291+
287292 // Extract the field name
288293 final String identifierName =
289294 expression
290295 .getArraySource ()
291296 .accept (new PostgresIdentifierExpressionVisitor (postgresQueryParser ));
292297
293298 final String parsedLhs ;
294- if (isFlatCollection ) {
295- // For flat collections, assume all arrays are native PostgreSQL arrays
296- // Use direct column reference with double quotes
299+ if (isFlatCollection && !isJsonbArray ) {
300+ // For flat collections with native arrays, use direct column reference with double quotes
297301 parsedLhs = postgresQueryParser .transformField (identifierName ).getPgColumn ();
298302 } else {
303+ // For nested collections OR JSONB arrays in flat collections, use JSONB path accessor
299304 final PostgresIdentifierExpressionVisitor identifierVisitor =
300305 new PostgresIdentifierExpressionVisitor (postgresQueryParser );
301306 final PostgresSelectTypeExpressionVisitor arrayPathVisitor =
@@ -316,16 +321,16 @@ private String getFilterStringForAnyOperator(final DocumentArrayFilterExpression
316321 .getFilter ()
317322 .accept (new PostgresFilterTypeExpressionVisitor (postgresQueryParser , wrapper ));
318323
319- if (isFlatCollection ) {
320- // For flat collections, assume all arrays are native and use unnest()
324+ if (isFlatCollection && ! isJsonbArray ) {
325+ // For flat collections with native arrays, use unnest()
321326 // Note: DocumentArrayFilterExpression typically works with JSONB arrays containing objects
322327 // For simplicity, we default to text[] type cast, though this may need refinement
323328 String arrayTypeCast = "::text[]" ;
324329 return String .format (
325330 "EXISTS (SELECT 1 FROM unnest(COALESCE(%s, ARRAY[]%s)) AS \" %s\" WHERE %s)" ,
326331 parsedLhs , arrayTypeCast , alias , parsedFilter );
327332 } else {
328- // For nested collections with JSONB arrays, use jsonb_array_elements()
333+ // For nested collections OR JSONB arrays in flat collections , use jsonb_array_elements()
329334 return String .format (
330335 "EXISTS (SELECT 1 FROM jsonb_array_elements(COALESCE(%s, '[]'::jsonb)) AS \" %s\" WHERE %s)" ,
331336 parsedLhs , alias , parsedFilter );
0 commit comments