From fe6cff4eb538f7ec0a036344e5cc89224cfe2a2c Mon Sep 17 00:00:00 2001 From: Ronak Date: Tue, 9 Aug 2022 18:26:37 +0530 Subject: [PATCH 1/7] feat: add selection support in older api --- .../documentstore/postgres/PostgresCollection.java | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/document-store/src/main/java/org/hypertrace/core/documentstore/postgres/PostgresCollection.java b/document-store/src/main/java/org/hypertrace/core/documentstore/postgres/PostgresCollection.java index 10b45926..30a84e2b 100644 --- a/document-store/src/main/java/org/hypertrace/core/documentstore/postgres/PostgresCollection.java +++ b/document-store/src/main/java/org/hypertrace/core/documentstore/postgres/PostgresCollection.java @@ -325,7 +325,9 @@ public BulkUpdateResult bulkOperationOnArrayValue(BulkArrayValueUpdateRequest re @Override public CloseableIterator search(Query query) { String filters = null; - StringBuilder sqlBuilder = new StringBuilder("SELECT * FROM ").append(collectionName); + String selection = prepareSelections(query); + StringBuilder sqlBuilder = + new StringBuilder(String.format("SELECT %s FROM ", selection)).append(collectionName); Params.Builder paramsBuilder = Params.newBuilder(); // If there is a filter in the query, parse it fully. @@ -366,6 +368,14 @@ public CloseableIterator search(Query query) { return EMPTY_ITERATOR; } + private String prepareSelections(Query query) { + List selections = query.getSelections(); + if (selections.isEmpty()) return "*"; + return selections.stream() + .map(selection -> PostgresUtils.prepareFieldAccessorExpr(selection, "document")) + .collect(Collectors.joining(",")); + } + @Override public CloseableIterator find( final org.hypertrace.core.documentstore.query.Query query) { From 480a506903fb3bc84b4468bf9edf14f01de4eebe Mon Sep 17 00:00:00 2001 From: Ronak Date: Tue, 9 Aug 2022 18:47:16 +0530 Subject: [PATCH 2/7] fixed the bulk array update internal query --- .../core/documentstore/postgres/PostgresCollection.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/document-store/src/main/java/org/hypertrace/core/documentstore/postgres/PostgresCollection.java b/document-store/src/main/java/org/hypertrace/core/documentstore/postgres/PostgresCollection.java index 30a84e2b..c5220a45 100644 --- a/document-store/src/main/java/org/hypertrace/core/documentstore/postgres/PostgresCollection.java +++ b/document-store/src/main/java/org/hypertrace/core/documentstore/postgres/PostgresCollection.java @@ -750,7 +750,7 @@ private Optional getCreatedTime(Key key) throws IOException { private CloseableIterator searchDocsForKeys(Set keys) { List keysAsStr = keys.stream().map(Key::toString).collect(Collectors.toList()); Query query = - new Query().withSelection("*").withFilter(new Filter(Filter.Op.IN, ID, keysAsStr)); + new Query().withFilter(new Filter(Filter.Op.IN, ID, keysAsStr)); return search(query); } From d56c56f6d2be7e248aecac0fb89dc3a236c66c22 Mon Sep 17 00:00:00 2001 From: Ronak Date: Tue, 9 Aug 2022 19:19:23 +0530 Subject: [PATCH 3/7] fixed closable iterator issue --- .../postgres/PostgresCollection.java | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/document-store/src/main/java/org/hypertrace/core/documentstore/postgres/PostgresCollection.java b/document-store/src/main/java/org/hypertrace/core/documentstore/postgres/PostgresCollection.java index c5220a45..3740f9ff 100644 --- a/document-store/src/main/java/org/hypertrace/core/documentstore/postgres/PostgresCollection.java +++ b/document-store/src/main/java/org/hypertrace/core/documentstore/postgres/PostgresCollection.java @@ -359,8 +359,13 @@ public CloseableIterator search(Query query) { try { PreparedStatement preparedStatement = buildPreparedStatement(sqlBuilder.toString(), paramsBuilder.build()); + LOGGER.warn("Executing search query:{}", preparedStatement.toString()); ResultSet resultSet = preparedStatement.executeQuery(); - return new PostgresResultIterator(resultSet); + CloseableIterator closeableIterator = + query.getSelections().size() > 0 + ? new PostgresResultIteratorWithMetaData(resultSet) + : new PostgresResultIterator(resultSet); + return closeableIterator; } catch (SQLException e) { LOGGER.error("SQLException querying documents. query: {}", query, e); } @@ -372,7 +377,11 @@ private String prepareSelections(Query query) { List selections = query.getSelections(); if (selections.isEmpty()) return "*"; return selections.stream() - .map(selection -> PostgresUtils.prepareFieldAccessorExpr(selection, "document")) + .map( + selection -> + String.format( + "%s AS \"%s\"", + PostgresUtils.prepareFieldAccessorExpr(selection, "document"), selection)) .collect(Collectors.joining(",")); } @@ -749,8 +758,7 @@ private Optional getCreatedTime(Key key) throws IOException { private CloseableIterator searchDocsForKeys(Set keys) { List keysAsStr = keys.stream().map(Key::toString).collect(Collectors.toList()); - Query query = - new Query().withFilter(new Filter(Filter.Op.IN, ID, keysAsStr)); + Query query = new Query().withFilter(new Filter(Filter.Op.IN, ID, keysAsStr)); return search(query); } @@ -788,6 +796,7 @@ private CloseableIterator executeQueryV1( try { PreparedStatement preparedStatement = buildPreparedStatement(sqlQuery, queryParser.getParamsBuilder().build()); + LOGGER.warn("Executing executeQueryV1 query:{}", preparedStatement.toString()); ResultSet resultSet = preparedStatement.executeQuery(); CloseableIterator closeableIterator = query.getSelections().size() > 0 From e4f987a1749a670c15df1819c0b76c6814467b9f Mon Sep 17 00:00:00 2001 From: Ronak Date: Wed, 10 Aug 2022 12:59:06 +0530 Subject: [PATCH 4/7] refactored common method, and fixed handling of outer column --- .../postgres/PostgresQueryParser.java | 67 ++++++------------- .../postgres/utils/PostgresUtils.java | 14 ++-- 2 files changed, 25 insertions(+), 56 deletions(-) diff --git a/document-store/src/main/java/org/hypertrace/core/documentstore/postgres/PostgresQueryParser.java b/document-store/src/main/java/org/hypertrace/core/documentstore/postgres/PostgresQueryParser.java index 1e002fd9..2d22ef1c 100644 --- a/document-store/src/main/java/org/hypertrace/core/documentstore/postgres/PostgresQueryParser.java +++ b/document-store/src/main/java/org/hypertrace/core/documentstore/postgres/PostgresQueryParser.java @@ -2,8 +2,6 @@ import static org.hypertrace.core.documentstore.Collection.UNSUPPORTED_QUERY_OPERATION; import static org.hypertrace.core.documentstore.postgres.PostgresCollection.CREATED_AT; -import static org.hypertrace.core.documentstore.postgres.PostgresCollection.DOCUMENT; -import static org.hypertrace.core.documentstore.postgres.PostgresCollection.DOC_PATH_SEPARATOR; import static org.hypertrace.core.documentstore.postgres.PostgresCollection.ID; import static org.hypertrace.core.documentstore.postgres.PostgresCollection.UPDATED_AT; @@ -16,6 +14,7 @@ import org.hypertrace.core.documentstore.Filter; import org.hypertrace.core.documentstore.OrderBy; import org.hypertrace.core.documentstore.postgres.Params.Builder; +import org.hypertrace.core.documentstore.postgres.utils.PostgresUtils; class PostgresQueryParser { @@ -45,7 +44,10 @@ static String parseNonCompositeFilter(Filter filter, Builder paramsBuilder) { Filter.Op op = filter.getOp(); Object value = filter.getValue(); String fieldName = filter.getFieldName(); - String fullFieldName = prepareCast(prepareFieldDataAccessorExpr(fieldName), value); + String fullFieldName = + prepareCast( + PostgresUtils.prepareFieldDataAccessorExpr(fieldName, PostgresUtils.DOCUMENT_COLUMN), + value); StringBuilder filterString = new StringBuilder(fullFieldName); String sqlOperator; Boolean isMultiValued = false; @@ -79,8 +81,9 @@ static String parseNonCompositeFilter(Filter filter, Builder paramsBuilder) { // Ref in context of NEQ - // https://github.com/hypertrace/document-store/pull/20#discussion_r547101520Other // so, we need - "document->key IS NULL OR document->key->> NOT IN (v1, v2)" - StringBuilder notInFilterString = prepareFieldAccessorExpr(fieldName); - if (notInFilterString != null) { + StringBuilder notInFilterString = + PostgresUtils.prepareFieldAccessorExpr(fieldName, PostgresUtils.DOCUMENT_COLUMN); + if (notInFilterString != null && !OUTER_COLUMNS.contains(fieldName)) { filterString = notInFilterString.append(" IS NULL OR ").append(fullFieldName); } sqlOperator = " NOT IN "; @@ -98,8 +101,9 @@ static String parseNonCompositeFilter(Filter filter, Builder paramsBuilder) { sqlOperator = " IS NULL "; value = null; // For fields inside jsonb - StringBuilder notExists = prepareFieldAccessorExpr(fieldName); - if (notExists != null) { + StringBuilder notExists = + PostgresUtils.prepareFieldAccessorExpr(fieldName, PostgresUtils.DOCUMENT_COLUMN); + if (notExists != null && !OUTER_COLUMNS.contains(fieldName)) { filterString = notExists; } break; @@ -107,8 +111,9 @@ static String parseNonCompositeFilter(Filter filter, Builder paramsBuilder) { sqlOperator = " IS NOT NULL "; value = null; // For fields inside jsonb - StringBuilder exists = prepareFieldAccessorExpr(fieldName); - if (exists != null) { + StringBuilder exists = + PostgresUtils.prepareFieldAccessorExpr(fieldName, PostgresUtils.DOCUMENT_COLUMN); + if (exists != null && !OUTER_COLUMNS.contains(fieldName)) { filterString = exists; } break; @@ -121,9 +126,10 @@ static String parseNonCompositeFilter(Filter filter, Builder paramsBuilder) { // Semantics for handling if key not exists and if it exists, its value // doesn't equal to the filter for Jsonb document will be done as: // "document->key IS NULL OR document->key->> != value" - StringBuilder notEquals = prepareFieldAccessorExpr(fieldName); + StringBuilder notEquals = + PostgresUtils.prepareFieldAccessorExpr(fieldName, PostgresUtils.DOCUMENT_COLUMN); // For fields inside jsonb - if (notEquals != null) { + if (notEquals != null && !OUTER_COLUMNS.contains(fieldName)) { filterString = notEquals.append(" IS NULL OR ").append(fullFieldName); } break; @@ -179,7 +185,8 @@ static String parseOrderBys(List orderBys) { return orderBys.stream() .map( orderBy -> - prepareFieldDataAccessorExpr(orderBy.getField()) + PostgresUtils.prepareFieldDataAccessorExpr( + orderBy.getField(), PostgresUtils.DOCUMENT_COLUMN) + " " + (orderBy.isAsc() ? "ASC" : "DESC")) .filter(str -> !StringUtils.isEmpty(str)) @@ -199,42 +206,6 @@ private static String prepareParameterizedStringForList( return "(" + collect + ")"; } - private static StringBuilder prepareFieldAccessorExpr(String fieldName) { - // Generate json field accessor statement - if (!OUTER_COLUMNS.contains(fieldName)) { - StringBuilder filterString = new StringBuilder(DOCUMENT); - String[] nestedFields = fieldName.split(DOC_PATH_SEPARATOR); - for (String nestedField : nestedFields) { - filterString.append(JSON_FIELD_ACCESSOR).append("'").append(nestedField).append("'"); - } - return filterString; - } - // Field accessor is only applicable to jsonb fields, return null otherwise - return null; - } - - /** - * Add field prefix for searching into json document based on postgres syntax, handles nested - * keys. Note: It doesn't handle array elements in json document. e.g SELECT * FROM TABLE where - * document ->> 'first' = 'name' and document -> 'address' ->> 'pin' = "00000" - */ - private static String prepareFieldDataAccessorExpr(String fieldName) { - StringBuilder fieldPrefix = new StringBuilder(fieldName); - if (!OUTER_COLUMNS.contains(fieldName)) { - fieldPrefix = new StringBuilder(DOCUMENT); - String[] nestedFields = fieldName.split(DOC_PATH_SEPARATOR); - for (int i = 0; i < nestedFields.length - 1; i++) { - fieldPrefix.append(JSON_FIELD_ACCESSOR).append("'").append(nestedFields[i]).append("'"); - } - fieldPrefix - .append(JSON_DATA_ACCESSOR) - .append("'") - .append(nestedFields[nestedFields.length - 1]) - .append("'"); - } - return fieldPrefix.toString(); - } - private static String prepareCast(String field, Object value) { String fmt = "CAST (%s AS %s)"; diff --git a/document-store/src/main/java/org/hypertrace/core/documentstore/postgres/utils/PostgresUtils.java b/document-store/src/main/java/org/hypertrace/core/documentstore/postgres/utils/PostgresUtils.java index 9448f322..072fd4d7 100644 --- a/document-store/src/main/java/org/hypertrace/core/documentstore/postgres/utils/PostgresUtils.java +++ b/document-store/src/main/java/org/hypertrace/core/documentstore/postgres/utils/PostgresUtils.java @@ -47,10 +47,8 @@ public static StringBuilder prepareFieldAccessorExpr(String fieldName, String co } return filterString; } - // Field accessor is only applicable to jsonb fields, return null otherwise - LOGGER.warn( - "Returning null string for field name {} and column name {}", fieldName, columnName); - return null; + // There is no need of field accessor in case of outer column. + return new StringBuilder(fieldName); } /** @@ -187,7 +185,7 @@ public static String parseNonCompositeFilter( // https://github.com/hypertrace/document-store/pull/20#discussion_r547101520Other // so, we need - "document->key IS NULL OR document->key->> NOT IN (v1, v2)" StringBuilder notInFilterString = prepareFieldAccessorExpr(fieldName, columnName); - if (notInFilterString != null) { + if (notInFilterString != null && !OUTER_COLUMNS.contains(fieldName)) { filterString = notInFilterString.append(" IS NULL OR ").append(fullFieldName); } sqlOperator = " NOT IN "; @@ -206,7 +204,7 @@ public static String parseNonCompositeFilter( value = null; // For fields inside jsonb StringBuilder notExists = prepareFieldAccessorExpr(fieldName, columnName); - if (notExists != null) { + if (notExists != null && !OUTER_COLUMNS.contains(fieldName)) { filterString = notExists; } break; @@ -215,7 +213,7 @@ public static String parseNonCompositeFilter( value = null; // For fields inside jsonb StringBuilder exists = prepareFieldAccessorExpr(fieldName, columnName); - if (exists != null) { + if (exists != null && !OUTER_COLUMNS.contains(fieldName)) { filterString = exists; } break; @@ -230,7 +228,7 @@ public static String parseNonCompositeFilter( // "document->key IS NULL OR document->key->> != value" StringBuilder notEquals = prepareFieldAccessorExpr(fieldName, columnName); // For fields inside jsonb - if (notEquals != null) { + if (notEquals != null && !OUTER_COLUMNS.contains(fieldName)) { filterString = notEquals.append(" IS NULL OR ").append(fullFieldName); } break; From d720c82c49d56ffd6f791d258ec5f52c932d02cd Mon Sep 17 00:00:00 2001 From: Ronak Date: Wed, 10 Aug 2022 15:12:06 +0530 Subject: [PATCH 5/7] refactor: clean up and removed duplicate logic --- .../postgres/PostgresCollection.java | 27 +-- .../postgres/PostgresQueryParser.java | 190 ++---------------- .../postgres/utils/PostgresUtils.java | 3 - .../postgres/PostgresQueryParserTest.java | 99 ++++++--- 4 files changed, 103 insertions(+), 216 deletions(-) diff --git a/document-store/src/main/java/org/hypertrace/core/documentstore/postgres/PostgresCollection.java b/document-store/src/main/java/org/hypertrace/core/documentstore/postgres/PostgresCollection.java index 3740f9ff..e92c1900 100644 --- a/document-store/src/main/java/org/hypertrace/core/documentstore/postgres/PostgresCollection.java +++ b/document-store/src/main/java/org/hypertrace/core/documentstore/postgres/PostgresCollection.java @@ -324,10 +324,11 @@ public BulkUpdateResult bulkOperationOnArrayValue(BulkArrayValueUpdateRequest re @Override public CloseableIterator search(Query query) { - String filters = null; - String selection = prepareSelections(query); + String selection = PostgresQueryParser.parseSelections(query.getSelections()); StringBuilder sqlBuilder = new StringBuilder(String.format("SELECT %s FROM ", selection)).append(collectionName); + + String filters = null; Params.Builder paramsBuilder = Params.newBuilder(); // If there is a filter in the query, parse it fully. @@ -335,8 +336,6 @@ public CloseableIterator search(Query query) { filters = PostgresQueryParser.parseFilter(query.getFilter(), paramsBuilder); } - LOGGER.debug("Sending query to PostgresSQL: {} : {}", collectionName, filters); - if (filters != null) { sqlBuilder.append(" WHERE ").append(filters); } @@ -356,10 +355,11 @@ public CloseableIterator search(Query query) { sqlBuilder.append(" OFFSET ").append(offset); } + String pgSqlQuery = sqlBuilder.toString(); try { PreparedStatement preparedStatement = - buildPreparedStatement(sqlBuilder.toString(), paramsBuilder.build()); - LOGGER.warn("Executing search query:{}", preparedStatement.toString()); + buildPreparedStatement(pgSqlQuery, paramsBuilder.build()); + LOGGER.warn("Executing search query to PostgresSQL:{}", preparedStatement.toString()); ResultSet resultSet = preparedStatement.executeQuery(); CloseableIterator closeableIterator = query.getSelections().size() > 0 @@ -367,24 +367,13 @@ public CloseableIterator search(Query query) { : new PostgresResultIterator(resultSet); return closeableIterator; } catch (SQLException e) { - LOGGER.error("SQLException querying documents. query: {}", query, e); + LOGGER.error( + "SQLException in querying documents - query: {}, sqlQuery:{}", query, pgSqlQuery, e); } return EMPTY_ITERATOR; } - private String prepareSelections(Query query) { - List selections = query.getSelections(); - if (selections.isEmpty()) return "*"; - return selections.stream() - .map( - selection -> - String.format( - "%s AS \"%s\"", - PostgresUtils.prepareFieldAccessorExpr(selection, "document"), selection)) - .collect(Collectors.joining(",")); - } - @Override public CloseableIterator find( final org.hypertrace.core.documentstore.query.Query query) { diff --git a/document-store/src/main/java/org/hypertrace/core/documentstore/postgres/PostgresQueryParser.java b/document-store/src/main/java/org/hypertrace/core/documentstore/postgres/PostgresQueryParser.java index 2d22ef1c..cdb12b2c 100644 --- a/document-store/src/main/java/org/hypertrace/core/documentstore/postgres/PostgresQueryParser.java +++ b/document-store/src/main/java/org/hypertrace/core/documentstore/postgres/PostgresQueryParser.java @@ -1,14 +1,8 @@ package org.hypertrace.core.documentstore.postgres; -import static org.hypertrace.core.documentstore.Collection.UNSUPPORTED_QUERY_OPERATION; -import static org.hypertrace.core.documentstore.postgres.PostgresCollection.CREATED_AT; -import static org.hypertrace.core.documentstore.postgres.PostgresCollection.ID; -import static org.hypertrace.core.documentstore.postgres.PostgresCollection.UPDATED_AT; - import java.util.Arrays; -import java.util.HashSet; import java.util.List; -import java.util.Set; +import java.util.Optional; import java.util.stream.Collectors; import org.apache.commons.lang3.StringUtils; import org.hypertrace.core.documentstore.Filter; @@ -18,140 +12,34 @@ class PostgresQueryParser { - private static final String QUESTION_MARK = "?"; - // postgres jsonb uses `->` instead of `.` for json field access - private static final String JSON_FIELD_ACCESSOR = "->"; - // postgres operator to fetch the value of json object as text. - private static final String JSON_DATA_ACCESSOR = "->>"; - private static final Set OUTER_COLUMNS = - new HashSet<>() { - { - add(CREATED_AT); - add(ID); - add(UPDATED_AT); - } - }; + static String parseSelections(List selections) { + return Optional.of( + selections.stream() + .map( + selection -> + String.format( + "%s AS \"%s\"", + PostgresUtils.prepareFieldAccessorExpr( + selection, PostgresUtils.DOCUMENT_COLUMN), + selection)) + .collect(Collectors.joining(","))) + .filter(str -> StringUtils.isNotBlank(str)) + .orElse("*"); + } static String parseFilter(Filter filter, Builder paramsBuilder) { if (filter.isComposite()) { return parseCompositeFilter(filter, paramsBuilder); } else { - return parseNonCompositeFilter(filter, paramsBuilder); + return PostgresUtils.parseNonCompositeFilter( + filter.getFieldName(), + PostgresUtils.DOCUMENT_COLUMN, + filter.getOp().toString(), + filter.getValue(), + paramsBuilder); } } - static String parseNonCompositeFilter(Filter filter, Builder paramsBuilder) { - Filter.Op op = filter.getOp(); - Object value = filter.getValue(); - String fieldName = filter.getFieldName(); - String fullFieldName = - prepareCast( - PostgresUtils.prepareFieldDataAccessorExpr(fieldName, PostgresUtils.DOCUMENT_COLUMN), - value); - StringBuilder filterString = new StringBuilder(fullFieldName); - String sqlOperator; - Boolean isMultiValued = false; - switch (op) { - case EQ: - sqlOperator = " = "; - break; - case GT: - sqlOperator = " > "; - break; - case LT: - sqlOperator = " < "; - break; - case GTE: - sqlOperator = " >= "; - break; - case LTE: - sqlOperator = " <= "; - break; - case LIKE: - // Case insensitive regex search, Append % at beginning and end of value to do a regex - // search - sqlOperator = " ILIKE "; - value = "%" + value + "%"; - break; - case NOT_IN: - // NOTE: Below two points - // 1. both NOT_IN and IN filter currently limited to non-array field - // - https://github.com/hypertrace/document-store/issues/32#issuecomment-781411676 - // 2. To make semantically opposite filter of IN, we need to check for if key is not present - // Ref in context of NEQ - - // https://github.com/hypertrace/document-store/pull/20#discussion_r547101520Other - // so, we need - "document->key IS NULL OR document->key->> NOT IN (v1, v2)" - StringBuilder notInFilterString = - PostgresUtils.prepareFieldAccessorExpr(fieldName, PostgresUtils.DOCUMENT_COLUMN); - if (notInFilterString != null && !OUTER_COLUMNS.contains(fieldName)) { - filterString = notInFilterString.append(" IS NULL OR ").append(fullFieldName); - } - sqlOperator = " NOT IN "; - isMultiValued = true; - value = prepareParameterizedStringForList((List) value, paramsBuilder); - break; - case IN: - // NOTE: both NOT_IN and IN filter currently limited to non-array field - // - https://github.com/hypertrace/document-store/issues/32#issuecomment-781411676 - sqlOperator = " IN "; - isMultiValued = true; - value = prepareParameterizedStringForList((List) value, paramsBuilder); - break; - case NOT_EXISTS: - sqlOperator = " IS NULL "; - value = null; - // For fields inside jsonb - StringBuilder notExists = - PostgresUtils.prepareFieldAccessorExpr(fieldName, PostgresUtils.DOCUMENT_COLUMN); - if (notExists != null && !OUTER_COLUMNS.contains(fieldName)) { - filterString = notExists; - } - break; - case EXISTS: - sqlOperator = " IS NOT NULL "; - value = null; - // For fields inside jsonb - StringBuilder exists = - PostgresUtils.prepareFieldAccessorExpr(fieldName, PostgresUtils.DOCUMENT_COLUMN); - if (exists != null && !OUTER_COLUMNS.contains(fieldName)) { - filterString = exists; - } - break; - case NEQ: - sqlOperator = " != "; - // https://github.com/hypertrace/document-store/pull/20#discussion_r547101520 - // The expected behaviour is to get all documents which either satisfy non equality - // condition - // or the key doesn't exist in them - // Semantics for handling if key not exists and if it exists, its value - // doesn't equal to the filter for Jsonb document will be done as: - // "document->key IS NULL OR document->key->> != value" - StringBuilder notEquals = - PostgresUtils.prepareFieldAccessorExpr(fieldName, PostgresUtils.DOCUMENT_COLUMN); - // For fields inside jsonb - if (notEquals != null && !OUTER_COLUMNS.contains(fieldName)) { - filterString = notEquals.append(" IS NULL OR ").append(fullFieldName); - } - break; - case CONTAINS: - // TODO: Matches condition inside an array of documents - default: - throw new UnsupportedOperationException(UNSUPPORTED_QUERY_OPERATION); - } - - filterString.append(sqlOperator); - if (value != null) { - if (isMultiValued) { - filterString.append(value); - } else { - filterString.append(QUESTION_MARK); - paramsBuilder.addObjectParam(value); - } - } - String filters = filterString.toString(); - return filters; - } - static String parseCompositeFilter(Filter filter, Builder paramsBuilder) { Filter.Op op = filter.getOp(); switch (op) { @@ -192,40 +80,4 @@ static String parseOrderBys(List orderBys) { .filter(str -> !StringUtils.isEmpty(str)) .collect(Collectors.joining(" , ")); } - - private static String prepareParameterizedStringForList( - List values, Params.Builder paramsBuilder) { - String collect = - values.stream() - .map( - val -> { - paramsBuilder.addObjectParam(val); - return QUESTION_MARK; - }) - .collect(Collectors.joining(", ")); - return "(" + collect + ")"; - } - - private static String prepareCast(String field, Object value) { - String fmt = "CAST (%s AS %s)"; - - // handle the case if the value type is collection for filter operator - `IN` - // Currently, for `IN` operator, we are considering List collection, and it is fair - // assumption that all its value of the same types. Based on that and for consistency - // we will use CAST ( as ) for all non string operator. - // Ref : https://github.com/hypertrace/document-store/pull/30#discussion_r571782575 - - if (value instanceof List && ((List) value).size() > 0) { - List listValue = (List) value; - value = listValue.get(0); - } - - if (value instanceof Number) { - return String.format(fmt, field, "NUMERIC"); - } else if (value instanceof Boolean) { - return String.format(fmt, field, "BOOLEAN"); - } else /* default is string */ { - return field; - } - } } diff --git a/document-store/src/main/java/org/hypertrace/core/documentstore/postgres/utils/PostgresUtils.java b/document-store/src/main/java/org/hypertrace/core/documentstore/postgres/utils/PostgresUtils.java index 072fd4d7..27e60123 100644 --- a/document-store/src/main/java/org/hypertrace/core/documentstore/postgres/utils/PostgresUtils.java +++ b/document-store/src/main/java/org/hypertrace/core/documentstore/postgres/utils/PostgresUtils.java @@ -15,11 +15,8 @@ import org.apache.commons.lang3.StringUtils; import org.hypertrace.core.documentstore.postgres.Params; import org.hypertrace.core.documentstore.postgres.Params.Builder; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; public class PostgresUtils { - private static final Logger LOGGER = LoggerFactory.getLogger(PostgresUtils.class); private static final String QUESTION_MARK = "?"; private static final String JSON_FIELD_ACCESSOR = "->"; private static final String JSON_DATA_ACCESSOR = "->>"; diff --git a/document-store/src/test/java/org/hypertrace/core/documentstore/postgres/PostgresQueryParserTest.java b/document-store/src/test/java/org/hypertrace/core/documentstore/postgres/PostgresQueryParserTest.java index a83773f3..3979c76e 100644 --- a/document-store/src/test/java/org/hypertrace/core/documentstore/postgres/PostgresQueryParserTest.java +++ b/document-store/src/test/java/org/hypertrace/core/documentstore/postgres/PostgresQueryParserTest.java @@ -10,6 +10,7 @@ import org.hypertrace.core.documentstore.Filter; import org.hypertrace.core.documentstore.Filter.Op; import org.hypertrace.core.documentstore.OrderBy; +import org.hypertrace.core.documentstore.postgres.utils.PostgresUtils; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; @@ -19,49 +20,97 @@ class PostgresQueryParserTest { void testParseNonCompositeFilter() { { Filter filter = new Filter(Filter.Op.EQ, ID, "val1"); - String query = PostgresQueryParser.parseNonCompositeFilter(filter, initParams()); + String query = + PostgresUtils.parseNonCompositeFilter( + filter.getFieldName(), + PostgresUtils.DOCUMENT_COLUMN, + filter.getOp().toString(), + filter.getValue(), + initParams()); Assertions.assertEquals(ID + " = ?", query); } { Filter filter = new Filter(Filter.Op.NEQ, ID, "val1"); - String query = PostgresQueryParser.parseNonCompositeFilter(filter, initParams()); + String query = + PostgresUtils.parseNonCompositeFilter( + filter.getFieldName(), + PostgresUtils.DOCUMENT_COLUMN, + filter.getOp().toString(), + filter.getValue(), + initParams()); Assertions.assertEquals(ID + " != ?", query); } { Filter filter = new Filter(Filter.Op.GT, ID, 5); - String query = PostgresQueryParser.parseNonCompositeFilter(filter, initParams()); + String query = + PostgresUtils.parseNonCompositeFilter( + filter.getFieldName(), + PostgresUtils.DOCUMENT_COLUMN, + filter.getOp().toString(), + filter.getValue(), + initParams()); Assertions.assertEquals("CAST (" + ID + " AS NUMERIC) > ?", query); } { Filter filter = new Filter(Filter.Op.GTE, ID, 5); - String query = PostgresQueryParser.parseNonCompositeFilter(filter, initParams()); + String query = + PostgresUtils.parseNonCompositeFilter( + filter.getFieldName(), + PostgresUtils.DOCUMENT_COLUMN, + filter.getOp().toString(), + filter.getValue(), + initParams()); Assertions.assertEquals("CAST (" + ID + " AS NUMERIC) >= ?", query); } { Filter filter = new Filter(Filter.Op.LT, ID, 5); - String query = PostgresQueryParser.parseNonCompositeFilter(filter, initParams()); + String query = + PostgresUtils.parseNonCompositeFilter( + filter.getFieldName(), + PostgresUtils.DOCUMENT_COLUMN, + filter.getOp().toString(), + filter.getValue(), + initParams()); Assertions.assertEquals("CAST (" + ID + " AS NUMERIC) < ?", query); } { Filter filter = new Filter(Filter.Op.LTE, ID, 5); - String query = PostgresQueryParser.parseNonCompositeFilter(filter, initParams()); + String query = + PostgresUtils.parseNonCompositeFilter( + filter.getFieldName(), + PostgresUtils.DOCUMENT_COLUMN, + filter.getOp().toString(), + filter.getValue(), + initParams()); Assertions.assertEquals("CAST (" + ID + " AS NUMERIC) <= ?", query); } { Filter filter = new Filter(Filter.Op.LIKE, ID, "abc"); - String query = PostgresQueryParser.parseNonCompositeFilter(filter, initParams()); + String query = + PostgresUtils.parseNonCompositeFilter( + filter.getFieldName(), + PostgresUtils.DOCUMENT_COLUMN, + filter.getOp().toString(), + filter.getValue(), + initParams()); Assertions.assertEquals(ID + " ILIKE ?", query); } { Filter filter = new Filter(Filter.Op.IN, ID, List.of("abc", "xyz")); - String query = PostgresQueryParser.parseNonCompositeFilter(filter, initParams()); + String query = + PostgresUtils.parseNonCompositeFilter( + filter.getFieldName(), + PostgresUtils.DOCUMENT_COLUMN, + filter.getOp().toString(), + filter.getValue(), + initParams()); Assertions.assertEquals(ID + " IN (?, ?)", query); } } @@ -70,74 +119,74 @@ void testParseNonCompositeFilter() { void testParseNonCompositeFilterForJsonField() { { Filter filter = new Filter(Filter.Op.EQ, "key1", "val1"); - String query = PostgresQueryParser.parseNonCompositeFilter(filter, initParams()); + String query = PostgresQueryParser.parseFilter(filter, initParams()); Assertions.assertEquals("document->>'key1' = ?", query); } { Filter filter = new Filter(Filter.Op.NEQ, "key1", "val1"); - String query = PostgresQueryParser.parseNonCompositeFilter(filter, initParams()); + String query = PostgresQueryParser.parseFilter(filter, initParams()); Assertions.assertEquals("document->'key1' IS NULL OR document->>'key1' != ?", query); } { Filter filter = new Filter(Filter.Op.GT, "key1", 5); - String query = PostgresQueryParser.parseNonCompositeFilter(filter, initParams()); + String query = PostgresQueryParser.parseFilter(filter, initParams()); Assertions.assertEquals("CAST (document->>'key1' AS NUMERIC) > ?", query); } { Filter filter = new Filter(Filter.Op.GTE, "key1", 5); - String query = PostgresQueryParser.parseNonCompositeFilter(filter, initParams()); + String query = PostgresQueryParser.parseFilter(filter, initParams()); Assertions.assertEquals("CAST (document->>'key1' AS NUMERIC) >= ?", query); } { Filter filter = new Filter(Filter.Op.LT, "key1", 5); - String query = PostgresQueryParser.parseNonCompositeFilter(filter, initParams()); + String query = PostgresQueryParser.parseFilter(filter, initParams()); Assertions.assertEquals("CAST (document->>'key1' AS NUMERIC) < ?", query); } { Filter filter = new Filter(Filter.Op.LTE, "key1", 5); - String query = PostgresQueryParser.parseNonCompositeFilter(filter, initParams()); + String query = PostgresQueryParser.parseFilter(filter, initParams()); Assertions.assertEquals("CAST (document->>'key1' AS NUMERIC) <= ?", query); } { Filter filter = new Filter(Filter.Op.LIKE, "key1", "abc"); - String query = PostgresQueryParser.parseNonCompositeFilter(filter, initParams()); + String query = PostgresQueryParser.parseFilter(filter, initParams()); Assertions.assertEquals("document->>'key1' ILIKE ?", query); } { Filter filter = new Filter(Filter.Op.IN, "key1", List.of("abc", "xyz")); - String query = PostgresQueryParser.parseNonCompositeFilter(filter, initParams()); + String query = PostgresQueryParser.parseFilter(filter, initParams()); Assertions.assertEquals("document->>'key1' IN (?, ?)", query); } { Filter filter = new Filter(Op.NOT_IN, "key1", List.of("abc", "xyz")); - String query = PostgresQueryParser.parseNonCompositeFilter(filter, initParams()); + String query = PostgresQueryParser.parseFilter(filter, initParams()); Assertions.assertEquals("document->'key1' IS NULL OR document->>'key1' NOT IN (?, ?)", query); } { Filter filter = new Filter(Filter.Op.EQ, DOCUMENT_ID, "k1:k2"); - String query = PostgresQueryParser.parseNonCompositeFilter(filter, initParams()); + String query = PostgresQueryParser.parseFilter(filter, initParams()); Assertions.assertEquals("document->>'_id' = ?", query); } { Filter filter = new Filter(Filter.Op.EXISTS, "key1.key2", null); - String query = PostgresQueryParser.parseNonCompositeFilter(filter, initParams()); + String query = PostgresQueryParser.parseFilter(filter, initParams()); System.err.println(query); Assertions.assertEquals("document->'key1'->'key2' IS NOT NULL ", query); } { Filter filter = new Filter(Filter.Op.NOT_EXISTS, "key1", null); - String query = PostgresQueryParser.parseNonCompositeFilter(filter, initParams()); + String query = PostgresQueryParser.parseFilter(filter, initParams()); Assertions.assertEquals("document->'key1' IS NULL ", query); } } @@ -151,7 +200,7 @@ void testNonCompositeFilterUnsupportedException() { Exception exception = assertThrows( UnsupportedOperationException.class, - () -> PostgresQueryParser.parseNonCompositeFilter(filter, initParams())); + () -> PostgresQueryParser.parseFilter(filter, initParams())); String actualMessage = exception.getMessage(); Assertions.assertTrue(actualMessage.contains(expected)); } @@ -174,7 +223,7 @@ void testParseQueryForCompositeFilter() { { Filter filter = new Filter(Filter.Op.EQ, ID, "val1").and(new Filter(Filter.Op.EQ, CREATED_AT, "val2")); - String query = PostgresQueryParser.parseCompositeFilter(filter, initParams()); + String query = PostgresQueryParser.parseFilter(filter, initParams()); Assertions.assertEquals(String.format("(%s = ?) AND (%s = ?)", ID, CREATED_AT), query); } @@ -182,7 +231,7 @@ void testParseQueryForCompositeFilter() { Filter filter = new Filter(Filter.Op.EQ, ID, "val1").or(new Filter(Filter.Op.EQ, CREATED_AT, "val2")); - String query = PostgresQueryParser.parseCompositeFilter(filter, initParams()); + String query = PostgresQueryParser.parseFilter(filter, initParams()); Assertions.assertEquals(String.format("(%s = ?) OR (%s = ?)", ID, CREATED_AT), query); } } @@ -192,7 +241,7 @@ void testParseQueryForCompositeFilterForJsonField() { { Filter filter = new Filter(Filter.Op.EQ, "key1", "val1").and(new Filter(Filter.Op.EQ, "key2", "val2")); - String query = PostgresQueryParser.parseCompositeFilter(filter, initParams()); + String query = PostgresQueryParser.parseFilter(filter, initParams()); Assertions.assertEquals("(document->>'key1' = ?) AND (document->>'key2' = ?)", query); } @@ -200,7 +249,7 @@ void testParseQueryForCompositeFilterForJsonField() { Filter filter = new Filter(Filter.Op.EQ, "key1", "val1").or(new Filter(Filter.Op.EQ, "key2", "val2")); - String query = PostgresQueryParser.parseCompositeFilter(filter, initParams()); + String query = PostgresQueryParser.parseFilter(filter, initParams()); Assertions.assertEquals("(document->>'key1' = ?) OR (document->>'key2' = ?)", query); } } From d57ab0cc3389f106f78178f92472c23f1a9a509c Mon Sep 17 00:00:00 2001 From: Ronak Date: Wed, 10 Aug 2022 18:57:33 +0530 Subject: [PATCH 6/7] adds warn logs for debugging --- .../core/documentstore/postgres/PostgresCollection.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/document-store/src/main/java/org/hypertrace/core/documentstore/postgres/PostgresCollection.java b/document-store/src/main/java/org/hypertrace/core/documentstore/postgres/PostgresCollection.java index e92c1900..21b03b5a 100644 --- a/document-store/src/main/java/org/hypertrace/core/documentstore/postgres/PostgresCollection.java +++ b/document-store/src/main/java/org/hypertrace/core/documentstore/postgres/PostgresCollection.java @@ -785,7 +785,7 @@ private CloseableIterator executeQueryV1( try { PreparedStatement preparedStatement = buildPreparedStatement(sqlQuery, queryParser.getParamsBuilder().build()); - LOGGER.warn("Executing executeQueryV1 query:{}", preparedStatement.toString()); + LOGGER.warn("Executing executeQueryV1 sqlQuery:{}", preparedStatement.toString()); ResultSet resultSet = preparedStatement.executeQuery(); CloseableIterator closeableIterator = query.getSelections().size() > 0 From ae42d97049034c924d106e671687974d7c725a7f Mon Sep 17 00:00:00 2001 From: Ronak Date: Thu, 11 Aug 2022 12:26:10 +0530 Subject: [PATCH 7/7] temp debug --- document-store/build.gradle.kts | 1 + .../core/documentstore/DocStoreTest.java | 66 ++++++++++++++++--- .../core/documentstore/InsightDto.java | 35 ++++++++++ .../postgres/PostgresCollection.java | 24 +++++-- .../postgres/utils/PostgresUtils.java | 2 + 5 files changed, 113 insertions(+), 15 deletions(-) create mode 100644 document-store/src/integrationTest/java/org/hypertrace/core/documentstore/InsightDto.java diff --git a/document-store/build.gradle.kts b/document-store/build.gradle.kts index cc0028a4..c609baaf 100644 --- a/document-store/build.gradle.kts +++ b/document-store/build.gradle.kts @@ -8,6 +8,7 @@ plugins { dependencies { api("com.typesafe:config:1.4.2") + implementation("org.projectlombok:lombok:1.18.18") annotationProcessor("org.projectlombok:lombok:1.18.22") compileOnly("org.projectlombok:lombok:1.18.22") implementation("org.apache.commons:commons-collections4:4.4") diff --git a/document-store/src/integrationTest/java/org/hypertrace/core/documentstore/DocStoreTest.java b/document-store/src/integrationTest/java/org/hypertrace/core/documentstore/DocStoreTest.java index 035b0051..a692808f 100644 --- a/document-store/src/integrationTest/java/org/hypertrace/core/documentstore/DocStoreTest.java +++ b/document-store/src/integrationTest/java/org/hypertrace/core/documentstore/DocStoreTest.java @@ -32,6 +32,7 @@ import java.util.List; import java.util.Map; import java.util.Objects; +import java.util.Optional; import java.util.Set; import java.util.stream.Collectors; import java.util.stream.IntStream; @@ -56,6 +57,7 @@ import org.testcontainers.containers.GenericContainer; import org.testcontainers.containers.wait.strategy.Wait; import org.testcontainers.shaded.com.google.common.collect.Maps; +import org.testcontainers.shaded.org.apache.commons.lang.StringEscapeUtils; import org.testcontainers.utility.DockerImageName; public class DocStoreTest { @@ -85,22 +87,25 @@ public static void init() { Datastore mongoDatastore = DatastoreProvider.getDatastore("Mongo", config); System.out.println(mongoDatastore.listCollections()); - postgres = - new GenericContainer<>(DockerImageName.parse("postgres:13.1")) - .withEnv("POSTGRES_PASSWORD", "postgres") - .withEnv("POSTGRES_USER", "postgres") - .withExposedPorts(5432) - .waitingFor(Wait.forListeningPort()); - postgres.start(); +// postgres = +// new GenericContainer<>(DockerImageName.parse("postgres:13.1")) +// .withEnv("POSTGRES_PASSWORD", "postgres") +// .withEnv("POSTGRES_USER", "postgres") +// .withExposedPorts(5432) +// .waitingFor(Wait.forListeningPort()); +// postgres.start(); + +// String postgresConnectionUrl = +// String.format("jdbc:postgresql://localhost:%s/", postgres.getMappedPort(5432)); String postgresConnectionUrl = - String.format("jdbc:postgresql://localhost:%s/", postgres.getMappedPort(5432)); + String.format("jdbc:postgresql://localhost:%s/", "5432"); DatastoreProvider.register("POSTGRES", PostgresDatastore.class); Map postgresConfig = new HashMap<>(); postgresConfig.putIfAbsent("url", postgresConnectionUrl); - postgresConfig.putIfAbsent("user", "postgres"); - postgresConfig.putIfAbsent("password", "postgres"); + postgresConfig.putIfAbsent("user", "keycloak"); + postgresConfig.putIfAbsent("password", "keycloak"); Datastore postgresDatastore = DatastoreProvider.getDatastore("Postgres", ConfigFactory.parseMap(postgresConfig)); System.out.println(postgresDatastore.listCollections()); @@ -139,6 +144,47 @@ private static Stream databaseContextMongo() { return Stream.of(Arguments.of(MONGO_STORE)); } + @ParameterizedTest + @MethodSource("databaseContextPostgres") + public void debugSearch(String dataStoreName) throws Exception { + /* + SELECT document->'identifyingAttributes' AS "identifyingAttributes", + document->'tenantId' AS "tenantId", + document->'type' AS "type",id AS "id", + document->'attributes' AS "attributes" + FROM insights WHERE ((document->>'tenantId' = '14d8d0d8-c1a9-4100-83a4-97edfeb85606') AND (document->>'type' = 'API')) + AND (document->'identifyingAttributes'->>'api_id' = '5e6f57d7-313d-34e1-a37e-a338c448c271') + */ + + Datastore datastore = datastoreMap.get(dataStoreName); + Collection collection = datastore.getCollection("insights"); + + Query query = new Query(); + query.addSelection("identifyingAttributes"); + query.addSelection("tenantId"); + query.addSelection("id"); + query.addSelection("attributes"); + query.addSelection("type"); + + Filter f1 = Filter.eq("tenantId", "14d8d0d8-c1a9-4100-83a4-97edfeb85606"); + Filter f2 = f1.and(Filter.eq("type", "API")); + Filter f3 = f2.and(Filter.eq("identifyingAttributes.api_id", "5e6f57d7-313d-34e1-a37e-a338c448c271")); + query.setFilter(f3); + + Iterator results = collection.search(query); + List documents = new ArrayList<>(); + while (results.hasNext()) { + Document document = results.next(); + String insightDocumentJson = document.toJson(); + String processed = StringEscapeUtils.unescapeJava(insightDocumentJson); + documents.add(document); + Optional mayBeInsightDto = + Optional.ofNullable(OBJECT_MAPPER.readValue(processed, InsightDto.class)); + Assertions.assertNotNull(mayBeInsightDto.get()); + } + Assertions.assertFalse(documents.isEmpty()); + } + @ParameterizedTest @MethodSource("databaseContextProvider") public void testUpsert(String dataStoreName) throws Exception { diff --git a/document-store/src/integrationTest/java/org/hypertrace/core/documentstore/InsightDto.java b/document-store/src/integrationTest/java/org/hypertrace/core/documentstore/InsightDto.java new file mode 100644 index 00000000..e0a2fb55 --- /dev/null +++ b/document-store/src/integrationTest/java/org/hypertrace/core/documentstore/InsightDto.java @@ -0,0 +1,35 @@ +package org.hypertrace.core.documentstore; + +import com.fasterxml.jackson.annotation.JsonProperty; +import java.util.Map; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@NoArgsConstructor +@AllArgsConstructor +@Builder +public class InsightDto { + public static final String ID_FIELD = "id"; + public static final String TYPE_FIELD = "type"; + public static final String ATTRIBUTES_FIELD = "attributes"; + public static final String IDENTIFYING_ATTRIBUTES_FIELD = "identifyingAttributes"; + public static final String TENANT_ID_FIELD = "tenantId"; + + @JsonProperty(value = ID_FIELD) + private String id; + + @JsonProperty(value = TYPE_FIELD) + private String type; + + @JsonProperty(value = ATTRIBUTES_FIELD) + private Map attributes; + + @JsonProperty(value = IDENTIFYING_ATTRIBUTES_FIELD) + private Map identifyingAttributes; + + @JsonProperty(value = TENANT_ID_FIELD) + private String tenantId; +} diff --git a/document-store/src/main/java/org/hypertrace/core/documentstore/postgres/PostgresCollection.java b/document-store/src/main/java/org/hypertrace/core/documentstore/postgres/PostgresCollection.java index 21b03b5a..aee5f7c1 100644 --- a/document-store/src/main/java/org/hypertrace/core/documentstore/postgres/PostgresCollection.java +++ b/document-store/src/main/java/org/hypertrace/core/documentstore/postgres/PostgresCollection.java @@ -1,6 +1,7 @@ package org.hypertrace.core.documentstore.postgres; import com.fasterxml.jackson.core.JsonParseException; +import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.node.ArrayNode; @@ -1083,11 +1084,7 @@ protected Document prepareDocument() throws SQLException, IOException { Map jsonNode = new HashMap(); for (int i = 1; i <= columnCount; i++) { String columnName = resultSetMetaData.getColumnName(i); - int columnType = resultSetMetaData.getColumnType(i); - String columnValue = - columnType == Types.ARRAY - ? MAPPER.writeValueAsString(resultSet.getArray(i).getArray()) - : resultSet.getString(i); + String columnValue = getColumnValue(resultSetMetaData, columnName, i); if (StringUtils.isNotEmpty(columnValue)) { JsonNode leafNodeValue = MAPPER.readTree(columnValue); if (PostgresUtils.isEncodedNestedField(columnName)) { @@ -1101,6 +1098,23 @@ protected Document prepareDocument() throws SQLException, IOException { return new JSONDocument(MAPPER.writeValueAsString(jsonNode)); } + private String getColumnValue(ResultSetMetaData resultSetMetaData, String columnName, int columnIndex) + throws SQLException, JsonProcessingException { + int columnType = resultSetMetaData.getColumnType(columnIndex); + // check for array + if (columnType == Types.ARRAY) { + return MAPPER.writeValueAsString(resultSet.getArray(columnIndex).getArray()); + } + + // check for ID column + if (PostgresUtils.OUTER_COLUMNS.contains(columnName) && columnName.equals(PostgresUtils.ID_COLUMN)) { + return MAPPER.writeValueAsString(resultSet.getString(columnIndex)); + } + + // rest of the columns + return resultSet.getString(columnIndex); + } + private void handleNestedField( String columnName, Map rootNode, JsonNode leafNodeValue) { List keys = PostgresUtils.splitNestedField(columnName); diff --git a/document-store/src/main/java/org/hypertrace/core/documentstore/postgres/utils/PostgresUtils.java b/document-store/src/main/java/org/hypertrace/core/documentstore/postgres/utils/PostgresUtils.java index 27e60123..5e992def 100644 --- a/document-store/src/main/java/org/hypertrace/core/documentstore/postgres/utils/PostgresUtils.java +++ b/document-store/src/main/java/org/hypertrace/core/documentstore/postgres/utils/PostgresUtils.java @@ -25,6 +25,8 @@ public class PostgresUtils { public static final Set OUTER_COLUMNS = new TreeSet<>(List.of(ID, CREATED_AT, UPDATED_AT)); + public static final String ID_COLUMN = ID; + public static final String DOCUMENT_COLUMN = DOCUMENT; public enum Type {