From 674d2eec9261c29fc8447fd9c37b705103543bdf Mon Sep 17 00:00:00 2001 From: Prashant Pandey Date: Mon, 22 Sep 2025 15:04:27 +0530 Subject: [PATCH 01/19] WIP --- .../mongo/MongoDocStoreTest.java | 6 +- .../core/documentstore/utils/Utils.java | 9 +- .../core/documentstore/Document.java | 2 + .../core/documentstore/DocumentType.java | 6 + .../core/documentstore/JSONDocument.java | 106 +++++++++++++++--- .../core/documentstore/mongo/MongoUtils.java | 2 +- .../postgres/PostgresCollection.java | 17 +-- .../postgres/utils/PostgresUtils.java | 7 +- .../core/documentstore/JSONDocumentTest.java | 4 +- .../PostgresDocStoreMetricProviderTest.java | 19 ++-- .../mongo/MongoCollectionTest.java | 6 +- .../postgres/PostgresCollectionTest.java | 5 +- .../query/v1/PostgresQueryParserTest.java | 6 +- .../core/documentstore/util/TestUtil.java | 2 +- 14 files changed, 143 insertions(+), 54 deletions(-) create mode 100644 document-store/src/main/java/org/hypertrace/core/documentstore/DocumentType.java diff --git a/document-store/src/integrationTest/java/org/hypertrace/core/documentstore/mongo/MongoDocStoreTest.java b/document-store/src/integrationTest/java/org/hypertrace/core/documentstore/mongo/MongoDocStoreTest.java index 488f77ae6..81157694d 100644 --- a/document-store/src/integrationTest/java/org/hypertrace/core/documentstore/mongo/MongoDocStoreTest.java +++ b/document-store/src/integrationTest/java/org/hypertrace/core/documentstore/mongo/MongoDocStoreTest.java @@ -125,7 +125,7 @@ public void testUpsertAndReturn() throws IOException { Collection collection = datastore.getCollection(COLLECTION_NAME); ObjectNode objectNode = OBJECT_MAPPER.createObjectNode(); objectNode.put("foo1", "bar1"); - Document document = new JSONDocument(objectNode); + Document document = JSONDocument.fromObject(objectNode); Document persistedDocument = collection.upsertAndReturn(new SingleValueKey("default", "testKey"), document); @@ -140,7 +140,7 @@ public void testUpsertAndReturn() throws IOException { objectNode = OBJECT_MAPPER.createObjectNode(); objectNode.put("foo2", "bar2"); - document = new JSONDocument(objectNode); + document = JSONDocument.fromObject(objectNode); // Upsert again and verify that createdTime does not change, while lastUpdatedTime // has changed and values have merged @@ -161,7 +161,7 @@ public void testBulkUpsertAndVerifyUpdatedTime() throws IOException { Collection collection = datastore.getCollection(COLLECTION_NAME); ObjectNode objectNode = OBJECT_MAPPER.createObjectNode(); objectNode.put("foo1", "bar1"); - Document document = new JSONDocument(objectNode); + Document document = JSONDocument.fromObject(objectNode); collection.bulkUpsert(Map.of(new SingleValueKey("default", "testKey"), document)); Query query = new Query(); diff --git a/document-store/src/integrationTest/java/org/hypertrace/core/documentstore/utils/Utils.java b/document-store/src/integrationTest/java/org/hypertrace/core/documentstore/utils/Utils.java index 75ea277f5..2449df011 100644 --- a/document-store/src/integrationTest/java/org/hypertrace/core/documentstore/utils/Utils.java +++ b/document-store/src/integrationTest/java/org/hypertrace/core/documentstore/utils/Utils.java @@ -27,6 +27,7 @@ import org.testcontainers.shaded.org.apache.commons.io.IOUtils; public class Utils { + private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper(); public static final String MONGO_STORE = "Mongo"; @@ -62,13 +63,13 @@ public static Document createDocument(ImmutablePair... pairs) { objectNode.putPOJO(pairs[i].getLeft(), pairs[i].getRight()); } } - return new JSONDocument(objectNode); + return JSONDocument.fromJsonNode(objectNode); } public static Document createDocument(String key, String value) { ObjectNode objectNode = OBJECT_MAPPER.createObjectNode(); objectNode.put(key, value); - return new JSONDocument(objectNode); + return JSONDocument.fromJsonNode(objectNode); } public static Document createDocument(String... keys) { @@ -76,7 +77,7 @@ public static Document createDocument(String... keys) { for (int i = 0; i < keys.length - 1; i++) { objectNode.put(keys[i], keys[i + 1]); } - return new JSONDocument(objectNode); + return JSONDocument.fromJsonNode(objectNode); } public static Optional readFileFromResource(String filePath) throws IOException { @@ -135,7 +136,7 @@ public static Map buildDocumentsFromResource(String resourcePath) Map documentMap = new HashMap<>(); for (Map map : maps) { Key key = new SingleValueKey(TENANT_ID, map.get(MongoCollection.ID_KEY).toString()); - Document value = new JSONDocument(map); + Document value = JSONDocument.fromObject(map); documentMap.put(key, value); } diff --git a/document-store/src/main/java/org/hypertrace/core/documentstore/Document.java b/document-store/src/main/java/org/hypertrace/core/documentstore/Document.java index 944913aac..a9327779e 100644 --- a/document-store/src/main/java/org/hypertrace/core/documentstore/Document.java +++ b/document-store/src/main/java/org/hypertrace/core/documentstore/Document.java @@ -3,4 +3,6 @@ public interface Document { String toJson(); + + DocumentType getDocumentType(); } diff --git a/document-store/src/main/java/org/hypertrace/core/documentstore/DocumentType.java b/document-store/src/main/java/org/hypertrace/core/documentstore/DocumentType.java new file mode 100644 index 000000000..745505ef2 --- /dev/null +++ b/document-store/src/main/java/org/hypertrace/core/documentstore/DocumentType.java @@ -0,0 +1,6 @@ +package org.hypertrace.core.documentstore; + +public enum DocumentType { + SQL_STORE, + DOCUMENT_STORE +} diff --git a/document-store/src/main/java/org/hypertrace/core/documentstore/JSONDocument.java b/document-store/src/main/java/org/hypertrace/core/documentstore/JSONDocument.java index c04d806de..98f7973b4 100644 --- a/document-store/src/main/java/org/hypertrace/core/documentstore/JSONDocument.java +++ b/document-store/src/main/java/org/hypertrace/core/documentstore/JSONDocument.java @@ -4,39 +4,35 @@ import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.node.ObjectNode; +import com.google.common.base.Preconditions; import java.io.IOException; import java.util.Objects; +import lombok.Builder; +@Builder public class JSONDocument implements Document { - private static ObjectMapper mapper = new ObjectMapper(); - private JsonNode node; + private static final ObjectMapper MAPPER = new ObjectMapper(); + private final JsonNode node; + private final DocumentType documentType; - public JSONDocument(String json) throws IOException { - node = mapper.readTree(json); - } - - public JSONDocument(Object object) throws IOException { - node = mapper.readTree(mapper.writeValueAsString(object)); - } - - public JSONDocument(JsonNode node) { - this.node = node; + private JSONDocument(JsonNode node, DocumentType documentType) { + this.node = Preconditions.checkNotNull(node, "JsonNode cannot be null"); + this.documentType = Preconditions.checkNotNull(documentType, "DocumentType cannot be null"); } @Override public String toJson() { try { - return mapper.writeValueAsString(node); + return MAPPER.writeValueAsString(node); } catch (JsonProcessingException e) { return "{}"; } } - public static JSONDocument errorDocument(String message) { - ObjectNode objectNode = mapper.createObjectNode(); - objectNode.put("errorMessage", message); - return new JSONDocument(objectNode); + @Override + public DocumentType getDocumentType() { + return documentType; } @Override @@ -56,4 +52,80 @@ public boolean equals(Object obj) { JSONDocument other = (JSONDocument) obj; return Objects.equals(node, other.node); } + + @Override + public int hashCode() { + return Objects.hash(node, documentType); + } + + // Builder class + public static class Builder { + + private JsonNode node; + private DocumentType documentType = DocumentType.DOCUMENT_STORE; // default is DOCUMENT_STORE + + public JSONDocument.Builder fromJson(String json) throws IOException { + this.node = MAPPER.readTree(json); + return this; + } + + public JSONDocument.Builder fromObject(Object object) throws IOException { + this.node = MAPPER.readTree(MAPPER.writeValueAsString(object)); + return this; + } + + public JSONDocument.Builder fromJsonNode(JsonNode node) { + this.node = node; + return this; + } + + public JSONDocument.Builder withDocumentType(DocumentType documentType) { + this.documentType = documentType; + return this; + } + + public JSONDocument build() { + if (node == null) { + throw new IllegalStateException("JsonNode must be set before building"); + } + return new JSONDocument(node, documentType); + } + } + + public static JSONDocument fromJson(String json) throws IOException { + return new JSONDocument.Builder().fromJson(json).build(); + } + + public static JSONDocument fromJson(String json, DocumentType type) throws IOException { + return new JSONDocument.Builder().fromJson(json).withDocumentType(type).build(); + } + + public static JSONDocument fromObject(Object object) throws IOException { + return new JSONDocument.Builder().fromObject(object).build(); + } + + public static JSONDocument fromObject(Object object, DocumentType type) throws IOException { + return new JSONDocument.Builder().fromObject(object).withDocumentType(type).build(); + } + + public static JSONDocument fromJsonNode(JsonNode jsonNode) { + return new JSONDocument.Builder().fromJsonNode(jsonNode).build(); + } + + public static JSONDocument fromJsonNode(JsonNode jsonNode, DocumentType type) throws IOException { + return new JSONDocument.Builder().fromJsonNode(jsonNode).withDocumentType(type).build(); + } + + public static JSONDocument errorDocument(String message) { + return errorDocument(message, DocumentType.DOCUMENT_STORE); + } + + public static JSONDocument errorDocument(String message, DocumentType documentType) { + ObjectNode objectNode = MAPPER.createObjectNode(); + objectNode.put("errorMessage", message); + return new JSONDocument.Builder() + .fromJsonNode(objectNode) + .withDocumentType(documentType) + .build(); + } } diff --git a/document-store/src/main/java/org/hypertrace/core/documentstore/mongo/MongoUtils.java b/document-store/src/main/java/org/hypertrace/core/documentstore/mongo/MongoUtils.java index f12868cff..2ed8a0ae3 100644 --- a/document-store/src/main/java/org/hypertrace/core/documentstore/mongo/MongoUtils.java +++ b/document-store/src/main/java/org/hypertrace/core/documentstore/mongo/MongoUtils.java @@ -137,7 +137,7 @@ public static Document dbObjectToDocument(BasicDBObject dbObject) { jsonString = dbObject.toJson(relaxed); JsonNode jsonNode = MAPPER.readTree(jsonString); JsonNode decodedJsonNode = recursiveClone(jsonNode, MongoUtils::decodeKey, identity()); - return new JSONDocument(decodedJsonNode); + return JSONDocument.fromJsonNode(decodedJsonNode); } catch (IOException e) { // throwing exception is not very useful here. return JSONDocument.errorDocument(e.getMessage()); 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 5582889a3..05460c1d6 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 @@ -61,6 +61,7 @@ import org.hypertrace.core.documentstore.Collection; import org.hypertrace.core.documentstore.CreateResult; import org.hypertrace.core.documentstore.Document; +import org.hypertrace.core.documentstore.DocumentType; import org.hypertrace.core.documentstore.Filter; import org.hypertrace.core.documentstore.JSONDocument; import org.hypertrace.core.documentstore.Key; @@ -879,7 +880,7 @@ private BulkUpdateResult bulkSetOnArrayValue( if (docRoot.isObject()) { ((ObjectNode) docRoot).put(DocStoreConstants.LAST_UPDATED_TIME, System.currentTimeMillis()); } - upsertMap.put(Key.from(id), new JSONDocument(docRoot)); + upsertMap.put(Key.from(id), JSONDocument.fromJsonNode(docRoot, DocumentType.SQL_STORE)); } return upsertDocs(upsertMap); } @@ -908,7 +909,7 @@ private BulkUpdateResult bulkRemoveOnArrayValue( candidateArray.removeAll(); candidateArray.addAll(existingItems); String id = docRoot.findValue(DOCUMENT_ID).asText(); - upsertMap.put(Key.from(id), new JSONDocument(docRoot)); + upsertMap.put(Key.from(id), JSONDocument.fromJsonNode(docRoot, DocumentType.SQL_STORE)); } return upsertDocs(upsertMap); } @@ -933,7 +934,7 @@ private BulkUpdateResult bulkAddOnArrayValue( if (docRoot.isObject()) { ((ObjectNode) docRoot).put(DocStoreConstants.LAST_UPDATED_TIME, System.currentTimeMillis()); } - upsertMap.put(Key.from(id), new JSONDocument(docRoot)); + upsertMap.put(Key.from(id), JSONDocument.fromJsonNode(docRoot, DocumentType.SQL_STORE)); } return upsertDocs(upsertMap); } @@ -1276,7 +1277,7 @@ public Document next() { } catch (IOException | SQLException e) { System.out.println("prepare document failed!"); closeResultSet(); - return JSONDocument.errorDocument(e.getMessage()); + return JSONDocument.errorDocument(e.getMessage(), DocumentType.SQL_STORE); } } @@ -1299,7 +1300,7 @@ protected Document prepareDocument() throws SQLException, IOException { jsonNode.remove(DOCUMENT_ID); } - return new JSONDocument(MAPPER.writeValueAsString(jsonNode)); + return JSONDocument.fromJson(MAPPER.writeValueAsString(jsonNode), DocumentType.SQL_STORE); } private void addColumnToJsonNode( @@ -1430,7 +1431,7 @@ public Document next() { return prepareDocument(); } catch (IOException | SQLException e) { closeResultSet(); - return JSONDocument.errorDocument(e.getMessage()); + return JSONDocument.errorDocument(e.getMessage(), DocumentType.SQL_STORE); } } @@ -1447,7 +1448,7 @@ protected Document prepareDocument() throws SQLException, IOException { jsonNode.put(CREATED_AT, String.valueOf(createdAt)); jsonNode.put(UPDATED_AT, String.valueOf(updatedAt)); - return new JSONDocument(MAPPER.writeValueAsString(jsonNode)); + return JSONDocument.fromJson(MAPPER.writeValueAsString(jsonNode), DocumentType.SQL_STORE); } protected void closeResultSet() { @@ -1508,7 +1509,7 @@ protected Document prepareDocument() throws SQLException, IOException { } } } - return new JSONDocument(MAPPER.writeValueAsString(jsonNode)); + return JSONDocument.fromJson(MAPPER.writeValueAsString(jsonNode), DocumentType.SQL_STORE); } private String getColumnValue( 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 24b8ffc6d..8e5b94e4b 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 @@ -36,6 +36,7 @@ @Slf4j public class PostgresUtils { + public static final String JSON_FIELD_ACCESSOR = "->"; private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper(); private static final String QUESTION_MARK = "?"; @@ -133,8 +134,9 @@ public static String prepareCast(String field, Type type) { public static String prepareCastForFieldAccessor(String field, Object value) { String fmt = "CAST (%s AS %s)"; Type type = getType(value); - if (type.equals(Type.STRING) || type.equals(Type.STRING_ARRAY)) + if (type.equals(Type.STRING) || type.equals(Type.STRING_ARRAY)) { return String.format(fmt, field, "TEXT"); + } return prepareCast(field, type); } @@ -587,7 +589,8 @@ public static DocumentAndId extractAndRemoveId(final Document document) throws I new ObjectMapper() .readValue(document.toJson(), new TypeReference>() {}); final String id = String.valueOf(requireNonNull(map.remove(IMPLICIT_ID))); - final Document documentWithoutId = new JSONDocument(new ObjectMapper().writeValueAsString(map)); + final Document documentWithoutId = + JSONDocument.fromJson(new ObjectMapper().writeValueAsString(map)); return new DocumentAndId(documentWithoutId, id); } diff --git a/document-store/src/test/java/org/hypertrace/core/documentstore/JSONDocumentTest.java b/document-store/src/test/java/org/hypertrace/core/documentstore/JSONDocumentTest.java index 5cd3be9ac..407d8c0aa 100644 --- a/document-store/src/test/java/org/hypertrace/core/documentstore/JSONDocumentTest.java +++ b/document-store/src/test/java/org/hypertrace/core/documentstore/JSONDocumentTest.java @@ -9,8 +9,8 @@ public class JSONDocumentTest { @Test public void testJSONDocument() throws Exception { Map data = Map.of("key1", "value1", "key2", "value2"); - JSONDocument document1 = new JSONDocument(data); - JSONDocument document2 = new JSONDocument(document1.toJson()); + JSONDocument document1 = JSONDocument.fromObject(data); + JSONDocument document2 = JSONDocument.fromJson(document1.toJson()); Assertions.assertEquals(document1, document2); Assertions.assertEquals(document1.toJson(), document2.toJson()); } diff --git a/document-store/src/test/java/org/hypertrace/core/documentstore/metric/postgres/PostgresDocStoreMetricProviderTest.java b/document-store/src/test/java/org/hypertrace/core/documentstore/metric/postgres/PostgresDocStoreMetricProviderTest.java index 2f961a34f..e47fa1279 100644 --- a/document-store/src/test/java/org/hypertrace/core/documentstore/metric/postgres/PostgresDocStoreMetricProviderTest.java +++ b/document-store/src/test/java/org/hypertrace/core/documentstore/metric/postgres/PostgresDocStoreMetricProviderTest.java @@ -46,9 +46,12 @@ class PostgresDocStoreMetricProviderTest { (PostgresConnectionConfig) PostgresConnectionConfig.builder().applicationName(postgresClientAppName).build(); - @Mock private PostgresDatastore mockDatastore; - @Mock private Collection mockCollection; - @Mock private CloseableIterator mockIterator; + @Mock + private PostgresDatastore mockDatastore; + @Mock + private Collection mockCollection; + @Mock + private CloseableIterator mockIterator; private PostgresDocStoreMetricProvider postgresDocStoreMetricProvider; @@ -92,7 +95,7 @@ void withNoValueColumnIterator_returnsDefaultMetric() throws IOException { when(mockCollection.query(query, queryOptions)).thenReturn(mockIterator); when(mockIterator.hasNext()).thenReturn(true, false); when(mockIterator.next()) - .thenReturn(new JSONDocument(Map.of("application_name", postgresClientAppName))); + .thenReturn(JSONDocument.fromObject(Map.of("application_name", postgresClientAppName))); final DocStoreMetric result = postgresDocStoreMetricProvider.getConnectionCountMetric(); assertEquals(defaultMetric, result); } @@ -119,7 +122,7 @@ void withMultipleDocuments_returnsDefaultMetric() throws IOException { when(mockIterator.hasNext()).thenReturn(true, true, false); when(mockIterator.next()) .thenReturn( - new JSONDocument( + JSONDocument.fromObject( Map.of("application_name", postgresClientAppName, "metric_value", 1))); final DocStoreMetric result = postgresDocStoreMetricProvider.getConnectionCountMetric(); assertEquals(defaultMetric, result); @@ -133,8 +136,8 @@ void withMultipleInvalidDocumentsFollowedByValidDocument_returnsTheRightMetric() when(mockIterator.next()) .thenReturn( () -> "invalid-json", - new JSONDocument(Map.of("value_missing", postgresClientAppName)), - new JSONDocument( + JSONDocument.fromObject(Map.of("value_missing", postgresClientAppName)), + JSONDocument.fromObject( Map.of("application_name", postgresClientAppName, "metric_value", 1))); final DocStoreMetric expected = DocStoreMetric.builder() @@ -152,7 +155,7 @@ void withValidDocument_returnsTheRightMetric() throws IOException { when(mockIterator.hasNext()).thenReturn(true, false); when(mockIterator.next()) .thenReturn( - new JSONDocument( + JSONDocument.fromObject( Map.of("application_name", postgresClientAppName, "metric_value", 1))); final DocStoreMetric expected = DocStoreMetric.builder() diff --git a/document-store/src/test/java/org/hypertrace/core/documentstore/mongo/MongoCollectionTest.java b/document-store/src/test/java/org/hypertrace/core/documentstore/mongo/MongoCollectionTest.java index 41574369a..77b6967f8 100644 --- a/document-store/src/test/java/org/hypertrace/core/documentstore/mongo/MongoCollectionTest.java +++ b/document-store/src/test/java/org/hypertrace/core/documentstore/mongo/MongoCollectionTest.java @@ -215,7 +215,7 @@ class CreateOrReplaceTest { @Test void testCreateOrReplace() throws IOException { final Key key = Key.from("some-key"); - final Document document = new JSONDocument("{\"planet\": \"Mars\"}"); + final Document document = JSONDocument.fromJson("{\"planet\": \"Mars\"}"); @SuppressWarnings("unchecked") final ArgumentCaptor> valueCaptor = ArgumentCaptor.forClass(List.class); @@ -263,7 +263,7 @@ class UpdateTest { private final SubDocumentUpdate quantityUpdate = SubDocumentUpdate.of("quantity", 1000); private final SubDocumentUpdate propsUpdate = SubDocumentUpdate.of( - "props", SubDocumentValue.of(new JSONDocument("{\"brand\": \"Dettol\"}"))); + "props", SubDocumentValue.of(JSONDocument.fromJson("{\"brand\": \"Dettol\"}"))); private final BasicDBObject setObject = readBasicDBObject("atomic_read_and_update/set_object.json"); private final BasicDBObject selections = @@ -370,7 +370,7 @@ class BulkUpdateTest { private final SubDocumentUpdate quantityUpdate = SubDocumentUpdate.of("quantity", 1000); private final SubDocumentUpdate propsUpdate = SubDocumentUpdate.of( - "props", SubDocumentValue.of(new JSONDocument("{\"brand\": \"Dettol\"}"))); + "props", SubDocumentValue.of(JSONDocument.fromJson("{\"brand\": \"Dettol\"}"))); private final BasicDBObject setObject = readBasicDBObject("atomic_read_and_update/set_object.json"); private final BasicDBObject selections = diff --git a/document-store/src/test/java/org/hypertrace/core/documentstore/postgres/PostgresCollectionTest.java b/document-store/src/test/java/org/hypertrace/core/documentstore/postgres/PostgresCollectionTest.java index e94f6009b..e695d5753 100644 --- a/document-store/src/test/java/org/hypertrace/core/documentstore/postgres/PostgresCollectionTest.java +++ b/document-store/src/test/java/org/hypertrace/core/documentstore/postgres/PostgresCollectionTest.java @@ -59,6 +59,7 @@ @ExtendWith(MockitoExtension.class) class PostgresCollectionTest { + private static final String COLLECTION_NAME = "test_collection"; private static final long currentTime = 1658956123L; @@ -84,7 +85,7 @@ void setUp() { @Test void testCreateOrReplace() throws Exception { final Key key = Key.from("some_key"); - final Document document = new JSONDocument("{\"planet\": \"Mars\"}"); + final Document document = JSONDocument.fromJson("{\"planet\": \"Mars\"}"); when(mockClient.getConnection()).thenReturn(mockConnection); when(mockConnection.prepareStatement( @@ -923,7 +924,7 @@ private List buildUpdates() throws IOException { final SubDocumentUpdate quantityUpdate = SubDocumentUpdate.of("quantity", 1000); final SubDocumentUpdate propsUpdate = SubDocumentUpdate.of( - "props", SubDocumentValue.of(new JSONDocument("{\"brand\": \"Dettol\"}"))); + "props", SubDocumentValue.of(JSONDocument.fromJson("{\"brand\": \"Dettol\"}"))); return List.of(dateUpdate, quantityUpdate, propsUpdate); } diff --git a/document-store/src/test/java/org/hypertrace/core/documentstore/postgres/query/v1/PostgresQueryParserTest.java b/document-store/src/test/java/org/hypertrace/core/documentstore/postgres/query/v1/PostgresQueryParserTest.java index 816d92112..7799e94f2 100644 --- a/document-store/src/test/java/org/hypertrace/core/documentstore/postgres/query/v1/PostgresQueryParserTest.java +++ b/document-store/src/test/java/org/hypertrace/core/documentstore/postgres/query/v1/PostgresQueryParserTest.java @@ -1333,7 +1333,7 @@ void testContainsFilter() throws IOException { RelationalExpression.of( IdentifierExpression.of("sales"), CONTAINS, - ConstantExpression.of(new JSONDocument("\"a\"")))) + ConstantExpression.of(JSONDocument.fromJson("\"a\"")))) .build(); PostgresQueryParser postgresQueryParser = new PostgresQueryParser(TEST_TABLE, PostgresQueryTransformer.transform(query)); @@ -1359,7 +1359,7 @@ void testContainsAndUnnestFilters() throws IOException { IdentifierExpression.of("sales.medium"), CONTAINS, ConstantExpression.of( - new JSONDocument("{\"type\": \"retail\",\"volume\": 500}")))) + JSONDocument.fromJson("{\"type\": \"retail\",\"volume\": 500}")))) .build()) .build(); @@ -1394,7 +1394,7 @@ void testNotContainsAndUnnestFilters() throws IOException { IdentifierExpression.of("sales.medium"), NOT_CONTAINS, ConstantExpression.of( - new JSONDocument("{\"type\": \"retail\",\"volume\": 500}")))) + JSONDocument.fromJson("{\"type\": \"retail\",\"volume\": 500}")))) .build()) .build(); diff --git a/document-store/src/test/java/org/hypertrace/core/documentstore/util/TestUtil.java b/document-store/src/test/java/org/hypertrace/core/documentstore/util/TestUtil.java index 7d0006192..24bfee676 100644 --- a/document-store/src/test/java/org/hypertrace/core/documentstore/util/TestUtil.java +++ b/document-store/src/test/java/org/hypertrace/core/documentstore/util/TestUtil.java @@ -33,7 +33,7 @@ public static BasicDBObject readBasicDBObject(final String filePath) throws IOEx } public static Document readDocument(final String filePath) throws IOException { - return new JSONDocument(readFileFromResource(filePath).orElseThrow()); + return JSONDocument.fromJson(readFileFromResource(filePath).orElseThrow()); } @SuppressWarnings("unchecked") From f6cb67aea6f5d12ada81eaaa2634a56c8fc4452b Mon Sep 17 00:00:00 2001 From: Prashant Pandey Date: Mon, 22 Sep 2025 15:04:35 +0530 Subject: [PATCH 02/19] Revert "WIP" This reverts commit 674d2eec9261c29fc8447fd9c37b705103543bdf. --- .../mongo/MongoDocStoreTest.java | 6 +- .../core/documentstore/utils/Utils.java | 9 +- .../core/documentstore/Document.java | 2 - .../core/documentstore/DocumentType.java | 6 - .../core/documentstore/JSONDocument.java | 106 +++--------------- .../core/documentstore/mongo/MongoUtils.java | 2 +- .../postgres/PostgresCollection.java | 17 ++- .../postgres/utils/PostgresUtils.java | 7 +- .../core/documentstore/JSONDocumentTest.java | 4 +- .../PostgresDocStoreMetricProviderTest.java | 19 ++-- .../mongo/MongoCollectionTest.java | 6 +- .../postgres/PostgresCollectionTest.java | 5 +- .../query/v1/PostgresQueryParserTest.java | 6 +- .../core/documentstore/util/TestUtil.java | 2 +- 14 files changed, 54 insertions(+), 143 deletions(-) delete mode 100644 document-store/src/main/java/org/hypertrace/core/documentstore/DocumentType.java diff --git a/document-store/src/integrationTest/java/org/hypertrace/core/documentstore/mongo/MongoDocStoreTest.java b/document-store/src/integrationTest/java/org/hypertrace/core/documentstore/mongo/MongoDocStoreTest.java index 81157694d..488f77ae6 100644 --- a/document-store/src/integrationTest/java/org/hypertrace/core/documentstore/mongo/MongoDocStoreTest.java +++ b/document-store/src/integrationTest/java/org/hypertrace/core/documentstore/mongo/MongoDocStoreTest.java @@ -125,7 +125,7 @@ public void testUpsertAndReturn() throws IOException { Collection collection = datastore.getCollection(COLLECTION_NAME); ObjectNode objectNode = OBJECT_MAPPER.createObjectNode(); objectNode.put("foo1", "bar1"); - Document document = JSONDocument.fromObject(objectNode); + Document document = new JSONDocument(objectNode); Document persistedDocument = collection.upsertAndReturn(new SingleValueKey("default", "testKey"), document); @@ -140,7 +140,7 @@ public void testUpsertAndReturn() throws IOException { objectNode = OBJECT_MAPPER.createObjectNode(); objectNode.put("foo2", "bar2"); - document = JSONDocument.fromObject(objectNode); + document = new JSONDocument(objectNode); // Upsert again and verify that createdTime does not change, while lastUpdatedTime // has changed and values have merged @@ -161,7 +161,7 @@ public void testBulkUpsertAndVerifyUpdatedTime() throws IOException { Collection collection = datastore.getCollection(COLLECTION_NAME); ObjectNode objectNode = OBJECT_MAPPER.createObjectNode(); objectNode.put("foo1", "bar1"); - Document document = JSONDocument.fromObject(objectNode); + Document document = new JSONDocument(objectNode); collection.bulkUpsert(Map.of(new SingleValueKey("default", "testKey"), document)); Query query = new Query(); diff --git a/document-store/src/integrationTest/java/org/hypertrace/core/documentstore/utils/Utils.java b/document-store/src/integrationTest/java/org/hypertrace/core/documentstore/utils/Utils.java index 2449df011..75ea277f5 100644 --- a/document-store/src/integrationTest/java/org/hypertrace/core/documentstore/utils/Utils.java +++ b/document-store/src/integrationTest/java/org/hypertrace/core/documentstore/utils/Utils.java @@ -27,7 +27,6 @@ import org.testcontainers.shaded.org.apache.commons.io.IOUtils; public class Utils { - private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper(); public static final String MONGO_STORE = "Mongo"; @@ -63,13 +62,13 @@ public static Document createDocument(ImmutablePair... pairs) { objectNode.putPOJO(pairs[i].getLeft(), pairs[i].getRight()); } } - return JSONDocument.fromJsonNode(objectNode); + return new JSONDocument(objectNode); } public static Document createDocument(String key, String value) { ObjectNode objectNode = OBJECT_MAPPER.createObjectNode(); objectNode.put(key, value); - return JSONDocument.fromJsonNode(objectNode); + return new JSONDocument(objectNode); } public static Document createDocument(String... keys) { @@ -77,7 +76,7 @@ public static Document createDocument(String... keys) { for (int i = 0; i < keys.length - 1; i++) { objectNode.put(keys[i], keys[i + 1]); } - return JSONDocument.fromJsonNode(objectNode); + return new JSONDocument(objectNode); } public static Optional readFileFromResource(String filePath) throws IOException { @@ -136,7 +135,7 @@ public static Map buildDocumentsFromResource(String resourcePath) Map documentMap = new HashMap<>(); for (Map map : maps) { Key key = new SingleValueKey(TENANT_ID, map.get(MongoCollection.ID_KEY).toString()); - Document value = JSONDocument.fromObject(map); + Document value = new JSONDocument(map); documentMap.put(key, value); } diff --git a/document-store/src/main/java/org/hypertrace/core/documentstore/Document.java b/document-store/src/main/java/org/hypertrace/core/documentstore/Document.java index a9327779e..944913aac 100644 --- a/document-store/src/main/java/org/hypertrace/core/documentstore/Document.java +++ b/document-store/src/main/java/org/hypertrace/core/documentstore/Document.java @@ -3,6 +3,4 @@ public interface Document { String toJson(); - - DocumentType getDocumentType(); } diff --git a/document-store/src/main/java/org/hypertrace/core/documentstore/DocumentType.java b/document-store/src/main/java/org/hypertrace/core/documentstore/DocumentType.java deleted file mode 100644 index 745505ef2..000000000 --- a/document-store/src/main/java/org/hypertrace/core/documentstore/DocumentType.java +++ /dev/null @@ -1,6 +0,0 @@ -package org.hypertrace.core.documentstore; - -public enum DocumentType { - SQL_STORE, - DOCUMENT_STORE -} diff --git a/document-store/src/main/java/org/hypertrace/core/documentstore/JSONDocument.java b/document-store/src/main/java/org/hypertrace/core/documentstore/JSONDocument.java index 98f7973b4..c04d806de 100644 --- a/document-store/src/main/java/org/hypertrace/core/documentstore/JSONDocument.java +++ b/document-store/src/main/java/org/hypertrace/core/documentstore/JSONDocument.java @@ -4,35 +4,39 @@ import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.node.ObjectNode; -import com.google.common.base.Preconditions; import java.io.IOException; import java.util.Objects; -import lombok.Builder; -@Builder public class JSONDocument implements Document { - private static final ObjectMapper MAPPER = new ObjectMapper(); - private final JsonNode node; - private final DocumentType documentType; + private static ObjectMapper mapper = new ObjectMapper(); + private JsonNode node; - private JSONDocument(JsonNode node, DocumentType documentType) { - this.node = Preconditions.checkNotNull(node, "JsonNode cannot be null"); - this.documentType = Preconditions.checkNotNull(documentType, "DocumentType cannot be null"); + public JSONDocument(String json) throws IOException { + node = mapper.readTree(json); + } + + public JSONDocument(Object object) throws IOException { + node = mapper.readTree(mapper.writeValueAsString(object)); + } + + public JSONDocument(JsonNode node) { + this.node = node; } @Override public String toJson() { try { - return MAPPER.writeValueAsString(node); + return mapper.writeValueAsString(node); } catch (JsonProcessingException e) { return "{}"; } } - @Override - public DocumentType getDocumentType() { - return documentType; + public static JSONDocument errorDocument(String message) { + ObjectNode objectNode = mapper.createObjectNode(); + objectNode.put("errorMessage", message); + return new JSONDocument(objectNode); } @Override @@ -52,80 +56,4 @@ public boolean equals(Object obj) { JSONDocument other = (JSONDocument) obj; return Objects.equals(node, other.node); } - - @Override - public int hashCode() { - return Objects.hash(node, documentType); - } - - // Builder class - public static class Builder { - - private JsonNode node; - private DocumentType documentType = DocumentType.DOCUMENT_STORE; // default is DOCUMENT_STORE - - public JSONDocument.Builder fromJson(String json) throws IOException { - this.node = MAPPER.readTree(json); - return this; - } - - public JSONDocument.Builder fromObject(Object object) throws IOException { - this.node = MAPPER.readTree(MAPPER.writeValueAsString(object)); - return this; - } - - public JSONDocument.Builder fromJsonNode(JsonNode node) { - this.node = node; - return this; - } - - public JSONDocument.Builder withDocumentType(DocumentType documentType) { - this.documentType = documentType; - return this; - } - - public JSONDocument build() { - if (node == null) { - throw new IllegalStateException("JsonNode must be set before building"); - } - return new JSONDocument(node, documentType); - } - } - - public static JSONDocument fromJson(String json) throws IOException { - return new JSONDocument.Builder().fromJson(json).build(); - } - - public static JSONDocument fromJson(String json, DocumentType type) throws IOException { - return new JSONDocument.Builder().fromJson(json).withDocumentType(type).build(); - } - - public static JSONDocument fromObject(Object object) throws IOException { - return new JSONDocument.Builder().fromObject(object).build(); - } - - public static JSONDocument fromObject(Object object, DocumentType type) throws IOException { - return new JSONDocument.Builder().fromObject(object).withDocumentType(type).build(); - } - - public static JSONDocument fromJsonNode(JsonNode jsonNode) { - return new JSONDocument.Builder().fromJsonNode(jsonNode).build(); - } - - public static JSONDocument fromJsonNode(JsonNode jsonNode, DocumentType type) throws IOException { - return new JSONDocument.Builder().fromJsonNode(jsonNode).withDocumentType(type).build(); - } - - public static JSONDocument errorDocument(String message) { - return errorDocument(message, DocumentType.DOCUMENT_STORE); - } - - public static JSONDocument errorDocument(String message, DocumentType documentType) { - ObjectNode objectNode = MAPPER.createObjectNode(); - objectNode.put("errorMessage", message); - return new JSONDocument.Builder() - .fromJsonNode(objectNode) - .withDocumentType(documentType) - .build(); - } } diff --git a/document-store/src/main/java/org/hypertrace/core/documentstore/mongo/MongoUtils.java b/document-store/src/main/java/org/hypertrace/core/documentstore/mongo/MongoUtils.java index 2ed8a0ae3..f12868cff 100644 --- a/document-store/src/main/java/org/hypertrace/core/documentstore/mongo/MongoUtils.java +++ b/document-store/src/main/java/org/hypertrace/core/documentstore/mongo/MongoUtils.java @@ -137,7 +137,7 @@ public static Document dbObjectToDocument(BasicDBObject dbObject) { jsonString = dbObject.toJson(relaxed); JsonNode jsonNode = MAPPER.readTree(jsonString); JsonNode decodedJsonNode = recursiveClone(jsonNode, MongoUtils::decodeKey, identity()); - return JSONDocument.fromJsonNode(decodedJsonNode); + return new JSONDocument(decodedJsonNode); } catch (IOException e) { // throwing exception is not very useful here. return JSONDocument.errorDocument(e.getMessage()); 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 05460c1d6..5582889a3 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 @@ -61,7 +61,6 @@ import org.hypertrace.core.documentstore.Collection; import org.hypertrace.core.documentstore.CreateResult; import org.hypertrace.core.documentstore.Document; -import org.hypertrace.core.documentstore.DocumentType; import org.hypertrace.core.documentstore.Filter; import org.hypertrace.core.documentstore.JSONDocument; import org.hypertrace.core.documentstore.Key; @@ -880,7 +879,7 @@ private BulkUpdateResult bulkSetOnArrayValue( if (docRoot.isObject()) { ((ObjectNode) docRoot).put(DocStoreConstants.LAST_UPDATED_TIME, System.currentTimeMillis()); } - upsertMap.put(Key.from(id), JSONDocument.fromJsonNode(docRoot, DocumentType.SQL_STORE)); + upsertMap.put(Key.from(id), new JSONDocument(docRoot)); } return upsertDocs(upsertMap); } @@ -909,7 +908,7 @@ private BulkUpdateResult bulkRemoveOnArrayValue( candidateArray.removeAll(); candidateArray.addAll(existingItems); String id = docRoot.findValue(DOCUMENT_ID).asText(); - upsertMap.put(Key.from(id), JSONDocument.fromJsonNode(docRoot, DocumentType.SQL_STORE)); + upsertMap.put(Key.from(id), new JSONDocument(docRoot)); } return upsertDocs(upsertMap); } @@ -934,7 +933,7 @@ private BulkUpdateResult bulkAddOnArrayValue( if (docRoot.isObject()) { ((ObjectNode) docRoot).put(DocStoreConstants.LAST_UPDATED_TIME, System.currentTimeMillis()); } - upsertMap.put(Key.from(id), JSONDocument.fromJsonNode(docRoot, DocumentType.SQL_STORE)); + upsertMap.put(Key.from(id), new JSONDocument(docRoot)); } return upsertDocs(upsertMap); } @@ -1277,7 +1276,7 @@ public Document next() { } catch (IOException | SQLException e) { System.out.println("prepare document failed!"); closeResultSet(); - return JSONDocument.errorDocument(e.getMessage(), DocumentType.SQL_STORE); + return JSONDocument.errorDocument(e.getMessage()); } } @@ -1300,7 +1299,7 @@ protected Document prepareDocument() throws SQLException, IOException { jsonNode.remove(DOCUMENT_ID); } - return JSONDocument.fromJson(MAPPER.writeValueAsString(jsonNode), DocumentType.SQL_STORE); + return new JSONDocument(MAPPER.writeValueAsString(jsonNode)); } private void addColumnToJsonNode( @@ -1431,7 +1430,7 @@ public Document next() { return prepareDocument(); } catch (IOException | SQLException e) { closeResultSet(); - return JSONDocument.errorDocument(e.getMessage(), DocumentType.SQL_STORE); + return JSONDocument.errorDocument(e.getMessage()); } } @@ -1448,7 +1447,7 @@ protected Document prepareDocument() throws SQLException, IOException { jsonNode.put(CREATED_AT, String.valueOf(createdAt)); jsonNode.put(UPDATED_AT, String.valueOf(updatedAt)); - return JSONDocument.fromJson(MAPPER.writeValueAsString(jsonNode), DocumentType.SQL_STORE); + return new JSONDocument(MAPPER.writeValueAsString(jsonNode)); } protected void closeResultSet() { @@ -1509,7 +1508,7 @@ protected Document prepareDocument() throws SQLException, IOException { } } } - return JSONDocument.fromJson(MAPPER.writeValueAsString(jsonNode), DocumentType.SQL_STORE); + return new JSONDocument(MAPPER.writeValueAsString(jsonNode)); } private String getColumnValue( 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 8e5b94e4b..24b8ffc6d 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 @@ -36,7 +36,6 @@ @Slf4j public class PostgresUtils { - public static final String JSON_FIELD_ACCESSOR = "->"; private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper(); private static final String QUESTION_MARK = "?"; @@ -134,9 +133,8 @@ public static String prepareCast(String field, Type type) { public static String prepareCastForFieldAccessor(String field, Object value) { String fmt = "CAST (%s AS %s)"; Type type = getType(value); - if (type.equals(Type.STRING) || type.equals(Type.STRING_ARRAY)) { + if (type.equals(Type.STRING) || type.equals(Type.STRING_ARRAY)) return String.format(fmt, field, "TEXT"); - } return prepareCast(field, type); } @@ -589,8 +587,7 @@ public static DocumentAndId extractAndRemoveId(final Document document) throws I new ObjectMapper() .readValue(document.toJson(), new TypeReference>() {}); final String id = String.valueOf(requireNonNull(map.remove(IMPLICIT_ID))); - final Document documentWithoutId = - JSONDocument.fromJson(new ObjectMapper().writeValueAsString(map)); + final Document documentWithoutId = new JSONDocument(new ObjectMapper().writeValueAsString(map)); return new DocumentAndId(documentWithoutId, id); } diff --git a/document-store/src/test/java/org/hypertrace/core/documentstore/JSONDocumentTest.java b/document-store/src/test/java/org/hypertrace/core/documentstore/JSONDocumentTest.java index 407d8c0aa..5cd3be9ac 100644 --- a/document-store/src/test/java/org/hypertrace/core/documentstore/JSONDocumentTest.java +++ b/document-store/src/test/java/org/hypertrace/core/documentstore/JSONDocumentTest.java @@ -9,8 +9,8 @@ public class JSONDocumentTest { @Test public void testJSONDocument() throws Exception { Map data = Map.of("key1", "value1", "key2", "value2"); - JSONDocument document1 = JSONDocument.fromObject(data); - JSONDocument document2 = JSONDocument.fromJson(document1.toJson()); + JSONDocument document1 = new JSONDocument(data); + JSONDocument document2 = new JSONDocument(document1.toJson()); Assertions.assertEquals(document1, document2); Assertions.assertEquals(document1.toJson(), document2.toJson()); } diff --git a/document-store/src/test/java/org/hypertrace/core/documentstore/metric/postgres/PostgresDocStoreMetricProviderTest.java b/document-store/src/test/java/org/hypertrace/core/documentstore/metric/postgres/PostgresDocStoreMetricProviderTest.java index e47fa1279..2f961a34f 100644 --- a/document-store/src/test/java/org/hypertrace/core/documentstore/metric/postgres/PostgresDocStoreMetricProviderTest.java +++ b/document-store/src/test/java/org/hypertrace/core/documentstore/metric/postgres/PostgresDocStoreMetricProviderTest.java @@ -46,12 +46,9 @@ class PostgresDocStoreMetricProviderTest { (PostgresConnectionConfig) PostgresConnectionConfig.builder().applicationName(postgresClientAppName).build(); - @Mock - private PostgresDatastore mockDatastore; - @Mock - private Collection mockCollection; - @Mock - private CloseableIterator mockIterator; + @Mock private PostgresDatastore mockDatastore; + @Mock private Collection mockCollection; + @Mock private CloseableIterator mockIterator; private PostgresDocStoreMetricProvider postgresDocStoreMetricProvider; @@ -95,7 +92,7 @@ void withNoValueColumnIterator_returnsDefaultMetric() throws IOException { when(mockCollection.query(query, queryOptions)).thenReturn(mockIterator); when(mockIterator.hasNext()).thenReturn(true, false); when(mockIterator.next()) - .thenReturn(JSONDocument.fromObject(Map.of("application_name", postgresClientAppName))); + .thenReturn(new JSONDocument(Map.of("application_name", postgresClientAppName))); final DocStoreMetric result = postgresDocStoreMetricProvider.getConnectionCountMetric(); assertEquals(defaultMetric, result); } @@ -122,7 +119,7 @@ void withMultipleDocuments_returnsDefaultMetric() throws IOException { when(mockIterator.hasNext()).thenReturn(true, true, false); when(mockIterator.next()) .thenReturn( - JSONDocument.fromObject( + new JSONDocument( Map.of("application_name", postgresClientAppName, "metric_value", 1))); final DocStoreMetric result = postgresDocStoreMetricProvider.getConnectionCountMetric(); assertEquals(defaultMetric, result); @@ -136,8 +133,8 @@ void withMultipleInvalidDocumentsFollowedByValidDocument_returnsTheRightMetric() when(mockIterator.next()) .thenReturn( () -> "invalid-json", - JSONDocument.fromObject(Map.of("value_missing", postgresClientAppName)), - JSONDocument.fromObject( + new JSONDocument(Map.of("value_missing", postgresClientAppName)), + new JSONDocument( Map.of("application_name", postgresClientAppName, "metric_value", 1))); final DocStoreMetric expected = DocStoreMetric.builder() @@ -155,7 +152,7 @@ void withValidDocument_returnsTheRightMetric() throws IOException { when(mockIterator.hasNext()).thenReturn(true, false); when(mockIterator.next()) .thenReturn( - JSONDocument.fromObject( + new JSONDocument( Map.of("application_name", postgresClientAppName, "metric_value", 1))); final DocStoreMetric expected = DocStoreMetric.builder() diff --git a/document-store/src/test/java/org/hypertrace/core/documentstore/mongo/MongoCollectionTest.java b/document-store/src/test/java/org/hypertrace/core/documentstore/mongo/MongoCollectionTest.java index 77b6967f8..41574369a 100644 --- a/document-store/src/test/java/org/hypertrace/core/documentstore/mongo/MongoCollectionTest.java +++ b/document-store/src/test/java/org/hypertrace/core/documentstore/mongo/MongoCollectionTest.java @@ -215,7 +215,7 @@ class CreateOrReplaceTest { @Test void testCreateOrReplace() throws IOException { final Key key = Key.from("some-key"); - final Document document = JSONDocument.fromJson("{\"planet\": \"Mars\"}"); + final Document document = new JSONDocument("{\"planet\": \"Mars\"}"); @SuppressWarnings("unchecked") final ArgumentCaptor> valueCaptor = ArgumentCaptor.forClass(List.class); @@ -263,7 +263,7 @@ class UpdateTest { private final SubDocumentUpdate quantityUpdate = SubDocumentUpdate.of("quantity", 1000); private final SubDocumentUpdate propsUpdate = SubDocumentUpdate.of( - "props", SubDocumentValue.of(JSONDocument.fromJson("{\"brand\": \"Dettol\"}"))); + "props", SubDocumentValue.of(new JSONDocument("{\"brand\": \"Dettol\"}"))); private final BasicDBObject setObject = readBasicDBObject("atomic_read_and_update/set_object.json"); private final BasicDBObject selections = @@ -370,7 +370,7 @@ class BulkUpdateTest { private final SubDocumentUpdate quantityUpdate = SubDocumentUpdate.of("quantity", 1000); private final SubDocumentUpdate propsUpdate = SubDocumentUpdate.of( - "props", SubDocumentValue.of(JSONDocument.fromJson("{\"brand\": \"Dettol\"}"))); + "props", SubDocumentValue.of(new JSONDocument("{\"brand\": \"Dettol\"}"))); private final BasicDBObject setObject = readBasicDBObject("atomic_read_and_update/set_object.json"); private final BasicDBObject selections = diff --git a/document-store/src/test/java/org/hypertrace/core/documentstore/postgres/PostgresCollectionTest.java b/document-store/src/test/java/org/hypertrace/core/documentstore/postgres/PostgresCollectionTest.java index e695d5753..e94f6009b 100644 --- a/document-store/src/test/java/org/hypertrace/core/documentstore/postgres/PostgresCollectionTest.java +++ b/document-store/src/test/java/org/hypertrace/core/documentstore/postgres/PostgresCollectionTest.java @@ -59,7 +59,6 @@ @ExtendWith(MockitoExtension.class) class PostgresCollectionTest { - private static final String COLLECTION_NAME = "test_collection"; private static final long currentTime = 1658956123L; @@ -85,7 +84,7 @@ void setUp() { @Test void testCreateOrReplace() throws Exception { final Key key = Key.from("some_key"); - final Document document = JSONDocument.fromJson("{\"planet\": \"Mars\"}"); + final Document document = new JSONDocument("{\"planet\": \"Mars\"}"); when(mockClient.getConnection()).thenReturn(mockConnection); when(mockConnection.prepareStatement( @@ -924,7 +923,7 @@ private List buildUpdates() throws IOException { final SubDocumentUpdate quantityUpdate = SubDocumentUpdate.of("quantity", 1000); final SubDocumentUpdate propsUpdate = SubDocumentUpdate.of( - "props", SubDocumentValue.of(JSONDocument.fromJson("{\"brand\": \"Dettol\"}"))); + "props", SubDocumentValue.of(new JSONDocument("{\"brand\": \"Dettol\"}"))); return List.of(dateUpdate, quantityUpdate, propsUpdate); } diff --git a/document-store/src/test/java/org/hypertrace/core/documentstore/postgres/query/v1/PostgresQueryParserTest.java b/document-store/src/test/java/org/hypertrace/core/documentstore/postgres/query/v1/PostgresQueryParserTest.java index 7799e94f2..816d92112 100644 --- a/document-store/src/test/java/org/hypertrace/core/documentstore/postgres/query/v1/PostgresQueryParserTest.java +++ b/document-store/src/test/java/org/hypertrace/core/documentstore/postgres/query/v1/PostgresQueryParserTest.java @@ -1333,7 +1333,7 @@ void testContainsFilter() throws IOException { RelationalExpression.of( IdentifierExpression.of("sales"), CONTAINS, - ConstantExpression.of(JSONDocument.fromJson("\"a\"")))) + ConstantExpression.of(new JSONDocument("\"a\"")))) .build(); PostgresQueryParser postgresQueryParser = new PostgresQueryParser(TEST_TABLE, PostgresQueryTransformer.transform(query)); @@ -1359,7 +1359,7 @@ void testContainsAndUnnestFilters() throws IOException { IdentifierExpression.of("sales.medium"), CONTAINS, ConstantExpression.of( - JSONDocument.fromJson("{\"type\": \"retail\",\"volume\": 500}")))) + new JSONDocument("{\"type\": \"retail\",\"volume\": 500}")))) .build()) .build(); @@ -1394,7 +1394,7 @@ void testNotContainsAndUnnestFilters() throws IOException { IdentifierExpression.of("sales.medium"), NOT_CONTAINS, ConstantExpression.of( - JSONDocument.fromJson("{\"type\": \"retail\",\"volume\": 500}")))) + new JSONDocument("{\"type\": \"retail\",\"volume\": 500}")))) .build()) .build(); diff --git a/document-store/src/test/java/org/hypertrace/core/documentstore/util/TestUtil.java b/document-store/src/test/java/org/hypertrace/core/documentstore/util/TestUtil.java index 24bfee676..7d0006192 100644 --- a/document-store/src/test/java/org/hypertrace/core/documentstore/util/TestUtil.java +++ b/document-store/src/test/java/org/hypertrace/core/documentstore/util/TestUtil.java @@ -33,7 +33,7 @@ public static BasicDBObject readBasicDBObject(final String filePath) throws IOEx } public static Document readDocument(final String filePath) throws IOException { - return JSONDocument.fromJson(readFileFromResource(filePath).orElseThrow()); + return new JSONDocument(readFileFromResource(filePath).orElseThrow()); } @SuppressWarnings("unchecked") From 6e7795d4519ba37e012d41271944497b0743601e Mon Sep 17 00:00:00 2001 From: Prashant Pandey Date: Mon, 22 Sep 2025 15:05:25 +0530 Subject: [PATCH 03/19] Reapply "WIP" This reverts commit f6cb67aea6f5d12ada81eaaa2634a56c8fc4452b. --- .../mongo/MongoDocStoreTest.java | 6 +- .../core/documentstore/utils/Utils.java | 9 +- .../core/documentstore/Document.java | 2 + .../core/documentstore/DocumentType.java | 6 + .../core/documentstore/JSONDocument.java | 106 +++++++++++++++--- .../core/documentstore/mongo/MongoUtils.java | 2 +- .../postgres/PostgresCollection.java | 17 +-- .../postgres/utils/PostgresUtils.java | 7 +- .../core/documentstore/JSONDocumentTest.java | 4 +- .../PostgresDocStoreMetricProviderTest.java | 19 ++-- .../mongo/MongoCollectionTest.java | 6 +- .../postgres/PostgresCollectionTest.java | 5 +- .../query/v1/PostgresQueryParserTest.java | 6 +- .../core/documentstore/util/TestUtil.java | 2 +- 14 files changed, 143 insertions(+), 54 deletions(-) create mode 100644 document-store/src/main/java/org/hypertrace/core/documentstore/DocumentType.java diff --git a/document-store/src/integrationTest/java/org/hypertrace/core/documentstore/mongo/MongoDocStoreTest.java b/document-store/src/integrationTest/java/org/hypertrace/core/documentstore/mongo/MongoDocStoreTest.java index 488f77ae6..81157694d 100644 --- a/document-store/src/integrationTest/java/org/hypertrace/core/documentstore/mongo/MongoDocStoreTest.java +++ b/document-store/src/integrationTest/java/org/hypertrace/core/documentstore/mongo/MongoDocStoreTest.java @@ -125,7 +125,7 @@ public void testUpsertAndReturn() throws IOException { Collection collection = datastore.getCollection(COLLECTION_NAME); ObjectNode objectNode = OBJECT_MAPPER.createObjectNode(); objectNode.put("foo1", "bar1"); - Document document = new JSONDocument(objectNode); + Document document = JSONDocument.fromObject(objectNode); Document persistedDocument = collection.upsertAndReturn(new SingleValueKey("default", "testKey"), document); @@ -140,7 +140,7 @@ public void testUpsertAndReturn() throws IOException { objectNode = OBJECT_MAPPER.createObjectNode(); objectNode.put("foo2", "bar2"); - document = new JSONDocument(objectNode); + document = JSONDocument.fromObject(objectNode); // Upsert again and verify that createdTime does not change, while lastUpdatedTime // has changed and values have merged @@ -161,7 +161,7 @@ public void testBulkUpsertAndVerifyUpdatedTime() throws IOException { Collection collection = datastore.getCollection(COLLECTION_NAME); ObjectNode objectNode = OBJECT_MAPPER.createObjectNode(); objectNode.put("foo1", "bar1"); - Document document = new JSONDocument(objectNode); + Document document = JSONDocument.fromObject(objectNode); collection.bulkUpsert(Map.of(new SingleValueKey("default", "testKey"), document)); Query query = new Query(); diff --git a/document-store/src/integrationTest/java/org/hypertrace/core/documentstore/utils/Utils.java b/document-store/src/integrationTest/java/org/hypertrace/core/documentstore/utils/Utils.java index 75ea277f5..2449df011 100644 --- a/document-store/src/integrationTest/java/org/hypertrace/core/documentstore/utils/Utils.java +++ b/document-store/src/integrationTest/java/org/hypertrace/core/documentstore/utils/Utils.java @@ -27,6 +27,7 @@ import org.testcontainers.shaded.org.apache.commons.io.IOUtils; public class Utils { + private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper(); public static final String MONGO_STORE = "Mongo"; @@ -62,13 +63,13 @@ public static Document createDocument(ImmutablePair... pairs) { objectNode.putPOJO(pairs[i].getLeft(), pairs[i].getRight()); } } - return new JSONDocument(objectNode); + return JSONDocument.fromJsonNode(objectNode); } public static Document createDocument(String key, String value) { ObjectNode objectNode = OBJECT_MAPPER.createObjectNode(); objectNode.put(key, value); - return new JSONDocument(objectNode); + return JSONDocument.fromJsonNode(objectNode); } public static Document createDocument(String... keys) { @@ -76,7 +77,7 @@ public static Document createDocument(String... keys) { for (int i = 0; i < keys.length - 1; i++) { objectNode.put(keys[i], keys[i + 1]); } - return new JSONDocument(objectNode); + return JSONDocument.fromJsonNode(objectNode); } public static Optional readFileFromResource(String filePath) throws IOException { @@ -135,7 +136,7 @@ public static Map buildDocumentsFromResource(String resourcePath) Map documentMap = new HashMap<>(); for (Map map : maps) { Key key = new SingleValueKey(TENANT_ID, map.get(MongoCollection.ID_KEY).toString()); - Document value = new JSONDocument(map); + Document value = JSONDocument.fromObject(map); documentMap.put(key, value); } diff --git a/document-store/src/main/java/org/hypertrace/core/documentstore/Document.java b/document-store/src/main/java/org/hypertrace/core/documentstore/Document.java index 944913aac..a9327779e 100644 --- a/document-store/src/main/java/org/hypertrace/core/documentstore/Document.java +++ b/document-store/src/main/java/org/hypertrace/core/documentstore/Document.java @@ -3,4 +3,6 @@ public interface Document { String toJson(); + + DocumentType getDocumentType(); } diff --git a/document-store/src/main/java/org/hypertrace/core/documentstore/DocumentType.java b/document-store/src/main/java/org/hypertrace/core/documentstore/DocumentType.java new file mode 100644 index 000000000..745505ef2 --- /dev/null +++ b/document-store/src/main/java/org/hypertrace/core/documentstore/DocumentType.java @@ -0,0 +1,6 @@ +package org.hypertrace.core.documentstore; + +public enum DocumentType { + SQL_STORE, + DOCUMENT_STORE +} diff --git a/document-store/src/main/java/org/hypertrace/core/documentstore/JSONDocument.java b/document-store/src/main/java/org/hypertrace/core/documentstore/JSONDocument.java index c04d806de..98f7973b4 100644 --- a/document-store/src/main/java/org/hypertrace/core/documentstore/JSONDocument.java +++ b/document-store/src/main/java/org/hypertrace/core/documentstore/JSONDocument.java @@ -4,39 +4,35 @@ import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.node.ObjectNode; +import com.google.common.base.Preconditions; import java.io.IOException; import java.util.Objects; +import lombok.Builder; +@Builder public class JSONDocument implements Document { - private static ObjectMapper mapper = new ObjectMapper(); - private JsonNode node; + private static final ObjectMapper MAPPER = new ObjectMapper(); + private final JsonNode node; + private final DocumentType documentType; - public JSONDocument(String json) throws IOException { - node = mapper.readTree(json); - } - - public JSONDocument(Object object) throws IOException { - node = mapper.readTree(mapper.writeValueAsString(object)); - } - - public JSONDocument(JsonNode node) { - this.node = node; + private JSONDocument(JsonNode node, DocumentType documentType) { + this.node = Preconditions.checkNotNull(node, "JsonNode cannot be null"); + this.documentType = Preconditions.checkNotNull(documentType, "DocumentType cannot be null"); } @Override public String toJson() { try { - return mapper.writeValueAsString(node); + return MAPPER.writeValueAsString(node); } catch (JsonProcessingException e) { return "{}"; } } - public static JSONDocument errorDocument(String message) { - ObjectNode objectNode = mapper.createObjectNode(); - objectNode.put("errorMessage", message); - return new JSONDocument(objectNode); + @Override + public DocumentType getDocumentType() { + return documentType; } @Override @@ -56,4 +52,80 @@ public boolean equals(Object obj) { JSONDocument other = (JSONDocument) obj; return Objects.equals(node, other.node); } + + @Override + public int hashCode() { + return Objects.hash(node, documentType); + } + + // Builder class + public static class Builder { + + private JsonNode node; + private DocumentType documentType = DocumentType.DOCUMENT_STORE; // default is DOCUMENT_STORE + + public JSONDocument.Builder fromJson(String json) throws IOException { + this.node = MAPPER.readTree(json); + return this; + } + + public JSONDocument.Builder fromObject(Object object) throws IOException { + this.node = MAPPER.readTree(MAPPER.writeValueAsString(object)); + return this; + } + + public JSONDocument.Builder fromJsonNode(JsonNode node) { + this.node = node; + return this; + } + + public JSONDocument.Builder withDocumentType(DocumentType documentType) { + this.documentType = documentType; + return this; + } + + public JSONDocument build() { + if (node == null) { + throw new IllegalStateException("JsonNode must be set before building"); + } + return new JSONDocument(node, documentType); + } + } + + public static JSONDocument fromJson(String json) throws IOException { + return new JSONDocument.Builder().fromJson(json).build(); + } + + public static JSONDocument fromJson(String json, DocumentType type) throws IOException { + return new JSONDocument.Builder().fromJson(json).withDocumentType(type).build(); + } + + public static JSONDocument fromObject(Object object) throws IOException { + return new JSONDocument.Builder().fromObject(object).build(); + } + + public static JSONDocument fromObject(Object object, DocumentType type) throws IOException { + return new JSONDocument.Builder().fromObject(object).withDocumentType(type).build(); + } + + public static JSONDocument fromJsonNode(JsonNode jsonNode) { + return new JSONDocument.Builder().fromJsonNode(jsonNode).build(); + } + + public static JSONDocument fromJsonNode(JsonNode jsonNode, DocumentType type) throws IOException { + return new JSONDocument.Builder().fromJsonNode(jsonNode).withDocumentType(type).build(); + } + + public static JSONDocument errorDocument(String message) { + return errorDocument(message, DocumentType.DOCUMENT_STORE); + } + + public static JSONDocument errorDocument(String message, DocumentType documentType) { + ObjectNode objectNode = MAPPER.createObjectNode(); + objectNode.put("errorMessage", message); + return new JSONDocument.Builder() + .fromJsonNode(objectNode) + .withDocumentType(documentType) + .build(); + } } diff --git a/document-store/src/main/java/org/hypertrace/core/documentstore/mongo/MongoUtils.java b/document-store/src/main/java/org/hypertrace/core/documentstore/mongo/MongoUtils.java index f12868cff..2ed8a0ae3 100644 --- a/document-store/src/main/java/org/hypertrace/core/documentstore/mongo/MongoUtils.java +++ b/document-store/src/main/java/org/hypertrace/core/documentstore/mongo/MongoUtils.java @@ -137,7 +137,7 @@ public static Document dbObjectToDocument(BasicDBObject dbObject) { jsonString = dbObject.toJson(relaxed); JsonNode jsonNode = MAPPER.readTree(jsonString); JsonNode decodedJsonNode = recursiveClone(jsonNode, MongoUtils::decodeKey, identity()); - return new JSONDocument(decodedJsonNode); + return JSONDocument.fromJsonNode(decodedJsonNode); } catch (IOException e) { // throwing exception is not very useful here. return JSONDocument.errorDocument(e.getMessage()); 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 5582889a3..05460c1d6 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 @@ -61,6 +61,7 @@ import org.hypertrace.core.documentstore.Collection; import org.hypertrace.core.documentstore.CreateResult; import org.hypertrace.core.documentstore.Document; +import org.hypertrace.core.documentstore.DocumentType; import org.hypertrace.core.documentstore.Filter; import org.hypertrace.core.documentstore.JSONDocument; import org.hypertrace.core.documentstore.Key; @@ -879,7 +880,7 @@ private BulkUpdateResult bulkSetOnArrayValue( if (docRoot.isObject()) { ((ObjectNode) docRoot).put(DocStoreConstants.LAST_UPDATED_TIME, System.currentTimeMillis()); } - upsertMap.put(Key.from(id), new JSONDocument(docRoot)); + upsertMap.put(Key.from(id), JSONDocument.fromJsonNode(docRoot, DocumentType.SQL_STORE)); } return upsertDocs(upsertMap); } @@ -908,7 +909,7 @@ private BulkUpdateResult bulkRemoveOnArrayValue( candidateArray.removeAll(); candidateArray.addAll(existingItems); String id = docRoot.findValue(DOCUMENT_ID).asText(); - upsertMap.put(Key.from(id), new JSONDocument(docRoot)); + upsertMap.put(Key.from(id), JSONDocument.fromJsonNode(docRoot, DocumentType.SQL_STORE)); } return upsertDocs(upsertMap); } @@ -933,7 +934,7 @@ private BulkUpdateResult bulkAddOnArrayValue( if (docRoot.isObject()) { ((ObjectNode) docRoot).put(DocStoreConstants.LAST_UPDATED_TIME, System.currentTimeMillis()); } - upsertMap.put(Key.from(id), new JSONDocument(docRoot)); + upsertMap.put(Key.from(id), JSONDocument.fromJsonNode(docRoot, DocumentType.SQL_STORE)); } return upsertDocs(upsertMap); } @@ -1276,7 +1277,7 @@ public Document next() { } catch (IOException | SQLException e) { System.out.println("prepare document failed!"); closeResultSet(); - return JSONDocument.errorDocument(e.getMessage()); + return JSONDocument.errorDocument(e.getMessage(), DocumentType.SQL_STORE); } } @@ -1299,7 +1300,7 @@ protected Document prepareDocument() throws SQLException, IOException { jsonNode.remove(DOCUMENT_ID); } - return new JSONDocument(MAPPER.writeValueAsString(jsonNode)); + return JSONDocument.fromJson(MAPPER.writeValueAsString(jsonNode), DocumentType.SQL_STORE); } private void addColumnToJsonNode( @@ -1430,7 +1431,7 @@ public Document next() { return prepareDocument(); } catch (IOException | SQLException e) { closeResultSet(); - return JSONDocument.errorDocument(e.getMessage()); + return JSONDocument.errorDocument(e.getMessage(), DocumentType.SQL_STORE); } } @@ -1447,7 +1448,7 @@ protected Document prepareDocument() throws SQLException, IOException { jsonNode.put(CREATED_AT, String.valueOf(createdAt)); jsonNode.put(UPDATED_AT, String.valueOf(updatedAt)); - return new JSONDocument(MAPPER.writeValueAsString(jsonNode)); + return JSONDocument.fromJson(MAPPER.writeValueAsString(jsonNode), DocumentType.SQL_STORE); } protected void closeResultSet() { @@ -1508,7 +1509,7 @@ protected Document prepareDocument() throws SQLException, IOException { } } } - return new JSONDocument(MAPPER.writeValueAsString(jsonNode)); + return JSONDocument.fromJson(MAPPER.writeValueAsString(jsonNode), DocumentType.SQL_STORE); } private String getColumnValue( 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 24b8ffc6d..8e5b94e4b 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 @@ -36,6 +36,7 @@ @Slf4j public class PostgresUtils { + public static final String JSON_FIELD_ACCESSOR = "->"; private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper(); private static final String QUESTION_MARK = "?"; @@ -133,8 +134,9 @@ public static String prepareCast(String field, Type type) { public static String prepareCastForFieldAccessor(String field, Object value) { String fmt = "CAST (%s AS %s)"; Type type = getType(value); - if (type.equals(Type.STRING) || type.equals(Type.STRING_ARRAY)) + if (type.equals(Type.STRING) || type.equals(Type.STRING_ARRAY)) { return String.format(fmt, field, "TEXT"); + } return prepareCast(field, type); } @@ -587,7 +589,8 @@ public static DocumentAndId extractAndRemoveId(final Document document) throws I new ObjectMapper() .readValue(document.toJson(), new TypeReference>() {}); final String id = String.valueOf(requireNonNull(map.remove(IMPLICIT_ID))); - final Document documentWithoutId = new JSONDocument(new ObjectMapper().writeValueAsString(map)); + final Document documentWithoutId = + JSONDocument.fromJson(new ObjectMapper().writeValueAsString(map)); return new DocumentAndId(documentWithoutId, id); } diff --git a/document-store/src/test/java/org/hypertrace/core/documentstore/JSONDocumentTest.java b/document-store/src/test/java/org/hypertrace/core/documentstore/JSONDocumentTest.java index 5cd3be9ac..407d8c0aa 100644 --- a/document-store/src/test/java/org/hypertrace/core/documentstore/JSONDocumentTest.java +++ b/document-store/src/test/java/org/hypertrace/core/documentstore/JSONDocumentTest.java @@ -9,8 +9,8 @@ public class JSONDocumentTest { @Test public void testJSONDocument() throws Exception { Map data = Map.of("key1", "value1", "key2", "value2"); - JSONDocument document1 = new JSONDocument(data); - JSONDocument document2 = new JSONDocument(document1.toJson()); + JSONDocument document1 = JSONDocument.fromObject(data); + JSONDocument document2 = JSONDocument.fromJson(document1.toJson()); Assertions.assertEquals(document1, document2); Assertions.assertEquals(document1.toJson(), document2.toJson()); } diff --git a/document-store/src/test/java/org/hypertrace/core/documentstore/metric/postgres/PostgresDocStoreMetricProviderTest.java b/document-store/src/test/java/org/hypertrace/core/documentstore/metric/postgres/PostgresDocStoreMetricProviderTest.java index 2f961a34f..e47fa1279 100644 --- a/document-store/src/test/java/org/hypertrace/core/documentstore/metric/postgres/PostgresDocStoreMetricProviderTest.java +++ b/document-store/src/test/java/org/hypertrace/core/documentstore/metric/postgres/PostgresDocStoreMetricProviderTest.java @@ -46,9 +46,12 @@ class PostgresDocStoreMetricProviderTest { (PostgresConnectionConfig) PostgresConnectionConfig.builder().applicationName(postgresClientAppName).build(); - @Mock private PostgresDatastore mockDatastore; - @Mock private Collection mockCollection; - @Mock private CloseableIterator mockIterator; + @Mock + private PostgresDatastore mockDatastore; + @Mock + private Collection mockCollection; + @Mock + private CloseableIterator mockIterator; private PostgresDocStoreMetricProvider postgresDocStoreMetricProvider; @@ -92,7 +95,7 @@ void withNoValueColumnIterator_returnsDefaultMetric() throws IOException { when(mockCollection.query(query, queryOptions)).thenReturn(mockIterator); when(mockIterator.hasNext()).thenReturn(true, false); when(mockIterator.next()) - .thenReturn(new JSONDocument(Map.of("application_name", postgresClientAppName))); + .thenReturn(JSONDocument.fromObject(Map.of("application_name", postgresClientAppName))); final DocStoreMetric result = postgresDocStoreMetricProvider.getConnectionCountMetric(); assertEquals(defaultMetric, result); } @@ -119,7 +122,7 @@ void withMultipleDocuments_returnsDefaultMetric() throws IOException { when(mockIterator.hasNext()).thenReturn(true, true, false); when(mockIterator.next()) .thenReturn( - new JSONDocument( + JSONDocument.fromObject( Map.of("application_name", postgresClientAppName, "metric_value", 1))); final DocStoreMetric result = postgresDocStoreMetricProvider.getConnectionCountMetric(); assertEquals(defaultMetric, result); @@ -133,8 +136,8 @@ void withMultipleInvalidDocumentsFollowedByValidDocument_returnsTheRightMetric() when(mockIterator.next()) .thenReturn( () -> "invalid-json", - new JSONDocument(Map.of("value_missing", postgresClientAppName)), - new JSONDocument( + JSONDocument.fromObject(Map.of("value_missing", postgresClientAppName)), + JSONDocument.fromObject( Map.of("application_name", postgresClientAppName, "metric_value", 1))); final DocStoreMetric expected = DocStoreMetric.builder() @@ -152,7 +155,7 @@ void withValidDocument_returnsTheRightMetric() throws IOException { when(mockIterator.hasNext()).thenReturn(true, false); when(mockIterator.next()) .thenReturn( - new JSONDocument( + JSONDocument.fromObject( Map.of("application_name", postgresClientAppName, "metric_value", 1))); final DocStoreMetric expected = DocStoreMetric.builder() diff --git a/document-store/src/test/java/org/hypertrace/core/documentstore/mongo/MongoCollectionTest.java b/document-store/src/test/java/org/hypertrace/core/documentstore/mongo/MongoCollectionTest.java index 41574369a..77b6967f8 100644 --- a/document-store/src/test/java/org/hypertrace/core/documentstore/mongo/MongoCollectionTest.java +++ b/document-store/src/test/java/org/hypertrace/core/documentstore/mongo/MongoCollectionTest.java @@ -215,7 +215,7 @@ class CreateOrReplaceTest { @Test void testCreateOrReplace() throws IOException { final Key key = Key.from("some-key"); - final Document document = new JSONDocument("{\"planet\": \"Mars\"}"); + final Document document = JSONDocument.fromJson("{\"planet\": \"Mars\"}"); @SuppressWarnings("unchecked") final ArgumentCaptor> valueCaptor = ArgumentCaptor.forClass(List.class); @@ -263,7 +263,7 @@ class UpdateTest { private final SubDocumentUpdate quantityUpdate = SubDocumentUpdate.of("quantity", 1000); private final SubDocumentUpdate propsUpdate = SubDocumentUpdate.of( - "props", SubDocumentValue.of(new JSONDocument("{\"brand\": \"Dettol\"}"))); + "props", SubDocumentValue.of(JSONDocument.fromJson("{\"brand\": \"Dettol\"}"))); private final BasicDBObject setObject = readBasicDBObject("atomic_read_and_update/set_object.json"); private final BasicDBObject selections = @@ -370,7 +370,7 @@ class BulkUpdateTest { private final SubDocumentUpdate quantityUpdate = SubDocumentUpdate.of("quantity", 1000); private final SubDocumentUpdate propsUpdate = SubDocumentUpdate.of( - "props", SubDocumentValue.of(new JSONDocument("{\"brand\": \"Dettol\"}"))); + "props", SubDocumentValue.of(JSONDocument.fromJson("{\"brand\": \"Dettol\"}"))); private final BasicDBObject setObject = readBasicDBObject("atomic_read_and_update/set_object.json"); private final BasicDBObject selections = diff --git a/document-store/src/test/java/org/hypertrace/core/documentstore/postgres/PostgresCollectionTest.java b/document-store/src/test/java/org/hypertrace/core/documentstore/postgres/PostgresCollectionTest.java index e94f6009b..e695d5753 100644 --- a/document-store/src/test/java/org/hypertrace/core/documentstore/postgres/PostgresCollectionTest.java +++ b/document-store/src/test/java/org/hypertrace/core/documentstore/postgres/PostgresCollectionTest.java @@ -59,6 +59,7 @@ @ExtendWith(MockitoExtension.class) class PostgresCollectionTest { + private static final String COLLECTION_NAME = "test_collection"; private static final long currentTime = 1658956123L; @@ -84,7 +85,7 @@ void setUp() { @Test void testCreateOrReplace() throws Exception { final Key key = Key.from("some_key"); - final Document document = new JSONDocument("{\"planet\": \"Mars\"}"); + final Document document = JSONDocument.fromJson("{\"planet\": \"Mars\"}"); when(mockClient.getConnection()).thenReturn(mockConnection); when(mockConnection.prepareStatement( @@ -923,7 +924,7 @@ private List buildUpdates() throws IOException { final SubDocumentUpdate quantityUpdate = SubDocumentUpdate.of("quantity", 1000); final SubDocumentUpdate propsUpdate = SubDocumentUpdate.of( - "props", SubDocumentValue.of(new JSONDocument("{\"brand\": \"Dettol\"}"))); + "props", SubDocumentValue.of(JSONDocument.fromJson("{\"brand\": \"Dettol\"}"))); return List.of(dateUpdate, quantityUpdate, propsUpdate); } diff --git a/document-store/src/test/java/org/hypertrace/core/documentstore/postgres/query/v1/PostgresQueryParserTest.java b/document-store/src/test/java/org/hypertrace/core/documentstore/postgres/query/v1/PostgresQueryParserTest.java index 816d92112..7799e94f2 100644 --- a/document-store/src/test/java/org/hypertrace/core/documentstore/postgres/query/v1/PostgresQueryParserTest.java +++ b/document-store/src/test/java/org/hypertrace/core/documentstore/postgres/query/v1/PostgresQueryParserTest.java @@ -1333,7 +1333,7 @@ void testContainsFilter() throws IOException { RelationalExpression.of( IdentifierExpression.of("sales"), CONTAINS, - ConstantExpression.of(new JSONDocument("\"a\"")))) + ConstantExpression.of(JSONDocument.fromJson("\"a\"")))) .build(); PostgresQueryParser postgresQueryParser = new PostgresQueryParser(TEST_TABLE, PostgresQueryTransformer.transform(query)); @@ -1359,7 +1359,7 @@ void testContainsAndUnnestFilters() throws IOException { IdentifierExpression.of("sales.medium"), CONTAINS, ConstantExpression.of( - new JSONDocument("{\"type\": \"retail\",\"volume\": 500}")))) + JSONDocument.fromJson("{\"type\": \"retail\",\"volume\": 500}")))) .build()) .build(); @@ -1394,7 +1394,7 @@ void testNotContainsAndUnnestFilters() throws IOException { IdentifierExpression.of("sales.medium"), NOT_CONTAINS, ConstantExpression.of( - new JSONDocument("{\"type\": \"retail\",\"volume\": 500}")))) + JSONDocument.fromJson("{\"type\": \"retail\",\"volume\": 500}")))) .build()) .build(); diff --git a/document-store/src/test/java/org/hypertrace/core/documentstore/util/TestUtil.java b/document-store/src/test/java/org/hypertrace/core/documentstore/util/TestUtil.java index 7d0006192..24bfee676 100644 --- a/document-store/src/test/java/org/hypertrace/core/documentstore/util/TestUtil.java +++ b/document-store/src/test/java/org/hypertrace/core/documentstore/util/TestUtil.java @@ -33,7 +33,7 @@ public static BasicDBObject readBasicDBObject(final String filePath) throws IOEx } public static Document readDocument(final String filePath) throws IOException { - return new JSONDocument(readFileFromResource(filePath).orElseThrow()); + return JSONDocument.fromJson(readFileFromResource(filePath).orElseThrow()); } @SuppressWarnings("unchecked") From 957a8975037805910555e59669f07c27876fce30 Mon Sep 17 00:00:00 2001 From: Prashant Pandey Date: Mon, 22 Sep 2025 15:06:22 +0530 Subject: [PATCH 04/19] Revert "Reapply "WIP"" This reverts commit 6e7795d4519ba37e012d41271944497b0743601e. --- .../mongo/MongoDocStoreTest.java | 6 +- .../core/documentstore/utils/Utils.java | 9 +- .../core/documentstore/Document.java | 2 - .../core/documentstore/DocumentType.java | 6 - .../core/documentstore/JSONDocument.java | 106 +++--------------- .../core/documentstore/mongo/MongoUtils.java | 2 +- .../postgres/PostgresCollection.java | 17 ++- .../postgres/utils/PostgresUtils.java | 7 +- .../core/documentstore/JSONDocumentTest.java | 4 +- .../PostgresDocStoreMetricProviderTest.java | 19 ++-- .../mongo/MongoCollectionTest.java | 6 +- .../postgres/PostgresCollectionTest.java | 5 +- .../query/v1/PostgresQueryParserTest.java | 6 +- .../core/documentstore/util/TestUtil.java | 2 +- 14 files changed, 54 insertions(+), 143 deletions(-) delete mode 100644 document-store/src/main/java/org/hypertrace/core/documentstore/DocumentType.java diff --git a/document-store/src/integrationTest/java/org/hypertrace/core/documentstore/mongo/MongoDocStoreTest.java b/document-store/src/integrationTest/java/org/hypertrace/core/documentstore/mongo/MongoDocStoreTest.java index 81157694d..488f77ae6 100644 --- a/document-store/src/integrationTest/java/org/hypertrace/core/documentstore/mongo/MongoDocStoreTest.java +++ b/document-store/src/integrationTest/java/org/hypertrace/core/documentstore/mongo/MongoDocStoreTest.java @@ -125,7 +125,7 @@ public void testUpsertAndReturn() throws IOException { Collection collection = datastore.getCollection(COLLECTION_NAME); ObjectNode objectNode = OBJECT_MAPPER.createObjectNode(); objectNode.put("foo1", "bar1"); - Document document = JSONDocument.fromObject(objectNode); + Document document = new JSONDocument(objectNode); Document persistedDocument = collection.upsertAndReturn(new SingleValueKey("default", "testKey"), document); @@ -140,7 +140,7 @@ public void testUpsertAndReturn() throws IOException { objectNode = OBJECT_MAPPER.createObjectNode(); objectNode.put("foo2", "bar2"); - document = JSONDocument.fromObject(objectNode); + document = new JSONDocument(objectNode); // Upsert again and verify that createdTime does not change, while lastUpdatedTime // has changed and values have merged @@ -161,7 +161,7 @@ public void testBulkUpsertAndVerifyUpdatedTime() throws IOException { Collection collection = datastore.getCollection(COLLECTION_NAME); ObjectNode objectNode = OBJECT_MAPPER.createObjectNode(); objectNode.put("foo1", "bar1"); - Document document = JSONDocument.fromObject(objectNode); + Document document = new JSONDocument(objectNode); collection.bulkUpsert(Map.of(new SingleValueKey("default", "testKey"), document)); Query query = new Query(); diff --git a/document-store/src/integrationTest/java/org/hypertrace/core/documentstore/utils/Utils.java b/document-store/src/integrationTest/java/org/hypertrace/core/documentstore/utils/Utils.java index 2449df011..75ea277f5 100644 --- a/document-store/src/integrationTest/java/org/hypertrace/core/documentstore/utils/Utils.java +++ b/document-store/src/integrationTest/java/org/hypertrace/core/documentstore/utils/Utils.java @@ -27,7 +27,6 @@ import org.testcontainers.shaded.org.apache.commons.io.IOUtils; public class Utils { - private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper(); public static final String MONGO_STORE = "Mongo"; @@ -63,13 +62,13 @@ public static Document createDocument(ImmutablePair... pairs) { objectNode.putPOJO(pairs[i].getLeft(), pairs[i].getRight()); } } - return JSONDocument.fromJsonNode(objectNode); + return new JSONDocument(objectNode); } public static Document createDocument(String key, String value) { ObjectNode objectNode = OBJECT_MAPPER.createObjectNode(); objectNode.put(key, value); - return JSONDocument.fromJsonNode(objectNode); + return new JSONDocument(objectNode); } public static Document createDocument(String... keys) { @@ -77,7 +76,7 @@ public static Document createDocument(String... keys) { for (int i = 0; i < keys.length - 1; i++) { objectNode.put(keys[i], keys[i + 1]); } - return JSONDocument.fromJsonNode(objectNode); + return new JSONDocument(objectNode); } public static Optional readFileFromResource(String filePath) throws IOException { @@ -136,7 +135,7 @@ public static Map buildDocumentsFromResource(String resourcePath) Map documentMap = new HashMap<>(); for (Map map : maps) { Key key = new SingleValueKey(TENANT_ID, map.get(MongoCollection.ID_KEY).toString()); - Document value = JSONDocument.fromObject(map); + Document value = new JSONDocument(map); documentMap.put(key, value); } diff --git a/document-store/src/main/java/org/hypertrace/core/documentstore/Document.java b/document-store/src/main/java/org/hypertrace/core/documentstore/Document.java index a9327779e..944913aac 100644 --- a/document-store/src/main/java/org/hypertrace/core/documentstore/Document.java +++ b/document-store/src/main/java/org/hypertrace/core/documentstore/Document.java @@ -3,6 +3,4 @@ public interface Document { String toJson(); - - DocumentType getDocumentType(); } diff --git a/document-store/src/main/java/org/hypertrace/core/documentstore/DocumentType.java b/document-store/src/main/java/org/hypertrace/core/documentstore/DocumentType.java deleted file mode 100644 index 745505ef2..000000000 --- a/document-store/src/main/java/org/hypertrace/core/documentstore/DocumentType.java +++ /dev/null @@ -1,6 +0,0 @@ -package org.hypertrace.core.documentstore; - -public enum DocumentType { - SQL_STORE, - DOCUMENT_STORE -} diff --git a/document-store/src/main/java/org/hypertrace/core/documentstore/JSONDocument.java b/document-store/src/main/java/org/hypertrace/core/documentstore/JSONDocument.java index 98f7973b4..c04d806de 100644 --- a/document-store/src/main/java/org/hypertrace/core/documentstore/JSONDocument.java +++ b/document-store/src/main/java/org/hypertrace/core/documentstore/JSONDocument.java @@ -4,35 +4,39 @@ import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.node.ObjectNode; -import com.google.common.base.Preconditions; import java.io.IOException; import java.util.Objects; -import lombok.Builder; -@Builder public class JSONDocument implements Document { - private static final ObjectMapper MAPPER = new ObjectMapper(); - private final JsonNode node; - private final DocumentType documentType; + private static ObjectMapper mapper = new ObjectMapper(); + private JsonNode node; - private JSONDocument(JsonNode node, DocumentType documentType) { - this.node = Preconditions.checkNotNull(node, "JsonNode cannot be null"); - this.documentType = Preconditions.checkNotNull(documentType, "DocumentType cannot be null"); + public JSONDocument(String json) throws IOException { + node = mapper.readTree(json); + } + + public JSONDocument(Object object) throws IOException { + node = mapper.readTree(mapper.writeValueAsString(object)); + } + + public JSONDocument(JsonNode node) { + this.node = node; } @Override public String toJson() { try { - return MAPPER.writeValueAsString(node); + return mapper.writeValueAsString(node); } catch (JsonProcessingException e) { return "{}"; } } - @Override - public DocumentType getDocumentType() { - return documentType; + public static JSONDocument errorDocument(String message) { + ObjectNode objectNode = mapper.createObjectNode(); + objectNode.put("errorMessage", message); + return new JSONDocument(objectNode); } @Override @@ -52,80 +56,4 @@ public boolean equals(Object obj) { JSONDocument other = (JSONDocument) obj; return Objects.equals(node, other.node); } - - @Override - public int hashCode() { - return Objects.hash(node, documentType); - } - - // Builder class - public static class Builder { - - private JsonNode node; - private DocumentType documentType = DocumentType.DOCUMENT_STORE; // default is DOCUMENT_STORE - - public JSONDocument.Builder fromJson(String json) throws IOException { - this.node = MAPPER.readTree(json); - return this; - } - - public JSONDocument.Builder fromObject(Object object) throws IOException { - this.node = MAPPER.readTree(MAPPER.writeValueAsString(object)); - return this; - } - - public JSONDocument.Builder fromJsonNode(JsonNode node) { - this.node = node; - return this; - } - - public JSONDocument.Builder withDocumentType(DocumentType documentType) { - this.documentType = documentType; - return this; - } - - public JSONDocument build() { - if (node == null) { - throw new IllegalStateException("JsonNode must be set before building"); - } - return new JSONDocument(node, documentType); - } - } - - public static JSONDocument fromJson(String json) throws IOException { - return new JSONDocument.Builder().fromJson(json).build(); - } - - public static JSONDocument fromJson(String json, DocumentType type) throws IOException { - return new JSONDocument.Builder().fromJson(json).withDocumentType(type).build(); - } - - public static JSONDocument fromObject(Object object) throws IOException { - return new JSONDocument.Builder().fromObject(object).build(); - } - - public static JSONDocument fromObject(Object object, DocumentType type) throws IOException { - return new JSONDocument.Builder().fromObject(object).withDocumentType(type).build(); - } - - public static JSONDocument fromJsonNode(JsonNode jsonNode) { - return new JSONDocument.Builder().fromJsonNode(jsonNode).build(); - } - - public static JSONDocument fromJsonNode(JsonNode jsonNode, DocumentType type) throws IOException { - return new JSONDocument.Builder().fromJsonNode(jsonNode).withDocumentType(type).build(); - } - - public static JSONDocument errorDocument(String message) { - return errorDocument(message, DocumentType.DOCUMENT_STORE); - } - - public static JSONDocument errorDocument(String message, DocumentType documentType) { - ObjectNode objectNode = MAPPER.createObjectNode(); - objectNode.put("errorMessage", message); - return new JSONDocument.Builder() - .fromJsonNode(objectNode) - .withDocumentType(documentType) - .build(); - } } diff --git a/document-store/src/main/java/org/hypertrace/core/documentstore/mongo/MongoUtils.java b/document-store/src/main/java/org/hypertrace/core/documentstore/mongo/MongoUtils.java index 2ed8a0ae3..f12868cff 100644 --- a/document-store/src/main/java/org/hypertrace/core/documentstore/mongo/MongoUtils.java +++ b/document-store/src/main/java/org/hypertrace/core/documentstore/mongo/MongoUtils.java @@ -137,7 +137,7 @@ public static Document dbObjectToDocument(BasicDBObject dbObject) { jsonString = dbObject.toJson(relaxed); JsonNode jsonNode = MAPPER.readTree(jsonString); JsonNode decodedJsonNode = recursiveClone(jsonNode, MongoUtils::decodeKey, identity()); - return JSONDocument.fromJsonNode(decodedJsonNode); + return new JSONDocument(decodedJsonNode); } catch (IOException e) { // throwing exception is not very useful here. return JSONDocument.errorDocument(e.getMessage()); 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 05460c1d6..5582889a3 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 @@ -61,7 +61,6 @@ import org.hypertrace.core.documentstore.Collection; import org.hypertrace.core.documentstore.CreateResult; import org.hypertrace.core.documentstore.Document; -import org.hypertrace.core.documentstore.DocumentType; import org.hypertrace.core.documentstore.Filter; import org.hypertrace.core.documentstore.JSONDocument; import org.hypertrace.core.documentstore.Key; @@ -880,7 +879,7 @@ private BulkUpdateResult bulkSetOnArrayValue( if (docRoot.isObject()) { ((ObjectNode) docRoot).put(DocStoreConstants.LAST_UPDATED_TIME, System.currentTimeMillis()); } - upsertMap.put(Key.from(id), JSONDocument.fromJsonNode(docRoot, DocumentType.SQL_STORE)); + upsertMap.put(Key.from(id), new JSONDocument(docRoot)); } return upsertDocs(upsertMap); } @@ -909,7 +908,7 @@ private BulkUpdateResult bulkRemoveOnArrayValue( candidateArray.removeAll(); candidateArray.addAll(existingItems); String id = docRoot.findValue(DOCUMENT_ID).asText(); - upsertMap.put(Key.from(id), JSONDocument.fromJsonNode(docRoot, DocumentType.SQL_STORE)); + upsertMap.put(Key.from(id), new JSONDocument(docRoot)); } return upsertDocs(upsertMap); } @@ -934,7 +933,7 @@ private BulkUpdateResult bulkAddOnArrayValue( if (docRoot.isObject()) { ((ObjectNode) docRoot).put(DocStoreConstants.LAST_UPDATED_TIME, System.currentTimeMillis()); } - upsertMap.put(Key.from(id), JSONDocument.fromJsonNode(docRoot, DocumentType.SQL_STORE)); + upsertMap.put(Key.from(id), new JSONDocument(docRoot)); } return upsertDocs(upsertMap); } @@ -1277,7 +1276,7 @@ public Document next() { } catch (IOException | SQLException e) { System.out.println("prepare document failed!"); closeResultSet(); - return JSONDocument.errorDocument(e.getMessage(), DocumentType.SQL_STORE); + return JSONDocument.errorDocument(e.getMessage()); } } @@ -1300,7 +1299,7 @@ protected Document prepareDocument() throws SQLException, IOException { jsonNode.remove(DOCUMENT_ID); } - return JSONDocument.fromJson(MAPPER.writeValueAsString(jsonNode), DocumentType.SQL_STORE); + return new JSONDocument(MAPPER.writeValueAsString(jsonNode)); } private void addColumnToJsonNode( @@ -1431,7 +1430,7 @@ public Document next() { return prepareDocument(); } catch (IOException | SQLException e) { closeResultSet(); - return JSONDocument.errorDocument(e.getMessage(), DocumentType.SQL_STORE); + return JSONDocument.errorDocument(e.getMessage()); } } @@ -1448,7 +1447,7 @@ protected Document prepareDocument() throws SQLException, IOException { jsonNode.put(CREATED_AT, String.valueOf(createdAt)); jsonNode.put(UPDATED_AT, String.valueOf(updatedAt)); - return JSONDocument.fromJson(MAPPER.writeValueAsString(jsonNode), DocumentType.SQL_STORE); + return new JSONDocument(MAPPER.writeValueAsString(jsonNode)); } protected void closeResultSet() { @@ -1509,7 +1508,7 @@ protected Document prepareDocument() throws SQLException, IOException { } } } - return JSONDocument.fromJson(MAPPER.writeValueAsString(jsonNode), DocumentType.SQL_STORE); + return new JSONDocument(MAPPER.writeValueAsString(jsonNode)); } private String getColumnValue( 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 8e5b94e4b..24b8ffc6d 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 @@ -36,7 +36,6 @@ @Slf4j public class PostgresUtils { - public static final String JSON_FIELD_ACCESSOR = "->"; private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper(); private static final String QUESTION_MARK = "?"; @@ -134,9 +133,8 @@ public static String prepareCast(String field, Type type) { public static String prepareCastForFieldAccessor(String field, Object value) { String fmt = "CAST (%s AS %s)"; Type type = getType(value); - if (type.equals(Type.STRING) || type.equals(Type.STRING_ARRAY)) { + if (type.equals(Type.STRING) || type.equals(Type.STRING_ARRAY)) return String.format(fmt, field, "TEXT"); - } return prepareCast(field, type); } @@ -589,8 +587,7 @@ public static DocumentAndId extractAndRemoveId(final Document document) throws I new ObjectMapper() .readValue(document.toJson(), new TypeReference>() {}); final String id = String.valueOf(requireNonNull(map.remove(IMPLICIT_ID))); - final Document documentWithoutId = - JSONDocument.fromJson(new ObjectMapper().writeValueAsString(map)); + final Document documentWithoutId = new JSONDocument(new ObjectMapper().writeValueAsString(map)); return new DocumentAndId(documentWithoutId, id); } diff --git a/document-store/src/test/java/org/hypertrace/core/documentstore/JSONDocumentTest.java b/document-store/src/test/java/org/hypertrace/core/documentstore/JSONDocumentTest.java index 407d8c0aa..5cd3be9ac 100644 --- a/document-store/src/test/java/org/hypertrace/core/documentstore/JSONDocumentTest.java +++ b/document-store/src/test/java/org/hypertrace/core/documentstore/JSONDocumentTest.java @@ -9,8 +9,8 @@ public class JSONDocumentTest { @Test public void testJSONDocument() throws Exception { Map data = Map.of("key1", "value1", "key2", "value2"); - JSONDocument document1 = JSONDocument.fromObject(data); - JSONDocument document2 = JSONDocument.fromJson(document1.toJson()); + JSONDocument document1 = new JSONDocument(data); + JSONDocument document2 = new JSONDocument(document1.toJson()); Assertions.assertEquals(document1, document2); Assertions.assertEquals(document1.toJson(), document2.toJson()); } diff --git a/document-store/src/test/java/org/hypertrace/core/documentstore/metric/postgres/PostgresDocStoreMetricProviderTest.java b/document-store/src/test/java/org/hypertrace/core/documentstore/metric/postgres/PostgresDocStoreMetricProviderTest.java index e47fa1279..2f961a34f 100644 --- a/document-store/src/test/java/org/hypertrace/core/documentstore/metric/postgres/PostgresDocStoreMetricProviderTest.java +++ b/document-store/src/test/java/org/hypertrace/core/documentstore/metric/postgres/PostgresDocStoreMetricProviderTest.java @@ -46,12 +46,9 @@ class PostgresDocStoreMetricProviderTest { (PostgresConnectionConfig) PostgresConnectionConfig.builder().applicationName(postgresClientAppName).build(); - @Mock - private PostgresDatastore mockDatastore; - @Mock - private Collection mockCollection; - @Mock - private CloseableIterator mockIterator; + @Mock private PostgresDatastore mockDatastore; + @Mock private Collection mockCollection; + @Mock private CloseableIterator mockIterator; private PostgresDocStoreMetricProvider postgresDocStoreMetricProvider; @@ -95,7 +92,7 @@ void withNoValueColumnIterator_returnsDefaultMetric() throws IOException { when(mockCollection.query(query, queryOptions)).thenReturn(mockIterator); when(mockIterator.hasNext()).thenReturn(true, false); when(mockIterator.next()) - .thenReturn(JSONDocument.fromObject(Map.of("application_name", postgresClientAppName))); + .thenReturn(new JSONDocument(Map.of("application_name", postgresClientAppName))); final DocStoreMetric result = postgresDocStoreMetricProvider.getConnectionCountMetric(); assertEquals(defaultMetric, result); } @@ -122,7 +119,7 @@ void withMultipleDocuments_returnsDefaultMetric() throws IOException { when(mockIterator.hasNext()).thenReturn(true, true, false); when(mockIterator.next()) .thenReturn( - JSONDocument.fromObject( + new JSONDocument( Map.of("application_name", postgresClientAppName, "metric_value", 1))); final DocStoreMetric result = postgresDocStoreMetricProvider.getConnectionCountMetric(); assertEquals(defaultMetric, result); @@ -136,8 +133,8 @@ void withMultipleInvalidDocumentsFollowedByValidDocument_returnsTheRightMetric() when(mockIterator.next()) .thenReturn( () -> "invalid-json", - JSONDocument.fromObject(Map.of("value_missing", postgresClientAppName)), - JSONDocument.fromObject( + new JSONDocument(Map.of("value_missing", postgresClientAppName)), + new JSONDocument( Map.of("application_name", postgresClientAppName, "metric_value", 1))); final DocStoreMetric expected = DocStoreMetric.builder() @@ -155,7 +152,7 @@ void withValidDocument_returnsTheRightMetric() throws IOException { when(mockIterator.hasNext()).thenReturn(true, false); when(mockIterator.next()) .thenReturn( - JSONDocument.fromObject( + new JSONDocument( Map.of("application_name", postgresClientAppName, "metric_value", 1))); final DocStoreMetric expected = DocStoreMetric.builder() diff --git a/document-store/src/test/java/org/hypertrace/core/documentstore/mongo/MongoCollectionTest.java b/document-store/src/test/java/org/hypertrace/core/documentstore/mongo/MongoCollectionTest.java index 77b6967f8..41574369a 100644 --- a/document-store/src/test/java/org/hypertrace/core/documentstore/mongo/MongoCollectionTest.java +++ b/document-store/src/test/java/org/hypertrace/core/documentstore/mongo/MongoCollectionTest.java @@ -215,7 +215,7 @@ class CreateOrReplaceTest { @Test void testCreateOrReplace() throws IOException { final Key key = Key.from("some-key"); - final Document document = JSONDocument.fromJson("{\"planet\": \"Mars\"}"); + final Document document = new JSONDocument("{\"planet\": \"Mars\"}"); @SuppressWarnings("unchecked") final ArgumentCaptor> valueCaptor = ArgumentCaptor.forClass(List.class); @@ -263,7 +263,7 @@ class UpdateTest { private final SubDocumentUpdate quantityUpdate = SubDocumentUpdate.of("quantity", 1000); private final SubDocumentUpdate propsUpdate = SubDocumentUpdate.of( - "props", SubDocumentValue.of(JSONDocument.fromJson("{\"brand\": \"Dettol\"}"))); + "props", SubDocumentValue.of(new JSONDocument("{\"brand\": \"Dettol\"}"))); private final BasicDBObject setObject = readBasicDBObject("atomic_read_and_update/set_object.json"); private final BasicDBObject selections = @@ -370,7 +370,7 @@ class BulkUpdateTest { private final SubDocumentUpdate quantityUpdate = SubDocumentUpdate.of("quantity", 1000); private final SubDocumentUpdate propsUpdate = SubDocumentUpdate.of( - "props", SubDocumentValue.of(JSONDocument.fromJson("{\"brand\": \"Dettol\"}"))); + "props", SubDocumentValue.of(new JSONDocument("{\"brand\": \"Dettol\"}"))); private final BasicDBObject setObject = readBasicDBObject("atomic_read_and_update/set_object.json"); private final BasicDBObject selections = diff --git a/document-store/src/test/java/org/hypertrace/core/documentstore/postgres/PostgresCollectionTest.java b/document-store/src/test/java/org/hypertrace/core/documentstore/postgres/PostgresCollectionTest.java index e695d5753..e94f6009b 100644 --- a/document-store/src/test/java/org/hypertrace/core/documentstore/postgres/PostgresCollectionTest.java +++ b/document-store/src/test/java/org/hypertrace/core/documentstore/postgres/PostgresCollectionTest.java @@ -59,7 +59,6 @@ @ExtendWith(MockitoExtension.class) class PostgresCollectionTest { - private static final String COLLECTION_NAME = "test_collection"; private static final long currentTime = 1658956123L; @@ -85,7 +84,7 @@ void setUp() { @Test void testCreateOrReplace() throws Exception { final Key key = Key.from("some_key"); - final Document document = JSONDocument.fromJson("{\"planet\": \"Mars\"}"); + final Document document = new JSONDocument("{\"planet\": \"Mars\"}"); when(mockClient.getConnection()).thenReturn(mockConnection); when(mockConnection.prepareStatement( @@ -924,7 +923,7 @@ private List buildUpdates() throws IOException { final SubDocumentUpdate quantityUpdate = SubDocumentUpdate.of("quantity", 1000); final SubDocumentUpdate propsUpdate = SubDocumentUpdate.of( - "props", SubDocumentValue.of(JSONDocument.fromJson("{\"brand\": \"Dettol\"}"))); + "props", SubDocumentValue.of(new JSONDocument("{\"brand\": \"Dettol\"}"))); return List.of(dateUpdate, quantityUpdate, propsUpdate); } diff --git a/document-store/src/test/java/org/hypertrace/core/documentstore/postgres/query/v1/PostgresQueryParserTest.java b/document-store/src/test/java/org/hypertrace/core/documentstore/postgres/query/v1/PostgresQueryParserTest.java index 7799e94f2..816d92112 100644 --- a/document-store/src/test/java/org/hypertrace/core/documentstore/postgres/query/v1/PostgresQueryParserTest.java +++ b/document-store/src/test/java/org/hypertrace/core/documentstore/postgres/query/v1/PostgresQueryParserTest.java @@ -1333,7 +1333,7 @@ void testContainsFilter() throws IOException { RelationalExpression.of( IdentifierExpression.of("sales"), CONTAINS, - ConstantExpression.of(JSONDocument.fromJson("\"a\"")))) + ConstantExpression.of(new JSONDocument("\"a\"")))) .build(); PostgresQueryParser postgresQueryParser = new PostgresQueryParser(TEST_TABLE, PostgresQueryTransformer.transform(query)); @@ -1359,7 +1359,7 @@ void testContainsAndUnnestFilters() throws IOException { IdentifierExpression.of("sales.medium"), CONTAINS, ConstantExpression.of( - JSONDocument.fromJson("{\"type\": \"retail\",\"volume\": 500}")))) + new JSONDocument("{\"type\": \"retail\",\"volume\": 500}")))) .build()) .build(); @@ -1394,7 +1394,7 @@ void testNotContainsAndUnnestFilters() throws IOException { IdentifierExpression.of("sales.medium"), NOT_CONTAINS, ConstantExpression.of( - JSONDocument.fromJson("{\"type\": \"retail\",\"volume\": 500}")))) + new JSONDocument("{\"type\": \"retail\",\"volume\": 500}")))) .build()) .build(); diff --git a/document-store/src/test/java/org/hypertrace/core/documentstore/util/TestUtil.java b/document-store/src/test/java/org/hypertrace/core/documentstore/util/TestUtil.java index 24bfee676..7d0006192 100644 --- a/document-store/src/test/java/org/hypertrace/core/documentstore/util/TestUtil.java +++ b/document-store/src/test/java/org/hypertrace/core/documentstore/util/TestUtil.java @@ -33,7 +33,7 @@ public static BasicDBObject readBasicDBObject(final String filePath) throws IOEx } public static Document readDocument(final String filePath) throws IOException { - return JSONDocument.fromJson(readFileFromResource(filePath).orElseThrow()); + return new JSONDocument(readFileFromResource(filePath).orElseThrow()); } @SuppressWarnings("unchecked") From 502c3739643a928528119b3ff9bdc934a34cb58b Mon Sep 17 00:00:00 2001 From: Prashant Pandey Date: Mon, 22 Sep 2025 15:08:44 +0530 Subject: [PATCH 05/19] Reapply "Reapply "WIP"" This reverts commit 957a8975037805910555e59669f07c27876fce30. --- .../mongo/MongoDocStoreTest.java | 6 +- .../core/documentstore/utils/Utils.java | 9 +- .../core/documentstore/Document.java | 2 + .../core/documentstore/DocumentType.java | 6 + .../core/documentstore/JSONDocument.java | 106 +++++++++++++++--- .../core/documentstore/mongo/MongoUtils.java | 2 +- .../postgres/PostgresCollection.java | 17 +-- .../postgres/utils/PostgresUtils.java | 7 +- .../core/documentstore/JSONDocumentTest.java | 4 +- .../PostgresDocStoreMetricProviderTest.java | 19 ++-- .../mongo/MongoCollectionTest.java | 6 +- .../postgres/PostgresCollectionTest.java | 5 +- .../query/v1/PostgresQueryParserTest.java | 6 +- .../core/documentstore/util/TestUtil.java | 2 +- 14 files changed, 143 insertions(+), 54 deletions(-) create mode 100644 document-store/src/main/java/org/hypertrace/core/documentstore/DocumentType.java diff --git a/document-store/src/integrationTest/java/org/hypertrace/core/documentstore/mongo/MongoDocStoreTest.java b/document-store/src/integrationTest/java/org/hypertrace/core/documentstore/mongo/MongoDocStoreTest.java index 488f77ae6..81157694d 100644 --- a/document-store/src/integrationTest/java/org/hypertrace/core/documentstore/mongo/MongoDocStoreTest.java +++ b/document-store/src/integrationTest/java/org/hypertrace/core/documentstore/mongo/MongoDocStoreTest.java @@ -125,7 +125,7 @@ public void testUpsertAndReturn() throws IOException { Collection collection = datastore.getCollection(COLLECTION_NAME); ObjectNode objectNode = OBJECT_MAPPER.createObjectNode(); objectNode.put("foo1", "bar1"); - Document document = new JSONDocument(objectNode); + Document document = JSONDocument.fromObject(objectNode); Document persistedDocument = collection.upsertAndReturn(new SingleValueKey("default", "testKey"), document); @@ -140,7 +140,7 @@ public void testUpsertAndReturn() throws IOException { objectNode = OBJECT_MAPPER.createObjectNode(); objectNode.put("foo2", "bar2"); - document = new JSONDocument(objectNode); + document = JSONDocument.fromObject(objectNode); // Upsert again and verify that createdTime does not change, while lastUpdatedTime // has changed and values have merged @@ -161,7 +161,7 @@ public void testBulkUpsertAndVerifyUpdatedTime() throws IOException { Collection collection = datastore.getCollection(COLLECTION_NAME); ObjectNode objectNode = OBJECT_MAPPER.createObjectNode(); objectNode.put("foo1", "bar1"); - Document document = new JSONDocument(objectNode); + Document document = JSONDocument.fromObject(objectNode); collection.bulkUpsert(Map.of(new SingleValueKey("default", "testKey"), document)); Query query = new Query(); diff --git a/document-store/src/integrationTest/java/org/hypertrace/core/documentstore/utils/Utils.java b/document-store/src/integrationTest/java/org/hypertrace/core/documentstore/utils/Utils.java index 75ea277f5..2449df011 100644 --- a/document-store/src/integrationTest/java/org/hypertrace/core/documentstore/utils/Utils.java +++ b/document-store/src/integrationTest/java/org/hypertrace/core/documentstore/utils/Utils.java @@ -27,6 +27,7 @@ import org.testcontainers.shaded.org.apache.commons.io.IOUtils; public class Utils { + private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper(); public static final String MONGO_STORE = "Mongo"; @@ -62,13 +63,13 @@ public static Document createDocument(ImmutablePair... pairs) { objectNode.putPOJO(pairs[i].getLeft(), pairs[i].getRight()); } } - return new JSONDocument(objectNode); + return JSONDocument.fromJsonNode(objectNode); } public static Document createDocument(String key, String value) { ObjectNode objectNode = OBJECT_MAPPER.createObjectNode(); objectNode.put(key, value); - return new JSONDocument(objectNode); + return JSONDocument.fromJsonNode(objectNode); } public static Document createDocument(String... keys) { @@ -76,7 +77,7 @@ public static Document createDocument(String... keys) { for (int i = 0; i < keys.length - 1; i++) { objectNode.put(keys[i], keys[i + 1]); } - return new JSONDocument(objectNode); + return JSONDocument.fromJsonNode(objectNode); } public static Optional readFileFromResource(String filePath) throws IOException { @@ -135,7 +136,7 @@ public static Map buildDocumentsFromResource(String resourcePath) Map documentMap = new HashMap<>(); for (Map map : maps) { Key key = new SingleValueKey(TENANT_ID, map.get(MongoCollection.ID_KEY).toString()); - Document value = new JSONDocument(map); + Document value = JSONDocument.fromObject(map); documentMap.put(key, value); } diff --git a/document-store/src/main/java/org/hypertrace/core/documentstore/Document.java b/document-store/src/main/java/org/hypertrace/core/documentstore/Document.java index 944913aac..a9327779e 100644 --- a/document-store/src/main/java/org/hypertrace/core/documentstore/Document.java +++ b/document-store/src/main/java/org/hypertrace/core/documentstore/Document.java @@ -3,4 +3,6 @@ public interface Document { String toJson(); + + DocumentType getDocumentType(); } diff --git a/document-store/src/main/java/org/hypertrace/core/documentstore/DocumentType.java b/document-store/src/main/java/org/hypertrace/core/documentstore/DocumentType.java new file mode 100644 index 000000000..745505ef2 --- /dev/null +++ b/document-store/src/main/java/org/hypertrace/core/documentstore/DocumentType.java @@ -0,0 +1,6 @@ +package org.hypertrace.core.documentstore; + +public enum DocumentType { + SQL_STORE, + DOCUMENT_STORE +} diff --git a/document-store/src/main/java/org/hypertrace/core/documentstore/JSONDocument.java b/document-store/src/main/java/org/hypertrace/core/documentstore/JSONDocument.java index c04d806de..98f7973b4 100644 --- a/document-store/src/main/java/org/hypertrace/core/documentstore/JSONDocument.java +++ b/document-store/src/main/java/org/hypertrace/core/documentstore/JSONDocument.java @@ -4,39 +4,35 @@ import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.node.ObjectNode; +import com.google.common.base.Preconditions; import java.io.IOException; import java.util.Objects; +import lombok.Builder; +@Builder public class JSONDocument implements Document { - private static ObjectMapper mapper = new ObjectMapper(); - private JsonNode node; + private static final ObjectMapper MAPPER = new ObjectMapper(); + private final JsonNode node; + private final DocumentType documentType; - public JSONDocument(String json) throws IOException { - node = mapper.readTree(json); - } - - public JSONDocument(Object object) throws IOException { - node = mapper.readTree(mapper.writeValueAsString(object)); - } - - public JSONDocument(JsonNode node) { - this.node = node; + private JSONDocument(JsonNode node, DocumentType documentType) { + this.node = Preconditions.checkNotNull(node, "JsonNode cannot be null"); + this.documentType = Preconditions.checkNotNull(documentType, "DocumentType cannot be null"); } @Override public String toJson() { try { - return mapper.writeValueAsString(node); + return MAPPER.writeValueAsString(node); } catch (JsonProcessingException e) { return "{}"; } } - public static JSONDocument errorDocument(String message) { - ObjectNode objectNode = mapper.createObjectNode(); - objectNode.put("errorMessage", message); - return new JSONDocument(objectNode); + @Override + public DocumentType getDocumentType() { + return documentType; } @Override @@ -56,4 +52,80 @@ public boolean equals(Object obj) { JSONDocument other = (JSONDocument) obj; return Objects.equals(node, other.node); } + + @Override + public int hashCode() { + return Objects.hash(node, documentType); + } + + // Builder class + public static class Builder { + + private JsonNode node; + private DocumentType documentType = DocumentType.DOCUMENT_STORE; // default is DOCUMENT_STORE + + public JSONDocument.Builder fromJson(String json) throws IOException { + this.node = MAPPER.readTree(json); + return this; + } + + public JSONDocument.Builder fromObject(Object object) throws IOException { + this.node = MAPPER.readTree(MAPPER.writeValueAsString(object)); + return this; + } + + public JSONDocument.Builder fromJsonNode(JsonNode node) { + this.node = node; + return this; + } + + public JSONDocument.Builder withDocumentType(DocumentType documentType) { + this.documentType = documentType; + return this; + } + + public JSONDocument build() { + if (node == null) { + throw new IllegalStateException("JsonNode must be set before building"); + } + return new JSONDocument(node, documentType); + } + } + + public static JSONDocument fromJson(String json) throws IOException { + return new JSONDocument.Builder().fromJson(json).build(); + } + + public static JSONDocument fromJson(String json, DocumentType type) throws IOException { + return new JSONDocument.Builder().fromJson(json).withDocumentType(type).build(); + } + + public static JSONDocument fromObject(Object object) throws IOException { + return new JSONDocument.Builder().fromObject(object).build(); + } + + public static JSONDocument fromObject(Object object, DocumentType type) throws IOException { + return new JSONDocument.Builder().fromObject(object).withDocumentType(type).build(); + } + + public static JSONDocument fromJsonNode(JsonNode jsonNode) { + return new JSONDocument.Builder().fromJsonNode(jsonNode).build(); + } + + public static JSONDocument fromJsonNode(JsonNode jsonNode, DocumentType type) throws IOException { + return new JSONDocument.Builder().fromJsonNode(jsonNode).withDocumentType(type).build(); + } + + public static JSONDocument errorDocument(String message) { + return errorDocument(message, DocumentType.DOCUMENT_STORE); + } + + public static JSONDocument errorDocument(String message, DocumentType documentType) { + ObjectNode objectNode = MAPPER.createObjectNode(); + objectNode.put("errorMessage", message); + return new JSONDocument.Builder() + .fromJsonNode(objectNode) + .withDocumentType(documentType) + .build(); + } } diff --git a/document-store/src/main/java/org/hypertrace/core/documentstore/mongo/MongoUtils.java b/document-store/src/main/java/org/hypertrace/core/documentstore/mongo/MongoUtils.java index f12868cff..2ed8a0ae3 100644 --- a/document-store/src/main/java/org/hypertrace/core/documentstore/mongo/MongoUtils.java +++ b/document-store/src/main/java/org/hypertrace/core/documentstore/mongo/MongoUtils.java @@ -137,7 +137,7 @@ public static Document dbObjectToDocument(BasicDBObject dbObject) { jsonString = dbObject.toJson(relaxed); JsonNode jsonNode = MAPPER.readTree(jsonString); JsonNode decodedJsonNode = recursiveClone(jsonNode, MongoUtils::decodeKey, identity()); - return new JSONDocument(decodedJsonNode); + return JSONDocument.fromJsonNode(decodedJsonNode); } catch (IOException e) { // throwing exception is not very useful here. return JSONDocument.errorDocument(e.getMessage()); 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 5582889a3..05460c1d6 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 @@ -61,6 +61,7 @@ import org.hypertrace.core.documentstore.Collection; import org.hypertrace.core.documentstore.CreateResult; import org.hypertrace.core.documentstore.Document; +import org.hypertrace.core.documentstore.DocumentType; import org.hypertrace.core.documentstore.Filter; import org.hypertrace.core.documentstore.JSONDocument; import org.hypertrace.core.documentstore.Key; @@ -879,7 +880,7 @@ private BulkUpdateResult bulkSetOnArrayValue( if (docRoot.isObject()) { ((ObjectNode) docRoot).put(DocStoreConstants.LAST_UPDATED_TIME, System.currentTimeMillis()); } - upsertMap.put(Key.from(id), new JSONDocument(docRoot)); + upsertMap.put(Key.from(id), JSONDocument.fromJsonNode(docRoot, DocumentType.SQL_STORE)); } return upsertDocs(upsertMap); } @@ -908,7 +909,7 @@ private BulkUpdateResult bulkRemoveOnArrayValue( candidateArray.removeAll(); candidateArray.addAll(existingItems); String id = docRoot.findValue(DOCUMENT_ID).asText(); - upsertMap.put(Key.from(id), new JSONDocument(docRoot)); + upsertMap.put(Key.from(id), JSONDocument.fromJsonNode(docRoot, DocumentType.SQL_STORE)); } return upsertDocs(upsertMap); } @@ -933,7 +934,7 @@ private BulkUpdateResult bulkAddOnArrayValue( if (docRoot.isObject()) { ((ObjectNode) docRoot).put(DocStoreConstants.LAST_UPDATED_TIME, System.currentTimeMillis()); } - upsertMap.put(Key.from(id), new JSONDocument(docRoot)); + upsertMap.put(Key.from(id), JSONDocument.fromJsonNode(docRoot, DocumentType.SQL_STORE)); } return upsertDocs(upsertMap); } @@ -1276,7 +1277,7 @@ public Document next() { } catch (IOException | SQLException e) { System.out.println("prepare document failed!"); closeResultSet(); - return JSONDocument.errorDocument(e.getMessage()); + return JSONDocument.errorDocument(e.getMessage(), DocumentType.SQL_STORE); } } @@ -1299,7 +1300,7 @@ protected Document prepareDocument() throws SQLException, IOException { jsonNode.remove(DOCUMENT_ID); } - return new JSONDocument(MAPPER.writeValueAsString(jsonNode)); + return JSONDocument.fromJson(MAPPER.writeValueAsString(jsonNode), DocumentType.SQL_STORE); } private void addColumnToJsonNode( @@ -1430,7 +1431,7 @@ public Document next() { return prepareDocument(); } catch (IOException | SQLException e) { closeResultSet(); - return JSONDocument.errorDocument(e.getMessage()); + return JSONDocument.errorDocument(e.getMessage(), DocumentType.SQL_STORE); } } @@ -1447,7 +1448,7 @@ protected Document prepareDocument() throws SQLException, IOException { jsonNode.put(CREATED_AT, String.valueOf(createdAt)); jsonNode.put(UPDATED_AT, String.valueOf(updatedAt)); - return new JSONDocument(MAPPER.writeValueAsString(jsonNode)); + return JSONDocument.fromJson(MAPPER.writeValueAsString(jsonNode), DocumentType.SQL_STORE); } protected void closeResultSet() { @@ -1508,7 +1509,7 @@ protected Document prepareDocument() throws SQLException, IOException { } } } - return new JSONDocument(MAPPER.writeValueAsString(jsonNode)); + return JSONDocument.fromJson(MAPPER.writeValueAsString(jsonNode), DocumentType.SQL_STORE); } private String getColumnValue( 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 24b8ffc6d..8e5b94e4b 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 @@ -36,6 +36,7 @@ @Slf4j public class PostgresUtils { + public static final String JSON_FIELD_ACCESSOR = "->"; private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper(); private static final String QUESTION_MARK = "?"; @@ -133,8 +134,9 @@ public static String prepareCast(String field, Type type) { public static String prepareCastForFieldAccessor(String field, Object value) { String fmt = "CAST (%s AS %s)"; Type type = getType(value); - if (type.equals(Type.STRING) || type.equals(Type.STRING_ARRAY)) + if (type.equals(Type.STRING) || type.equals(Type.STRING_ARRAY)) { return String.format(fmt, field, "TEXT"); + } return prepareCast(field, type); } @@ -587,7 +589,8 @@ public static DocumentAndId extractAndRemoveId(final Document document) throws I new ObjectMapper() .readValue(document.toJson(), new TypeReference>() {}); final String id = String.valueOf(requireNonNull(map.remove(IMPLICIT_ID))); - final Document documentWithoutId = new JSONDocument(new ObjectMapper().writeValueAsString(map)); + final Document documentWithoutId = + JSONDocument.fromJson(new ObjectMapper().writeValueAsString(map)); return new DocumentAndId(documentWithoutId, id); } diff --git a/document-store/src/test/java/org/hypertrace/core/documentstore/JSONDocumentTest.java b/document-store/src/test/java/org/hypertrace/core/documentstore/JSONDocumentTest.java index 5cd3be9ac..407d8c0aa 100644 --- a/document-store/src/test/java/org/hypertrace/core/documentstore/JSONDocumentTest.java +++ b/document-store/src/test/java/org/hypertrace/core/documentstore/JSONDocumentTest.java @@ -9,8 +9,8 @@ public class JSONDocumentTest { @Test public void testJSONDocument() throws Exception { Map data = Map.of("key1", "value1", "key2", "value2"); - JSONDocument document1 = new JSONDocument(data); - JSONDocument document2 = new JSONDocument(document1.toJson()); + JSONDocument document1 = JSONDocument.fromObject(data); + JSONDocument document2 = JSONDocument.fromJson(document1.toJson()); Assertions.assertEquals(document1, document2); Assertions.assertEquals(document1.toJson(), document2.toJson()); } diff --git a/document-store/src/test/java/org/hypertrace/core/documentstore/metric/postgres/PostgresDocStoreMetricProviderTest.java b/document-store/src/test/java/org/hypertrace/core/documentstore/metric/postgres/PostgresDocStoreMetricProviderTest.java index 2f961a34f..e47fa1279 100644 --- a/document-store/src/test/java/org/hypertrace/core/documentstore/metric/postgres/PostgresDocStoreMetricProviderTest.java +++ b/document-store/src/test/java/org/hypertrace/core/documentstore/metric/postgres/PostgresDocStoreMetricProviderTest.java @@ -46,9 +46,12 @@ class PostgresDocStoreMetricProviderTest { (PostgresConnectionConfig) PostgresConnectionConfig.builder().applicationName(postgresClientAppName).build(); - @Mock private PostgresDatastore mockDatastore; - @Mock private Collection mockCollection; - @Mock private CloseableIterator mockIterator; + @Mock + private PostgresDatastore mockDatastore; + @Mock + private Collection mockCollection; + @Mock + private CloseableIterator mockIterator; private PostgresDocStoreMetricProvider postgresDocStoreMetricProvider; @@ -92,7 +95,7 @@ void withNoValueColumnIterator_returnsDefaultMetric() throws IOException { when(mockCollection.query(query, queryOptions)).thenReturn(mockIterator); when(mockIterator.hasNext()).thenReturn(true, false); when(mockIterator.next()) - .thenReturn(new JSONDocument(Map.of("application_name", postgresClientAppName))); + .thenReturn(JSONDocument.fromObject(Map.of("application_name", postgresClientAppName))); final DocStoreMetric result = postgresDocStoreMetricProvider.getConnectionCountMetric(); assertEquals(defaultMetric, result); } @@ -119,7 +122,7 @@ void withMultipleDocuments_returnsDefaultMetric() throws IOException { when(mockIterator.hasNext()).thenReturn(true, true, false); when(mockIterator.next()) .thenReturn( - new JSONDocument( + JSONDocument.fromObject( Map.of("application_name", postgresClientAppName, "metric_value", 1))); final DocStoreMetric result = postgresDocStoreMetricProvider.getConnectionCountMetric(); assertEquals(defaultMetric, result); @@ -133,8 +136,8 @@ void withMultipleInvalidDocumentsFollowedByValidDocument_returnsTheRightMetric() when(mockIterator.next()) .thenReturn( () -> "invalid-json", - new JSONDocument(Map.of("value_missing", postgresClientAppName)), - new JSONDocument( + JSONDocument.fromObject(Map.of("value_missing", postgresClientAppName)), + JSONDocument.fromObject( Map.of("application_name", postgresClientAppName, "metric_value", 1))); final DocStoreMetric expected = DocStoreMetric.builder() @@ -152,7 +155,7 @@ void withValidDocument_returnsTheRightMetric() throws IOException { when(mockIterator.hasNext()).thenReturn(true, false); when(mockIterator.next()) .thenReturn( - new JSONDocument( + JSONDocument.fromObject( Map.of("application_name", postgresClientAppName, "metric_value", 1))); final DocStoreMetric expected = DocStoreMetric.builder() diff --git a/document-store/src/test/java/org/hypertrace/core/documentstore/mongo/MongoCollectionTest.java b/document-store/src/test/java/org/hypertrace/core/documentstore/mongo/MongoCollectionTest.java index 41574369a..77b6967f8 100644 --- a/document-store/src/test/java/org/hypertrace/core/documentstore/mongo/MongoCollectionTest.java +++ b/document-store/src/test/java/org/hypertrace/core/documentstore/mongo/MongoCollectionTest.java @@ -215,7 +215,7 @@ class CreateOrReplaceTest { @Test void testCreateOrReplace() throws IOException { final Key key = Key.from("some-key"); - final Document document = new JSONDocument("{\"planet\": \"Mars\"}"); + final Document document = JSONDocument.fromJson("{\"planet\": \"Mars\"}"); @SuppressWarnings("unchecked") final ArgumentCaptor> valueCaptor = ArgumentCaptor.forClass(List.class); @@ -263,7 +263,7 @@ class UpdateTest { private final SubDocumentUpdate quantityUpdate = SubDocumentUpdate.of("quantity", 1000); private final SubDocumentUpdate propsUpdate = SubDocumentUpdate.of( - "props", SubDocumentValue.of(new JSONDocument("{\"brand\": \"Dettol\"}"))); + "props", SubDocumentValue.of(JSONDocument.fromJson("{\"brand\": \"Dettol\"}"))); private final BasicDBObject setObject = readBasicDBObject("atomic_read_and_update/set_object.json"); private final BasicDBObject selections = @@ -370,7 +370,7 @@ class BulkUpdateTest { private final SubDocumentUpdate quantityUpdate = SubDocumentUpdate.of("quantity", 1000); private final SubDocumentUpdate propsUpdate = SubDocumentUpdate.of( - "props", SubDocumentValue.of(new JSONDocument("{\"brand\": \"Dettol\"}"))); + "props", SubDocumentValue.of(JSONDocument.fromJson("{\"brand\": \"Dettol\"}"))); private final BasicDBObject setObject = readBasicDBObject("atomic_read_and_update/set_object.json"); private final BasicDBObject selections = diff --git a/document-store/src/test/java/org/hypertrace/core/documentstore/postgres/PostgresCollectionTest.java b/document-store/src/test/java/org/hypertrace/core/documentstore/postgres/PostgresCollectionTest.java index e94f6009b..e695d5753 100644 --- a/document-store/src/test/java/org/hypertrace/core/documentstore/postgres/PostgresCollectionTest.java +++ b/document-store/src/test/java/org/hypertrace/core/documentstore/postgres/PostgresCollectionTest.java @@ -59,6 +59,7 @@ @ExtendWith(MockitoExtension.class) class PostgresCollectionTest { + private static final String COLLECTION_NAME = "test_collection"; private static final long currentTime = 1658956123L; @@ -84,7 +85,7 @@ void setUp() { @Test void testCreateOrReplace() throws Exception { final Key key = Key.from("some_key"); - final Document document = new JSONDocument("{\"planet\": \"Mars\"}"); + final Document document = JSONDocument.fromJson("{\"planet\": \"Mars\"}"); when(mockClient.getConnection()).thenReturn(mockConnection); when(mockConnection.prepareStatement( @@ -923,7 +924,7 @@ private List buildUpdates() throws IOException { final SubDocumentUpdate quantityUpdate = SubDocumentUpdate.of("quantity", 1000); final SubDocumentUpdate propsUpdate = SubDocumentUpdate.of( - "props", SubDocumentValue.of(new JSONDocument("{\"brand\": \"Dettol\"}"))); + "props", SubDocumentValue.of(JSONDocument.fromJson("{\"brand\": \"Dettol\"}"))); return List.of(dateUpdate, quantityUpdate, propsUpdate); } diff --git a/document-store/src/test/java/org/hypertrace/core/documentstore/postgres/query/v1/PostgresQueryParserTest.java b/document-store/src/test/java/org/hypertrace/core/documentstore/postgres/query/v1/PostgresQueryParserTest.java index 816d92112..7799e94f2 100644 --- a/document-store/src/test/java/org/hypertrace/core/documentstore/postgres/query/v1/PostgresQueryParserTest.java +++ b/document-store/src/test/java/org/hypertrace/core/documentstore/postgres/query/v1/PostgresQueryParserTest.java @@ -1333,7 +1333,7 @@ void testContainsFilter() throws IOException { RelationalExpression.of( IdentifierExpression.of("sales"), CONTAINS, - ConstantExpression.of(new JSONDocument("\"a\"")))) + ConstantExpression.of(JSONDocument.fromJson("\"a\"")))) .build(); PostgresQueryParser postgresQueryParser = new PostgresQueryParser(TEST_TABLE, PostgresQueryTransformer.transform(query)); @@ -1359,7 +1359,7 @@ void testContainsAndUnnestFilters() throws IOException { IdentifierExpression.of("sales.medium"), CONTAINS, ConstantExpression.of( - new JSONDocument("{\"type\": \"retail\",\"volume\": 500}")))) + JSONDocument.fromJson("{\"type\": \"retail\",\"volume\": 500}")))) .build()) .build(); @@ -1394,7 +1394,7 @@ void testNotContainsAndUnnestFilters() throws IOException { IdentifierExpression.of("sales.medium"), NOT_CONTAINS, ConstantExpression.of( - new JSONDocument("{\"type\": \"retail\",\"volume\": 500}")))) + JSONDocument.fromJson("{\"type\": \"retail\",\"volume\": 500}")))) .build()) .build(); diff --git a/document-store/src/test/java/org/hypertrace/core/documentstore/util/TestUtil.java b/document-store/src/test/java/org/hypertrace/core/documentstore/util/TestUtil.java index 7d0006192..24bfee676 100644 --- a/document-store/src/test/java/org/hypertrace/core/documentstore/util/TestUtil.java +++ b/document-store/src/test/java/org/hypertrace/core/documentstore/util/TestUtil.java @@ -33,7 +33,7 @@ public static BasicDBObject readBasicDBObject(final String filePath) throws IOEx } public static Document readDocument(final String filePath) throws IOException { - return new JSONDocument(readFileFromResource(filePath).orElseThrow()); + return JSONDocument.fromJson(readFileFromResource(filePath).orElseThrow()); } @SuppressWarnings("unchecked") From 0544358de654c39cb9667c7a8c1dd3b803e9b329 Mon Sep 17 00:00:00 2001 From: Prashant Pandey Date: Mon, 22 Sep 2025 15:11:39 +0530 Subject: [PATCH 06/19] WIP --- ...seDocumentStoreMetricProviderImplTest.java | 38 +++++++++++++++---- .../PostgresDocStoreMetricProviderTest.java | 36 ++++++++++++++---- 2 files changed, 59 insertions(+), 15 deletions(-) diff --git a/document-store/src/test/java/org/hypertrace/core/documentstore/metric/BaseDocumentStoreMetricProviderImplTest.java b/document-store/src/test/java/org/hypertrace/core/documentstore/metric/BaseDocumentStoreMetricProviderImplTest.java index deb2640e5..a5561462e 100644 --- a/document-store/src/test/java/org/hypertrace/core/documentstore/metric/BaseDocumentStoreMetricProviderImplTest.java +++ b/document-store/src/test/java/org/hypertrace/core/documentstore/metric/BaseDocumentStoreMetricProviderImplTest.java @@ -13,6 +13,7 @@ import org.hypertrace.core.documentstore.Collection; import org.hypertrace.core.documentstore.Datastore; import org.hypertrace.core.documentstore.Document; +import org.hypertrace.core.documentstore.DocumentType; import org.hypertrace.core.documentstore.JSONDocument; import org.hypertrace.core.documentstore.expression.impl.IdentifierExpression; import org.hypertrace.core.documentstore.model.config.CustomMetricConfig; @@ -91,7 +92,19 @@ void queryReturnsEmptyIterator_returnsEmptyList() { void queryReturnsInvalidJson_returnsEmptyList() { when(mockCollection.query(query, queryOptions)).thenReturn(mockIterator); when(mockIterator.hasNext()).thenReturn(true, false); - when(mockIterator.next()).thenReturn(() -> "invalid-json"); + when(mockIterator.next()) + .thenReturn( + new Document() { + @Override + public String toJson() { + return "invalid-json"; + } + + @Override + public DocumentType getDocumentType() { + return null; + } + }); final List result = baseDocStoreMetricProvider.getCustomMetrics(customMetricConfig); assertEquals(emptyList(), result); @@ -101,7 +114,7 @@ void queryReturnsInvalidJson_returnsEmptyList() { void queryReturnsDocumentWithMissingValue_returnsEmptyList() throws IOException { when(mockCollection.query(query, queryOptions)).thenReturn(mockIterator); when(mockIterator.hasNext()).thenReturn(true, false); - when(mockIterator.next()).thenReturn(new JSONDocument(Map.of("label1", "l1value"))); + when(mockIterator.next()).thenReturn(JSONDocument.fromObject(Map.of("label1", "l1value"))); final List result = baseDocStoreMetricProvider.getCustomMetrics(customMetricConfig); assertEquals(emptyList(), result); @@ -113,11 +126,22 @@ void queryReturnsValidAndInvalidDocument_returnsEmptyList() throws IOException { when(mockIterator.hasNext()).thenReturn(true, true, true, true, true, false); when(mockIterator.next()) .thenReturn( - () -> "invalid-json", - new JSONDocument(Map.of("label1", "l1value", "label2", "l2value", "metric_value", 1)), - new JSONDocument(Map.of("label1", "l1value")), - new JSONDocument(Map.of("label1", "l2value", "metric_value", 2)), - new JSONDocument(Map.of("label1", "l2value", "metric_value", 3))); + new Document() { + @Override + public String toJson() { + return "invalid-json"; + } + + @Override + public DocumentType getDocumentType() { + return null; + } + }, + JSONDocument.fromObject( + Map.of("label1", "l1value", "label2", "l2value", "metric_value", 1)), + JSONDocument.fromObject(Map.of("label1", "l1value")), + JSONDocument.fromObject(Map.of("label1", "l2value", "metric_value", 2)), + JSONDocument.fromObject(Map.of("label1", "l2value", "metric_value", 3))); final List expected = List.of( DocStoreMetric.builder() diff --git a/document-store/src/test/java/org/hypertrace/core/documentstore/metric/postgres/PostgresDocStoreMetricProviderTest.java b/document-store/src/test/java/org/hypertrace/core/documentstore/metric/postgres/PostgresDocStoreMetricProviderTest.java index e47fa1279..622f33437 100644 --- a/document-store/src/test/java/org/hypertrace/core/documentstore/metric/postgres/PostgresDocStoreMetricProviderTest.java +++ b/document-store/src/test/java/org/hypertrace/core/documentstore/metric/postgres/PostgresDocStoreMetricProviderTest.java @@ -14,6 +14,7 @@ import org.hypertrace.core.documentstore.CloseableIterator; import org.hypertrace.core.documentstore.Collection; import org.hypertrace.core.documentstore.Document; +import org.hypertrace.core.documentstore.DocumentType; import org.hypertrace.core.documentstore.JSONDocument; import org.hypertrace.core.documentstore.expression.impl.AggregateExpression; import org.hypertrace.core.documentstore.expression.impl.ConstantExpression; @@ -46,12 +47,9 @@ class PostgresDocStoreMetricProviderTest { (PostgresConnectionConfig) PostgresConnectionConfig.builder().applicationName(postgresClientAppName).build(); - @Mock - private PostgresDatastore mockDatastore; - @Mock - private Collection mockCollection; - @Mock - private CloseableIterator mockIterator; + @Mock private PostgresDatastore mockDatastore; + @Mock private Collection mockCollection; + @Mock private CloseableIterator mockIterator; private PostgresDocStoreMetricProvider postgresDocStoreMetricProvider; @@ -111,7 +109,19 @@ void withQueryExecutionException_returnsDefaultMetric() { void withInvalidJson_returnsDefaultMetric() { when(mockCollection.query(query, queryOptions)).thenReturn(mockIterator); when(mockIterator.hasNext()).thenReturn(true, false); - when(mockIterator.next()).thenReturn(() -> "invalid-json"); + when(mockIterator.next()) + .thenReturn( + new Document() { + @Override + public String toJson() { + return "invalid-json"; + } + + @Override + public DocumentType getDocumentType() { + return null; + } + }); final DocStoreMetric result = postgresDocStoreMetricProvider.getConnectionCountMetric(); assertEquals(defaultMetric, result); } @@ -135,7 +145,17 @@ void withMultipleInvalidDocumentsFollowedByValidDocument_returnsTheRightMetric() when(mockIterator.hasNext()).thenReturn(true, true, true, false); when(mockIterator.next()) .thenReturn( - () -> "invalid-json", + new Document() { + @Override + public String toJson() { + return "invalid-json"; + } + + @Override + public DocumentType getDocumentType() { + return null; + } + }, JSONDocument.fromObject(Map.of("value_missing", postgresClientAppName)), JSONDocument.fromObject( Map.of("application_name", postgresClientAppName, "metric_value", 1))); From 82dc37fcc41b80e3959c548b1c4e13c40e6e449b Mon Sep 17 00:00:00 2001 From: Prashant Pandey Date: Mon, 22 Sep 2025 15:20:56 +0530 Subject: [PATCH 07/19] WIP --- .../core/documentstore/JSONDocumentTest.java | 93 ++++++++++++++++++- 1 file changed, 90 insertions(+), 3 deletions(-) diff --git a/document-store/src/test/java/org/hypertrace/core/documentstore/JSONDocumentTest.java b/document-store/src/test/java/org/hypertrace/core/documentstore/JSONDocumentTest.java index 407d8c0aa..06b24cd4c 100644 --- a/document-store/src/test/java/org/hypertrace/core/documentstore/JSONDocumentTest.java +++ b/document-store/src/test/java/org/hypertrace/core/documentstore/JSONDocumentTest.java @@ -1,17 +1,104 @@ package org.hypertrace.core.documentstore; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.node.ObjectNode; +import java.io.IOException; import java.util.Map; -import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; public class JSONDocumentTest { + private static final ObjectMapper MAPPER = new ObjectMapper(); + @Test public void testJSONDocument() throws Exception { Map data = Map.of("key1", "value1", "key2", "value2"); JSONDocument document1 = JSONDocument.fromObject(data); JSONDocument document2 = JSONDocument.fromJson(document1.toJson()); - Assertions.assertEquals(document1, document2); - Assertions.assertEquals(document1.toJson(), document2.toJson()); + assertEquals(document1, document2); + assertEquals(document1.toJson(), document2.toJson()); + } + + @Test + void testFromJson() throws IOException { + String json = "{\"name\":\"John\",\"age\":30}"; + JSONDocument document = JSONDocument.fromJson(json); + + assertNotNull(document); + // default document type is DOCUMENT_STORE + assertEquals(DocumentType.DOCUMENT_STORE, document.getDocumentType()); + assertTrue(document.toJson().contains("John")); + } + + @Test + void testFromJsonWithDocumentType() throws IOException { + String json = "{\"name\":\"John\",\"age\":30}"; + JSONDocument document = JSONDocument.fromJson(json, DocumentType.SQL_STORE); + + assertNotNull(document); + assertEquals(DocumentType.SQL_STORE, document.getDocumentType()); + } + + @Test + void testFromObject() throws IOException { + Map data = Map.of("name", "Alice", "age", 25); + JSONDocument document = JSONDocument.fromObject(data); + + assertNotNull(document); + assertEquals(DocumentType.DOCUMENT_STORE, document.getDocumentType()); + assertTrue(document.toJson().contains("Alice")); + } + + @Test + void testFromObjectWithDocumentType() throws IOException { + Map data = Map.of("key", "value"); + JSONDocument document = JSONDocument.fromObject(data, DocumentType.SQL_STORE); + + assertNotNull(document); + assertEquals(DocumentType.SQL_STORE, document.getDocumentType()); + } + + @Test + void testFromJsonNode() throws IOException { + ObjectNode node = MAPPER.createObjectNode(); + node.put("city", "New York"); + + JSONDocument document = JSONDocument.fromJsonNode(node); + + assertNotNull(document); + assertEquals(DocumentType.DOCUMENT_STORE, document.getDocumentType()); + assertTrue(document.toJson().contains("New York")); + } + + @Test + void testErrorDocument() { + String errorMessage = "Something went wrong"; + JSONDocument errorDoc = JSONDocument.errorDocument(errorMessage); + + assertNotNull(errorDoc); + assertEquals(DocumentType.DOCUMENT_STORE, errorDoc.getDocumentType()); + assertTrue(errorDoc.toJson().contains(errorMessage)); + } + + @Test + void testErrorDocumentWithDocumentType() { + String errorMessage = "Database connection failed"; + JSONDocument errorDoc = JSONDocument.errorDocument(errorMessage, DocumentType.SQL_STORE); + + assertNotNull(errorDoc); + assertEquals(DocumentType.SQL_STORE, errorDoc.getDocumentType()); + assertTrue(errorDoc.toJson().contains(errorMessage)); + } + + @Test + void testToString() throws IOException { + Map data = Map.of("key", "value"); + JSONDocument document = JSONDocument.fromObject(data); + + assertEquals(document.toJson(), document.toString()); } } From c0ed72aea614de4f5d34af3c4dcccb1197583721 Mon Sep 17 00:00:00 2001 From: Prashant Pandey Date: Mon, 22 Sep 2025 15:28:28 +0530 Subject: [PATCH 08/19] Add default impl for `getDocumentType` --- .../main/java/org/hypertrace/core/documentstore/Document.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/document-store/src/main/java/org/hypertrace/core/documentstore/Document.java b/document-store/src/main/java/org/hypertrace/core/documentstore/Document.java index a9327779e..5e48e4011 100644 --- a/document-store/src/main/java/org/hypertrace/core/documentstore/Document.java +++ b/document-store/src/main/java/org/hypertrace/core/documentstore/Document.java @@ -4,5 +4,7 @@ public interface Document { String toJson(); - DocumentType getDocumentType(); + default DocumentType getDocumentType() { + return null; + } } From e9f25da711da02f3e9a71c81fe0e9a773affd0d6 Mon Sep 17 00:00:00 2001 From: Prashant Pandey Date: Mon, 22 Sep 2025 15:57:25 +0530 Subject: [PATCH 09/19] WIP --- .../core/documentstore/Document.java | 4 +++ .../core/documentstore/DocumentType.java | 6 +++++ .../core/documentstore/JSONDocument.java | 27 +++++++++++++++++++ .../core/documentstore/mongo/MongoUtils.java | 6 +++-- .../postgres/PostgresCollection.java | 11 ++++---- 5 files changed, 47 insertions(+), 7 deletions(-) create mode 100644 document-store/src/main/java/org/hypertrace/core/documentstore/DocumentType.java diff --git a/document-store/src/main/java/org/hypertrace/core/documentstore/Document.java b/document-store/src/main/java/org/hypertrace/core/documentstore/Document.java index 944913aac..5e48e4011 100644 --- a/document-store/src/main/java/org/hypertrace/core/documentstore/Document.java +++ b/document-store/src/main/java/org/hypertrace/core/documentstore/Document.java @@ -3,4 +3,8 @@ public interface Document { String toJson(); + + default DocumentType getDocumentType() { + return null; + } } diff --git a/document-store/src/main/java/org/hypertrace/core/documentstore/DocumentType.java b/document-store/src/main/java/org/hypertrace/core/documentstore/DocumentType.java new file mode 100644 index 000000000..745505ef2 --- /dev/null +++ b/document-store/src/main/java/org/hypertrace/core/documentstore/DocumentType.java @@ -0,0 +1,6 @@ +package org.hypertrace.core.documentstore; + +public enum DocumentType { + SQL_STORE, + DOCUMENT_STORE +} diff --git a/document-store/src/main/java/org/hypertrace/core/documentstore/JSONDocument.java b/document-store/src/main/java/org/hypertrace/core/documentstore/JSONDocument.java index c04d806de..68470f7db 100644 --- a/document-store/src/main/java/org/hypertrace/core/documentstore/JSONDocument.java +++ b/document-store/src/main/java/org/hypertrace/core/documentstore/JSONDocument.java @@ -11,19 +11,40 @@ public class JSONDocument implements Document { private static ObjectMapper mapper = new ObjectMapper(); private JsonNode node; + private DocumentType documentType; public JSONDocument(String json) throws IOException { node = mapper.readTree(json); } + public JSONDocument(String json, DocumentType documentType) throws IOException { + node = mapper.readTree(json); + this.documentType = documentType; + } + public JSONDocument(Object object) throws IOException { node = mapper.readTree(mapper.writeValueAsString(object)); } + public JSONDocument(Object object, DocumentType documentType) throws IOException { + node = mapper.readTree(mapper.writeValueAsString(object)); + this.documentType = documentType; + } + public JSONDocument(JsonNode node) { this.node = node; } + public JSONDocument(JsonNode node, DocumentType documentType) { + this.node = node; + this.documentType = documentType; + } + + @Override + public DocumentType getDocumentType() { + return this.documentType; + } + @Override public String toJson() { try { @@ -39,6 +60,12 @@ public static JSONDocument errorDocument(String message) { return new JSONDocument(objectNode); } + public static JSONDocument errorDocument(String message, DocumentType documentType) { + ObjectNode objectNode = mapper.createObjectNode(); + objectNode.put("errorMessage", message); + return new JSONDocument(objectNode, documentType); + } + @Override public String toString() { return toJson(); diff --git a/document-store/src/main/java/org/hypertrace/core/documentstore/mongo/MongoUtils.java b/document-store/src/main/java/org/hypertrace/core/documentstore/mongo/MongoUtils.java index f12868cff..4598a22fc 100644 --- a/document-store/src/main/java/org/hypertrace/core/documentstore/mongo/MongoUtils.java +++ b/document-store/src/main/java/org/hypertrace/core/documentstore/mongo/MongoUtils.java @@ -29,10 +29,12 @@ import org.bson.json.JsonMode; import org.bson.json.JsonWriterSettings; import org.hypertrace.core.documentstore.Document; +import org.hypertrace.core.documentstore.DocumentType; import org.hypertrace.core.documentstore.JSONDocument; import org.hypertrace.core.documentstore.model.options.ReturnDocumentType; public final class MongoUtils { + public static final String FIELD_SEPARATOR = "."; public static final String PREFIX = "$"; private static final String LITERAL = PREFIX + "literal"; @@ -137,10 +139,10 @@ public static Document dbObjectToDocument(BasicDBObject dbObject) { jsonString = dbObject.toJson(relaxed); JsonNode jsonNode = MAPPER.readTree(jsonString); JsonNode decodedJsonNode = recursiveClone(jsonNode, MongoUtils::decodeKey, identity()); - return new JSONDocument(decodedJsonNode); + return new JSONDocument(decodedJsonNode, DocumentType.DOCUMENT_STORE); } catch (IOException e) { // throwing exception is not very useful here. - return JSONDocument.errorDocument(e.getMessage()); + return JSONDocument.errorDocument(e.getMessage(), DocumentType.DOCUMENT_STORE); } } 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 5582889a3..5e496e08e 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 @@ -61,6 +61,7 @@ import org.hypertrace.core.documentstore.Collection; import org.hypertrace.core.documentstore.CreateResult; import org.hypertrace.core.documentstore.Document; +import org.hypertrace.core.documentstore.DocumentType; import org.hypertrace.core.documentstore.Filter; import org.hypertrace.core.documentstore.JSONDocument; import org.hypertrace.core.documentstore.Key; @@ -1276,7 +1277,7 @@ public Document next() { } catch (IOException | SQLException e) { System.out.println("prepare document failed!"); closeResultSet(); - return JSONDocument.errorDocument(e.getMessage()); + return JSONDocument.errorDocument(e.getMessage(), DocumentType.SQL_STORE); } } @@ -1299,7 +1300,7 @@ protected Document prepareDocument() throws SQLException, IOException { jsonNode.remove(DOCUMENT_ID); } - return new JSONDocument(MAPPER.writeValueAsString(jsonNode)); + return new JSONDocument(MAPPER.writeValueAsString(jsonNode), DocumentType.SQL_STORE); } private void addColumnToJsonNode( @@ -1430,7 +1431,7 @@ public Document next() { return prepareDocument(); } catch (IOException | SQLException e) { closeResultSet(); - return JSONDocument.errorDocument(e.getMessage()); + return JSONDocument.errorDocument(e.getMessage(), DocumentType.SQL_STORE); } } @@ -1447,7 +1448,7 @@ protected Document prepareDocument() throws SQLException, IOException { jsonNode.put(CREATED_AT, String.valueOf(createdAt)); jsonNode.put(UPDATED_AT, String.valueOf(updatedAt)); - return new JSONDocument(MAPPER.writeValueAsString(jsonNode)); + return new JSONDocument(MAPPER.writeValueAsString(jsonNode), DocumentType.SQL_STORE); } protected void closeResultSet() { @@ -1508,7 +1509,7 @@ protected Document prepareDocument() throws SQLException, IOException { } } } - return new JSONDocument(MAPPER.writeValueAsString(jsonNode)); + return new JSONDocument(MAPPER.writeValueAsString(jsonNode), DocumentType.SQL_STORE); } private String getColumnValue( From ee7678f64a5ab544c231d9c189d0a127685d352e Mon Sep 17 00:00:00 2001 From: Prashant Pandey Date: Mon, 22 Sep 2025 16:07:56 +0530 Subject: [PATCH 10/19] Rollback unnecessary changes --- .../mongo/MongoDocStoreTest.java | 6 +- .../core/documentstore/utils/Utils.java | 9 +- .../postgres/utils/PostgresUtils.java | 7 +- .../core/documentstore/JSONDocumentTest.java | 97 +------------------ ...seDocumentStoreMetricProviderImplTest.java | 38 ++------ .../PostgresDocStoreMetricProviderTest.java | 37 ++----- .../mongo/MongoCollectionTest.java | 6 +- .../postgres/PostgresCollectionTest.java | 5 +- .../query/v1/PostgresQueryParserTest.java | 6 +- .../core/documentstore/util/TestUtil.java | 2 +- 10 files changed, 37 insertions(+), 176 deletions(-) diff --git a/document-store/src/integrationTest/java/org/hypertrace/core/documentstore/mongo/MongoDocStoreTest.java b/document-store/src/integrationTest/java/org/hypertrace/core/documentstore/mongo/MongoDocStoreTest.java index 81157694d..488f77ae6 100644 --- a/document-store/src/integrationTest/java/org/hypertrace/core/documentstore/mongo/MongoDocStoreTest.java +++ b/document-store/src/integrationTest/java/org/hypertrace/core/documentstore/mongo/MongoDocStoreTest.java @@ -125,7 +125,7 @@ public void testUpsertAndReturn() throws IOException { Collection collection = datastore.getCollection(COLLECTION_NAME); ObjectNode objectNode = OBJECT_MAPPER.createObjectNode(); objectNode.put("foo1", "bar1"); - Document document = JSONDocument.fromObject(objectNode); + Document document = new JSONDocument(objectNode); Document persistedDocument = collection.upsertAndReturn(new SingleValueKey("default", "testKey"), document); @@ -140,7 +140,7 @@ public void testUpsertAndReturn() throws IOException { objectNode = OBJECT_MAPPER.createObjectNode(); objectNode.put("foo2", "bar2"); - document = JSONDocument.fromObject(objectNode); + document = new JSONDocument(objectNode); // Upsert again and verify that createdTime does not change, while lastUpdatedTime // has changed and values have merged @@ -161,7 +161,7 @@ public void testBulkUpsertAndVerifyUpdatedTime() throws IOException { Collection collection = datastore.getCollection(COLLECTION_NAME); ObjectNode objectNode = OBJECT_MAPPER.createObjectNode(); objectNode.put("foo1", "bar1"); - Document document = JSONDocument.fromObject(objectNode); + Document document = new JSONDocument(objectNode); collection.bulkUpsert(Map.of(new SingleValueKey("default", "testKey"), document)); Query query = new Query(); diff --git a/document-store/src/integrationTest/java/org/hypertrace/core/documentstore/utils/Utils.java b/document-store/src/integrationTest/java/org/hypertrace/core/documentstore/utils/Utils.java index 2449df011..75ea277f5 100644 --- a/document-store/src/integrationTest/java/org/hypertrace/core/documentstore/utils/Utils.java +++ b/document-store/src/integrationTest/java/org/hypertrace/core/documentstore/utils/Utils.java @@ -27,7 +27,6 @@ import org.testcontainers.shaded.org.apache.commons.io.IOUtils; public class Utils { - private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper(); public static final String MONGO_STORE = "Mongo"; @@ -63,13 +62,13 @@ public static Document createDocument(ImmutablePair... pairs) { objectNode.putPOJO(pairs[i].getLeft(), pairs[i].getRight()); } } - return JSONDocument.fromJsonNode(objectNode); + return new JSONDocument(objectNode); } public static Document createDocument(String key, String value) { ObjectNode objectNode = OBJECT_MAPPER.createObjectNode(); objectNode.put(key, value); - return JSONDocument.fromJsonNode(objectNode); + return new JSONDocument(objectNode); } public static Document createDocument(String... keys) { @@ -77,7 +76,7 @@ public static Document createDocument(String... keys) { for (int i = 0; i < keys.length - 1; i++) { objectNode.put(keys[i], keys[i + 1]); } - return JSONDocument.fromJsonNode(objectNode); + return new JSONDocument(objectNode); } public static Optional readFileFromResource(String filePath) throws IOException { @@ -136,7 +135,7 @@ public static Map buildDocumentsFromResource(String resourcePath) Map documentMap = new HashMap<>(); for (Map map : maps) { Key key = new SingleValueKey(TENANT_ID, map.get(MongoCollection.ID_KEY).toString()); - Document value = JSONDocument.fromObject(map); + Document value = new JSONDocument(map); documentMap.put(key, value); } 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 8e5b94e4b..24b8ffc6d 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 @@ -36,7 +36,6 @@ @Slf4j public class PostgresUtils { - public static final String JSON_FIELD_ACCESSOR = "->"; private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper(); private static final String QUESTION_MARK = "?"; @@ -134,9 +133,8 @@ public static String prepareCast(String field, Type type) { public static String prepareCastForFieldAccessor(String field, Object value) { String fmt = "CAST (%s AS %s)"; Type type = getType(value); - if (type.equals(Type.STRING) || type.equals(Type.STRING_ARRAY)) { + if (type.equals(Type.STRING) || type.equals(Type.STRING_ARRAY)) return String.format(fmt, field, "TEXT"); - } return prepareCast(field, type); } @@ -589,8 +587,7 @@ public static DocumentAndId extractAndRemoveId(final Document document) throws I new ObjectMapper() .readValue(document.toJson(), new TypeReference>() {}); final String id = String.valueOf(requireNonNull(map.remove(IMPLICIT_ID))); - final Document documentWithoutId = - JSONDocument.fromJson(new ObjectMapper().writeValueAsString(map)); + final Document documentWithoutId = new JSONDocument(new ObjectMapper().writeValueAsString(map)); return new DocumentAndId(documentWithoutId, id); } diff --git a/document-store/src/test/java/org/hypertrace/core/documentstore/JSONDocumentTest.java b/document-store/src/test/java/org/hypertrace/core/documentstore/JSONDocumentTest.java index 06b24cd4c..5cd3be9ac 100644 --- a/document-store/src/test/java/org/hypertrace/core/documentstore/JSONDocumentTest.java +++ b/document-store/src/test/java/org/hypertrace/core/documentstore/JSONDocumentTest.java @@ -1,104 +1,17 @@ package org.hypertrace.core.documentstore; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNotNull; -import static org.junit.jupiter.api.Assertions.assertTrue; - -import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.databind.node.ObjectNode; -import java.io.IOException; import java.util.Map; +import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; public class JSONDocumentTest { - private static final ObjectMapper MAPPER = new ObjectMapper(); - @Test public void testJSONDocument() throws Exception { Map data = Map.of("key1", "value1", "key2", "value2"); - JSONDocument document1 = JSONDocument.fromObject(data); - JSONDocument document2 = JSONDocument.fromJson(document1.toJson()); - assertEquals(document1, document2); - assertEquals(document1.toJson(), document2.toJson()); - } - - @Test - void testFromJson() throws IOException { - String json = "{\"name\":\"John\",\"age\":30}"; - JSONDocument document = JSONDocument.fromJson(json); - - assertNotNull(document); - // default document type is DOCUMENT_STORE - assertEquals(DocumentType.DOCUMENT_STORE, document.getDocumentType()); - assertTrue(document.toJson().contains("John")); - } - - @Test - void testFromJsonWithDocumentType() throws IOException { - String json = "{\"name\":\"John\",\"age\":30}"; - JSONDocument document = JSONDocument.fromJson(json, DocumentType.SQL_STORE); - - assertNotNull(document); - assertEquals(DocumentType.SQL_STORE, document.getDocumentType()); - } - - @Test - void testFromObject() throws IOException { - Map data = Map.of("name", "Alice", "age", 25); - JSONDocument document = JSONDocument.fromObject(data); - - assertNotNull(document); - assertEquals(DocumentType.DOCUMENT_STORE, document.getDocumentType()); - assertTrue(document.toJson().contains("Alice")); - } - - @Test - void testFromObjectWithDocumentType() throws IOException { - Map data = Map.of("key", "value"); - JSONDocument document = JSONDocument.fromObject(data, DocumentType.SQL_STORE); - - assertNotNull(document); - assertEquals(DocumentType.SQL_STORE, document.getDocumentType()); - } - - @Test - void testFromJsonNode() throws IOException { - ObjectNode node = MAPPER.createObjectNode(); - node.put("city", "New York"); - - JSONDocument document = JSONDocument.fromJsonNode(node); - - assertNotNull(document); - assertEquals(DocumentType.DOCUMENT_STORE, document.getDocumentType()); - assertTrue(document.toJson().contains("New York")); - } - - @Test - void testErrorDocument() { - String errorMessage = "Something went wrong"; - JSONDocument errorDoc = JSONDocument.errorDocument(errorMessage); - - assertNotNull(errorDoc); - assertEquals(DocumentType.DOCUMENT_STORE, errorDoc.getDocumentType()); - assertTrue(errorDoc.toJson().contains(errorMessage)); - } - - @Test - void testErrorDocumentWithDocumentType() { - String errorMessage = "Database connection failed"; - JSONDocument errorDoc = JSONDocument.errorDocument(errorMessage, DocumentType.SQL_STORE); - - assertNotNull(errorDoc); - assertEquals(DocumentType.SQL_STORE, errorDoc.getDocumentType()); - assertTrue(errorDoc.toJson().contains(errorMessage)); - } - - @Test - void testToString() throws IOException { - Map data = Map.of("key", "value"); - JSONDocument document = JSONDocument.fromObject(data); - - assertEquals(document.toJson(), document.toString()); + JSONDocument document1 = new JSONDocument(data); + JSONDocument document2 = new JSONDocument(document1.toJson()); + Assertions.assertEquals(document1, document2); + Assertions.assertEquals(document1.toJson(), document2.toJson()); } } diff --git a/document-store/src/test/java/org/hypertrace/core/documentstore/metric/BaseDocumentStoreMetricProviderImplTest.java b/document-store/src/test/java/org/hypertrace/core/documentstore/metric/BaseDocumentStoreMetricProviderImplTest.java index a5561462e..deb2640e5 100644 --- a/document-store/src/test/java/org/hypertrace/core/documentstore/metric/BaseDocumentStoreMetricProviderImplTest.java +++ b/document-store/src/test/java/org/hypertrace/core/documentstore/metric/BaseDocumentStoreMetricProviderImplTest.java @@ -13,7 +13,6 @@ import org.hypertrace.core.documentstore.Collection; import org.hypertrace.core.documentstore.Datastore; import org.hypertrace.core.documentstore.Document; -import org.hypertrace.core.documentstore.DocumentType; import org.hypertrace.core.documentstore.JSONDocument; import org.hypertrace.core.documentstore.expression.impl.IdentifierExpression; import org.hypertrace.core.documentstore.model.config.CustomMetricConfig; @@ -92,19 +91,7 @@ void queryReturnsEmptyIterator_returnsEmptyList() { void queryReturnsInvalidJson_returnsEmptyList() { when(mockCollection.query(query, queryOptions)).thenReturn(mockIterator); when(mockIterator.hasNext()).thenReturn(true, false); - when(mockIterator.next()) - .thenReturn( - new Document() { - @Override - public String toJson() { - return "invalid-json"; - } - - @Override - public DocumentType getDocumentType() { - return null; - } - }); + when(mockIterator.next()).thenReturn(() -> "invalid-json"); final List result = baseDocStoreMetricProvider.getCustomMetrics(customMetricConfig); assertEquals(emptyList(), result); @@ -114,7 +101,7 @@ public DocumentType getDocumentType() { void queryReturnsDocumentWithMissingValue_returnsEmptyList() throws IOException { when(mockCollection.query(query, queryOptions)).thenReturn(mockIterator); when(mockIterator.hasNext()).thenReturn(true, false); - when(mockIterator.next()).thenReturn(JSONDocument.fromObject(Map.of("label1", "l1value"))); + when(mockIterator.next()).thenReturn(new JSONDocument(Map.of("label1", "l1value"))); final List result = baseDocStoreMetricProvider.getCustomMetrics(customMetricConfig); assertEquals(emptyList(), result); @@ -126,22 +113,11 @@ void queryReturnsValidAndInvalidDocument_returnsEmptyList() throws IOException { when(mockIterator.hasNext()).thenReturn(true, true, true, true, true, false); when(mockIterator.next()) .thenReturn( - new Document() { - @Override - public String toJson() { - return "invalid-json"; - } - - @Override - public DocumentType getDocumentType() { - return null; - } - }, - JSONDocument.fromObject( - Map.of("label1", "l1value", "label2", "l2value", "metric_value", 1)), - JSONDocument.fromObject(Map.of("label1", "l1value")), - JSONDocument.fromObject(Map.of("label1", "l2value", "metric_value", 2)), - JSONDocument.fromObject(Map.of("label1", "l2value", "metric_value", 3))); + () -> "invalid-json", + new JSONDocument(Map.of("label1", "l1value", "label2", "l2value", "metric_value", 1)), + new JSONDocument(Map.of("label1", "l1value")), + new JSONDocument(Map.of("label1", "l2value", "metric_value", 2)), + new JSONDocument(Map.of("label1", "l2value", "metric_value", 3))); final List expected = List.of( DocStoreMetric.builder() diff --git a/document-store/src/test/java/org/hypertrace/core/documentstore/metric/postgres/PostgresDocStoreMetricProviderTest.java b/document-store/src/test/java/org/hypertrace/core/documentstore/metric/postgres/PostgresDocStoreMetricProviderTest.java index 622f33437..2f961a34f 100644 --- a/document-store/src/test/java/org/hypertrace/core/documentstore/metric/postgres/PostgresDocStoreMetricProviderTest.java +++ b/document-store/src/test/java/org/hypertrace/core/documentstore/metric/postgres/PostgresDocStoreMetricProviderTest.java @@ -14,7 +14,6 @@ import org.hypertrace.core.documentstore.CloseableIterator; import org.hypertrace.core.documentstore.Collection; import org.hypertrace.core.documentstore.Document; -import org.hypertrace.core.documentstore.DocumentType; import org.hypertrace.core.documentstore.JSONDocument; import org.hypertrace.core.documentstore.expression.impl.AggregateExpression; import org.hypertrace.core.documentstore.expression.impl.ConstantExpression; @@ -93,7 +92,7 @@ void withNoValueColumnIterator_returnsDefaultMetric() throws IOException { when(mockCollection.query(query, queryOptions)).thenReturn(mockIterator); when(mockIterator.hasNext()).thenReturn(true, false); when(mockIterator.next()) - .thenReturn(JSONDocument.fromObject(Map.of("application_name", postgresClientAppName))); + .thenReturn(new JSONDocument(Map.of("application_name", postgresClientAppName))); final DocStoreMetric result = postgresDocStoreMetricProvider.getConnectionCountMetric(); assertEquals(defaultMetric, result); } @@ -109,19 +108,7 @@ void withQueryExecutionException_returnsDefaultMetric() { void withInvalidJson_returnsDefaultMetric() { when(mockCollection.query(query, queryOptions)).thenReturn(mockIterator); when(mockIterator.hasNext()).thenReturn(true, false); - when(mockIterator.next()) - .thenReturn( - new Document() { - @Override - public String toJson() { - return "invalid-json"; - } - - @Override - public DocumentType getDocumentType() { - return null; - } - }); + when(mockIterator.next()).thenReturn(() -> "invalid-json"); final DocStoreMetric result = postgresDocStoreMetricProvider.getConnectionCountMetric(); assertEquals(defaultMetric, result); } @@ -132,7 +119,7 @@ void withMultipleDocuments_returnsDefaultMetric() throws IOException { when(mockIterator.hasNext()).thenReturn(true, true, false); when(mockIterator.next()) .thenReturn( - JSONDocument.fromObject( + new JSONDocument( Map.of("application_name", postgresClientAppName, "metric_value", 1))); final DocStoreMetric result = postgresDocStoreMetricProvider.getConnectionCountMetric(); assertEquals(defaultMetric, result); @@ -145,19 +132,9 @@ void withMultipleInvalidDocumentsFollowedByValidDocument_returnsTheRightMetric() when(mockIterator.hasNext()).thenReturn(true, true, true, false); when(mockIterator.next()) .thenReturn( - new Document() { - @Override - public String toJson() { - return "invalid-json"; - } - - @Override - public DocumentType getDocumentType() { - return null; - } - }, - JSONDocument.fromObject(Map.of("value_missing", postgresClientAppName)), - JSONDocument.fromObject( + () -> "invalid-json", + new JSONDocument(Map.of("value_missing", postgresClientAppName)), + new JSONDocument( Map.of("application_name", postgresClientAppName, "metric_value", 1))); final DocStoreMetric expected = DocStoreMetric.builder() @@ -175,7 +152,7 @@ void withValidDocument_returnsTheRightMetric() throws IOException { when(mockIterator.hasNext()).thenReturn(true, false); when(mockIterator.next()) .thenReturn( - JSONDocument.fromObject( + new JSONDocument( Map.of("application_name", postgresClientAppName, "metric_value", 1))); final DocStoreMetric expected = DocStoreMetric.builder() diff --git a/document-store/src/test/java/org/hypertrace/core/documentstore/mongo/MongoCollectionTest.java b/document-store/src/test/java/org/hypertrace/core/documentstore/mongo/MongoCollectionTest.java index 77b6967f8..41574369a 100644 --- a/document-store/src/test/java/org/hypertrace/core/documentstore/mongo/MongoCollectionTest.java +++ b/document-store/src/test/java/org/hypertrace/core/documentstore/mongo/MongoCollectionTest.java @@ -215,7 +215,7 @@ class CreateOrReplaceTest { @Test void testCreateOrReplace() throws IOException { final Key key = Key.from("some-key"); - final Document document = JSONDocument.fromJson("{\"planet\": \"Mars\"}"); + final Document document = new JSONDocument("{\"planet\": \"Mars\"}"); @SuppressWarnings("unchecked") final ArgumentCaptor> valueCaptor = ArgumentCaptor.forClass(List.class); @@ -263,7 +263,7 @@ class UpdateTest { private final SubDocumentUpdate quantityUpdate = SubDocumentUpdate.of("quantity", 1000); private final SubDocumentUpdate propsUpdate = SubDocumentUpdate.of( - "props", SubDocumentValue.of(JSONDocument.fromJson("{\"brand\": \"Dettol\"}"))); + "props", SubDocumentValue.of(new JSONDocument("{\"brand\": \"Dettol\"}"))); private final BasicDBObject setObject = readBasicDBObject("atomic_read_and_update/set_object.json"); private final BasicDBObject selections = @@ -370,7 +370,7 @@ class BulkUpdateTest { private final SubDocumentUpdate quantityUpdate = SubDocumentUpdate.of("quantity", 1000); private final SubDocumentUpdate propsUpdate = SubDocumentUpdate.of( - "props", SubDocumentValue.of(JSONDocument.fromJson("{\"brand\": \"Dettol\"}"))); + "props", SubDocumentValue.of(new JSONDocument("{\"brand\": \"Dettol\"}"))); private final BasicDBObject setObject = readBasicDBObject("atomic_read_and_update/set_object.json"); private final BasicDBObject selections = diff --git a/document-store/src/test/java/org/hypertrace/core/documentstore/postgres/PostgresCollectionTest.java b/document-store/src/test/java/org/hypertrace/core/documentstore/postgres/PostgresCollectionTest.java index e695d5753..e94f6009b 100644 --- a/document-store/src/test/java/org/hypertrace/core/documentstore/postgres/PostgresCollectionTest.java +++ b/document-store/src/test/java/org/hypertrace/core/documentstore/postgres/PostgresCollectionTest.java @@ -59,7 +59,6 @@ @ExtendWith(MockitoExtension.class) class PostgresCollectionTest { - private static final String COLLECTION_NAME = "test_collection"; private static final long currentTime = 1658956123L; @@ -85,7 +84,7 @@ void setUp() { @Test void testCreateOrReplace() throws Exception { final Key key = Key.from("some_key"); - final Document document = JSONDocument.fromJson("{\"planet\": \"Mars\"}"); + final Document document = new JSONDocument("{\"planet\": \"Mars\"}"); when(mockClient.getConnection()).thenReturn(mockConnection); when(mockConnection.prepareStatement( @@ -924,7 +923,7 @@ private List buildUpdates() throws IOException { final SubDocumentUpdate quantityUpdate = SubDocumentUpdate.of("quantity", 1000); final SubDocumentUpdate propsUpdate = SubDocumentUpdate.of( - "props", SubDocumentValue.of(JSONDocument.fromJson("{\"brand\": \"Dettol\"}"))); + "props", SubDocumentValue.of(new JSONDocument("{\"brand\": \"Dettol\"}"))); return List.of(dateUpdate, quantityUpdate, propsUpdate); } diff --git a/document-store/src/test/java/org/hypertrace/core/documentstore/postgres/query/v1/PostgresQueryParserTest.java b/document-store/src/test/java/org/hypertrace/core/documentstore/postgres/query/v1/PostgresQueryParserTest.java index 7799e94f2..816d92112 100644 --- a/document-store/src/test/java/org/hypertrace/core/documentstore/postgres/query/v1/PostgresQueryParserTest.java +++ b/document-store/src/test/java/org/hypertrace/core/documentstore/postgres/query/v1/PostgresQueryParserTest.java @@ -1333,7 +1333,7 @@ void testContainsFilter() throws IOException { RelationalExpression.of( IdentifierExpression.of("sales"), CONTAINS, - ConstantExpression.of(JSONDocument.fromJson("\"a\"")))) + ConstantExpression.of(new JSONDocument("\"a\"")))) .build(); PostgresQueryParser postgresQueryParser = new PostgresQueryParser(TEST_TABLE, PostgresQueryTransformer.transform(query)); @@ -1359,7 +1359,7 @@ void testContainsAndUnnestFilters() throws IOException { IdentifierExpression.of("sales.medium"), CONTAINS, ConstantExpression.of( - JSONDocument.fromJson("{\"type\": \"retail\",\"volume\": 500}")))) + new JSONDocument("{\"type\": \"retail\",\"volume\": 500}")))) .build()) .build(); @@ -1394,7 +1394,7 @@ void testNotContainsAndUnnestFilters() throws IOException { IdentifierExpression.of("sales.medium"), NOT_CONTAINS, ConstantExpression.of( - JSONDocument.fromJson("{\"type\": \"retail\",\"volume\": 500}")))) + new JSONDocument("{\"type\": \"retail\",\"volume\": 500}")))) .build()) .build(); diff --git a/document-store/src/test/java/org/hypertrace/core/documentstore/util/TestUtil.java b/document-store/src/test/java/org/hypertrace/core/documentstore/util/TestUtil.java index 24bfee676..7d0006192 100644 --- a/document-store/src/test/java/org/hypertrace/core/documentstore/util/TestUtil.java +++ b/document-store/src/test/java/org/hypertrace/core/documentstore/util/TestUtil.java @@ -33,7 +33,7 @@ public static BasicDBObject readBasicDBObject(final String filePath) throws IOEx } public static Document readDocument(final String filePath) throws IOException { - return JSONDocument.fromJson(readFileFromResource(filePath).orElseThrow()); + return new JSONDocument(readFileFromResource(filePath).orElseThrow()); } @SuppressWarnings("unchecked") From 63b0dece1d78d8d2e17da9f643f726c58ee72f51 Mon Sep 17 00:00:00 2001 From: Prashant Pandey Date: Mon, 22 Sep 2025 23:12:27 +0530 Subject: [PATCH 11/19] Added default DocumentType in Document.java --- .../main/java/org/hypertrace/core/documentstore/Document.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/document-store/src/main/java/org/hypertrace/core/documentstore/Document.java b/document-store/src/main/java/org/hypertrace/core/documentstore/Document.java index 5e48e4011..466e77ab2 100644 --- a/document-store/src/main/java/org/hypertrace/core/documentstore/Document.java +++ b/document-store/src/main/java/org/hypertrace/core/documentstore/Document.java @@ -5,6 +5,6 @@ public interface Document { String toJson(); default DocumentType getDocumentType() { - return null; + return DocumentType.DOCUMENT_STORE; } } From 7f91cf04f4045f453404a5e25bb92f30c1dfac7b Mon Sep 17 00:00:00 2001 From: Prashant Pandey Date: Wed, 24 Sep 2025 01:44:21 +0530 Subject: [PATCH 12/19] Address comments --- .../org/hypertrace/core/documentstore/Document.java | 2 +- .../hypertrace/core/documentstore/DocumentType.java | 7 +++++-- .../hypertrace/core/documentstore/JSONDocument.java | 2 +- .../core/documentstore/mongo/MongoUtils.java | 6 ++---- .../documentstore/postgres/PostgresCollection.java | 10 +++++----- 5 files changed, 14 insertions(+), 13 deletions(-) diff --git a/document-store/src/main/java/org/hypertrace/core/documentstore/Document.java b/document-store/src/main/java/org/hypertrace/core/documentstore/Document.java index 466e77ab2..fe9c84b85 100644 --- a/document-store/src/main/java/org/hypertrace/core/documentstore/Document.java +++ b/document-store/src/main/java/org/hypertrace/core/documentstore/Document.java @@ -5,6 +5,6 @@ public interface Document { String toJson(); default DocumentType getDocumentType() { - return DocumentType.DOCUMENT_STORE; + return DocumentType.NESTED; } } diff --git a/document-store/src/main/java/org/hypertrace/core/documentstore/DocumentType.java b/document-store/src/main/java/org/hypertrace/core/documentstore/DocumentType.java index 745505ef2..932eb1de1 100644 --- a/document-store/src/main/java/org/hypertrace/core/documentstore/DocumentType.java +++ b/document-store/src/main/java/org/hypertrace/core/documentstore/DocumentType.java @@ -1,6 +1,9 @@ package org.hypertrace.core.documentstore; public enum DocumentType { - SQL_STORE, - DOCUMENT_STORE + // FLAT documents don't contain columns/fields with type annotations like value, valueList, + // valueMap. They're plain JSON documents. + FLAT, + // NESTED documents contain columns/fields with the above type information. + NESTED } diff --git a/document-store/src/main/java/org/hypertrace/core/documentstore/JSONDocument.java b/document-store/src/main/java/org/hypertrace/core/documentstore/JSONDocument.java index 68470f7db..ce0ca968d 100644 --- a/document-store/src/main/java/org/hypertrace/core/documentstore/JSONDocument.java +++ b/document-store/src/main/java/org/hypertrace/core/documentstore/JSONDocument.java @@ -11,7 +11,7 @@ public class JSONDocument implements Document { private static ObjectMapper mapper = new ObjectMapper(); private JsonNode node; - private DocumentType documentType; + private DocumentType documentType = DocumentType.NESTED; public JSONDocument(String json) throws IOException { node = mapper.readTree(json); diff --git a/document-store/src/main/java/org/hypertrace/core/documentstore/mongo/MongoUtils.java b/document-store/src/main/java/org/hypertrace/core/documentstore/mongo/MongoUtils.java index 4598a22fc..f12868cff 100644 --- a/document-store/src/main/java/org/hypertrace/core/documentstore/mongo/MongoUtils.java +++ b/document-store/src/main/java/org/hypertrace/core/documentstore/mongo/MongoUtils.java @@ -29,12 +29,10 @@ import org.bson.json.JsonMode; import org.bson.json.JsonWriterSettings; import org.hypertrace.core.documentstore.Document; -import org.hypertrace.core.documentstore.DocumentType; import org.hypertrace.core.documentstore.JSONDocument; import org.hypertrace.core.documentstore.model.options.ReturnDocumentType; public final class MongoUtils { - public static final String FIELD_SEPARATOR = "."; public static final String PREFIX = "$"; private static final String LITERAL = PREFIX + "literal"; @@ -139,10 +137,10 @@ public static Document dbObjectToDocument(BasicDBObject dbObject) { jsonString = dbObject.toJson(relaxed); JsonNode jsonNode = MAPPER.readTree(jsonString); JsonNode decodedJsonNode = recursiveClone(jsonNode, MongoUtils::decodeKey, identity()); - return new JSONDocument(decodedJsonNode, DocumentType.DOCUMENT_STORE); + return new JSONDocument(decodedJsonNode); } catch (IOException e) { // throwing exception is not very useful here. - return JSONDocument.errorDocument(e.getMessage(), DocumentType.DOCUMENT_STORE); + return JSONDocument.errorDocument(e.getMessage()); } } 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 5e496e08e..725900cf6 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 @@ -1277,7 +1277,7 @@ public Document next() { } catch (IOException | SQLException e) { System.out.println("prepare document failed!"); closeResultSet(); - return JSONDocument.errorDocument(e.getMessage(), DocumentType.SQL_STORE); + return JSONDocument.errorDocument(e.getMessage(), DocumentType.FLAT); } } @@ -1300,7 +1300,7 @@ protected Document prepareDocument() throws SQLException, IOException { jsonNode.remove(DOCUMENT_ID); } - return new JSONDocument(MAPPER.writeValueAsString(jsonNode), DocumentType.SQL_STORE); + return new JSONDocument(MAPPER.writeValueAsString(jsonNode), DocumentType.FLAT); } private void addColumnToJsonNode( @@ -1431,7 +1431,7 @@ public Document next() { return prepareDocument(); } catch (IOException | SQLException e) { closeResultSet(); - return JSONDocument.errorDocument(e.getMessage(), DocumentType.SQL_STORE); + return JSONDocument.errorDocument(e.getMessage(), DocumentType.FLAT); } } @@ -1448,7 +1448,7 @@ protected Document prepareDocument() throws SQLException, IOException { jsonNode.put(CREATED_AT, String.valueOf(createdAt)); jsonNode.put(UPDATED_AT, String.valueOf(updatedAt)); - return new JSONDocument(MAPPER.writeValueAsString(jsonNode), DocumentType.SQL_STORE); + return new JSONDocument(MAPPER.writeValueAsString(jsonNode), DocumentType.FLAT); } protected void closeResultSet() { @@ -1509,7 +1509,7 @@ protected Document prepareDocument() throws SQLException, IOException { } } } - return new JSONDocument(MAPPER.writeValueAsString(jsonNode), DocumentType.SQL_STORE); + return new JSONDocument(MAPPER.writeValueAsString(jsonNode), DocumentType.FLAT); } private String getColumnValue( From ea2aefd4a516885f64f198debba8aabbd3f06491 Mon Sep 17 00:00:00 2001 From: Prashant Pandey Date: Wed, 24 Sep 2025 12:13:49 +0530 Subject: [PATCH 13/19] Address comments --- .../core/documentstore/JSONDocument.java | 6 ---- .../postgres/PostgresCollection.java | 28 +++++++++++++++---- .../postgres/PostgresQueryExecutor.java | 5 +++- 3 files changed, 26 insertions(+), 13 deletions(-) diff --git a/document-store/src/main/java/org/hypertrace/core/documentstore/JSONDocument.java b/document-store/src/main/java/org/hypertrace/core/documentstore/JSONDocument.java index ce0ca968d..db0866d9c 100644 --- a/document-store/src/main/java/org/hypertrace/core/documentstore/JSONDocument.java +++ b/document-store/src/main/java/org/hypertrace/core/documentstore/JSONDocument.java @@ -60,12 +60,6 @@ public static JSONDocument errorDocument(String message) { return new JSONDocument(objectNode); } - public static JSONDocument errorDocument(String message, DocumentType documentType) { - ObjectNode objectNode = mapper.createObjectNode(); - objectNode.put("errorMessage", message); - return new JSONDocument(objectNode, documentType); - } - @Override public String toString() { return toJson(); 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 725900cf6..8c6444be8 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 @@ -1265,6 +1265,10 @@ public PostgresResultIteratorWithBasicTypes(ResultSet resultSet) { super(resultSet); } + public PostgresResultIteratorWithBasicTypes(ResultSet resultSet, DocumentType documentType) { + super(resultSet, documentType); + } + @Override public Document next() { try { @@ -1277,7 +1281,7 @@ public Document next() { } catch (IOException | SQLException e) { System.out.println("prepare document failed!"); closeResultSet(); - return JSONDocument.errorDocument(e.getMessage(), DocumentType.FLAT); + return JSONDocument.errorDocument(e.getMessage()); } } @@ -1300,7 +1304,7 @@ protected Document prepareDocument() throws SQLException, IOException { jsonNode.remove(DOCUMENT_ID); } - return new JSONDocument(MAPPER.writeValueAsString(jsonNode), DocumentType.FLAT); + return new JSONDocument(MAPPER.writeValueAsString(jsonNode), documentType); } private void addColumnToJsonNode( @@ -1387,17 +1391,29 @@ static class PostgresResultIterator implements CloseableIterator { protected final ObjectMapper MAPPER = new ObjectMapper(); protected ResultSet resultSet; - private final boolean removeDocumentId; protected boolean cursorMovedForward = false; protected boolean hasNext = false; + private final boolean removeDocumentId; + protected DocumentType documentType; + public PostgresResultIterator(ResultSet resultSet) { this(resultSet, true); } PostgresResultIterator(ResultSet resultSet, boolean removeDocumentId) { + this(resultSet, removeDocumentId, DocumentType.NESTED); + } + + public PostgresResultIterator(ResultSet resultSet, DocumentType documentType) { + this(resultSet, true, documentType); + } + + PostgresResultIterator( + ResultSet resultSet, boolean removeDocumentId, DocumentType documentType) { this.resultSet = resultSet; this.removeDocumentId = removeDocumentId; + this.documentType = documentType; } @Override @@ -1431,7 +1447,7 @@ public Document next() { return prepareDocument(); } catch (IOException | SQLException e) { closeResultSet(); - return JSONDocument.errorDocument(e.getMessage(), DocumentType.FLAT); + return JSONDocument.errorDocument(e.getMessage()); } } @@ -1448,7 +1464,7 @@ protected Document prepareDocument() throws SQLException, IOException { jsonNode.put(CREATED_AT, String.valueOf(createdAt)); jsonNode.put(UPDATED_AT, String.valueOf(updatedAt)); - return new JSONDocument(MAPPER.writeValueAsString(jsonNode), DocumentType.FLAT); + return new JSONDocument(MAPPER.writeValueAsString(jsonNode), documentType); } protected void closeResultSet() { @@ -1509,7 +1525,7 @@ protected Document prepareDocument() throws SQLException, IOException { } } } - return new JSONDocument(MAPPER.writeValueAsString(jsonNode), DocumentType.FLAT); + return new JSONDocument(MAPPER.writeValueAsString(jsonNode), documentType); } private String getColumnValue( diff --git a/document-store/src/main/java/org/hypertrace/core/documentstore/postgres/PostgresQueryExecutor.java b/document-store/src/main/java/org/hypertrace/core/documentstore/postgres/PostgresQueryExecutor.java index 389a52b8c..33ea637c8 100644 --- a/document-store/src/main/java/org/hypertrace/core/documentstore/postgres/PostgresQueryExecutor.java +++ b/document-store/src/main/java/org/hypertrace/core/documentstore/postgres/PostgresQueryExecutor.java @@ -10,6 +10,7 @@ import lombok.extern.slf4j.Slf4j; import org.hypertrace.core.documentstore.CloseableIterator; import org.hypertrace.core.documentstore.Document; +import org.hypertrace.core.documentstore.DocumentType; import org.hypertrace.core.documentstore.postgres.PostgresCollection.PostgresResultIterator; import org.hypertrace.core.documentstore.postgres.PostgresCollection.PostgresResultIteratorWithMetaData; import org.hypertrace.core.documentstore.postgres.query.v1.transformer.PostgresQueryTransformer; @@ -18,6 +19,7 @@ @Slf4j @AllArgsConstructor public class PostgresQueryExecutor { + private final PostgresTableIdentifier tableIdentifier; public CloseableIterator execute(final Connection connection, final Query query) { @@ -37,7 +39,8 @@ public CloseableIterator execute( final ResultSet resultSet = preparedStatement.executeQuery(); if ((tableIdentifier.getTableName().equals(flatStructureCollectionName))) { - return new PostgresCollection.PostgresResultIteratorWithBasicTypes(resultSet); + return new PostgresCollection.PostgresResultIteratorWithBasicTypes( + resultSet, DocumentType.FLAT); } return query.getSelections().size() > 0 ? new PostgresResultIteratorWithMetaData(resultSet) From 3e3e6cf36c485ee7a8293d315a21d6b8552a4007 Mon Sep 17 00:00:00 2001 From: Prashant Pandey Date: Wed, 24 Sep 2025 12:15:39 +0530 Subject: [PATCH 14/19] Address docs --- .../java/org/hypertrace/core/documentstore/DocumentType.java | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/document-store/src/main/java/org/hypertrace/core/documentstore/DocumentType.java b/document-store/src/main/java/org/hypertrace/core/documentstore/DocumentType.java index 932eb1de1..948b38989 100644 --- a/document-store/src/main/java/org/hypertrace/core/documentstore/DocumentType.java +++ b/document-store/src/main/java/org/hypertrace/core/documentstore/DocumentType.java @@ -1,9 +1,8 @@ package org.hypertrace.core.documentstore; public enum DocumentType { - // FLAT documents don't contain columns/fields with type annotations like value, valueList, - // valueMap. They're plain JSON documents. + // FLAT documents contain individual columns for each attribute FLAT, - // NESTED documents contain columns/fields with the above type information. + // NESTED documents contains attributes as a nested JSON document NESTED } From e6a6f7e90e49400d309a1468983dc793d6848fca Mon Sep 17 00:00:00 2001 From: Prashant Pandey Date: Wed, 24 Sep 2025 14:03:17 +0530 Subject: [PATCH 15/19] Adds more test cases in JSONDocumentTest.java --- .../core/documentstore/JSONDocument.java | 2 +- .../core/documentstore/JSONDocumentTest.java | 91 +++++++++++++++++++ 2 files changed, 92 insertions(+), 1 deletion(-) diff --git a/document-store/src/main/java/org/hypertrace/core/documentstore/JSONDocument.java b/document-store/src/main/java/org/hypertrace/core/documentstore/JSONDocument.java index db0866d9c..85d23d706 100644 --- a/document-store/src/main/java/org/hypertrace/core/documentstore/JSONDocument.java +++ b/document-store/src/main/java/org/hypertrace/core/documentstore/JSONDocument.java @@ -75,6 +75,6 @@ public boolean equals(Object obj) { } JSONDocument other = (JSONDocument) obj; - return Objects.equals(node, other.node); + return Objects.equals(node, other.node) && documentType == other.documentType; } } diff --git a/document-store/src/test/java/org/hypertrace/core/documentstore/JSONDocumentTest.java b/document-store/src/test/java/org/hypertrace/core/documentstore/JSONDocumentTest.java index 5cd3be9ac..f31e47b93 100644 --- a/document-store/src/test/java/org/hypertrace/core/documentstore/JSONDocumentTest.java +++ b/document-store/src/test/java/org/hypertrace/core/documentstore/JSONDocumentTest.java @@ -1,11 +1,15 @@ package org.hypertrace.core.documentstore; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; import java.util.Map; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; public class JSONDocumentTest { + private static final ObjectMapper mapper = new ObjectMapper(); + @Test public void testJSONDocument() throws Exception { Map data = Map.of("key1", "value1", "key2", "value2"); @@ -14,4 +18,91 @@ public void testJSONDocument() throws Exception { Assertions.assertEquals(document1, document2); Assertions.assertEquals(document1.toJson(), document2.toJson()); } + + @Test + public void testJSONDocumentWithDefaultDocumentType() throws Exception { + Map data = Map.of("key1", "value1", "key2", "value2"); + JSONDocument document = new JSONDocument(data); + Assertions.assertEquals(DocumentType.NESTED, document.getDocumentType()); + } + + @Test + public void testJSONDocumentWithExplicitDocumentType() throws Exception { + Map data = Map.of("key1", "value1", "key2", "value2"); + + JSONDocument nestedDocument = new JSONDocument(data, DocumentType.NESTED); + Assertions.assertEquals(DocumentType.NESTED, nestedDocument.getDocumentType()); + + JSONDocument flatDocument = new JSONDocument(data, DocumentType.FLAT); + Assertions.assertEquals(DocumentType.FLAT, flatDocument.getDocumentType()); + } + + @Test + public void testJSONDocumentConstructorsWithDocumentType() throws Exception { + String json = "{\"key1\":\"value1\",\"key2\":\"value2\"}"; + JsonNode node = mapper.readTree(json); + Map data = Map.of("key1", "value1", "key2", "value2"); + + // Test string constructor with DocumentType + JSONDocument stringDoc = new JSONDocument(json, DocumentType.FLAT); + Assertions.assertEquals(DocumentType.FLAT, stringDoc.getDocumentType()); + + // Test object constructor with DocumentType + JSONDocument objectDoc = new JSONDocument(data, DocumentType.FLAT); + Assertions.assertEquals(DocumentType.FLAT, objectDoc.getDocumentType()); + + // Test JsonNode constructor with DocumentType + JSONDocument nodeDoc = new JSONDocument(node, DocumentType.FLAT); + Assertions.assertEquals(DocumentType.FLAT, nodeDoc.getDocumentType()); + } + + @Test + public void testEqualsWithSameContentDifferentDocumentType() throws Exception { + Map data = Map.of("key1", "value1", "key2", "value2"); + + JSONDocument nestedDoc = new JSONDocument(data, DocumentType.NESTED); + JSONDocument flatDoc = new JSONDocument(data, DocumentType.FLAT); + + // Current implementation only compares JsonNode, not DocumentType + // This test documents the current behavior - documents with same content but different types are equal + Assertions.assertNotEquals(nestedDoc, flatDoc); + Assertions.assertEquals(nestedDoc.toJson(), flatDoc.toJson()); + } + + @Test + public void testEqualsWithSameContentSameDocumentType() throws Exception { + Map data = Map.of("key1", "value1", "key2", "value2"); + + JSONDocument doc1 = new JSONDocument(data, DocumentType.FLAT); + JSONDocument doc2 = new JSONDocument(data, DocumentType.FLAT); + + Assertions.assertEquals(doc1, doc2); + Assertions.assertEquals(doc1.getDocumentType(), doc2.getDocumentType()); + } + + @Test + public void testErrorDocument() throws Exception { + String errorMessage = "Test error message"; + JSONDocument errorDoc = JSONDocument.errorDocument(errorMessage); + + // Verify default document type + Assertions.assertEquals(DocumentType.NESTED, errorDoc.getDocumentType()); + + // Verify error message is in the JSON + String expectedJson = "{\"errorMessage\":\"" + errorMessage + "\"}"; + Assertions.assertEquals(expectedJson, errorDoc.toJson()); + + // Test error document equality + JSONDocument anotherErrorDoc = JSONDocument.errorDocument(errorMessage); + Assertions.assertEquals(errorDoc, anotherErrorDoc); + } + + @Test + public void testToStringMethod() throws Exception { + Map data = Map.of("key1", "value1"); + JSONDocument document = new JSONDocument(data, DocumentType.FLAT); + + // toString should return the same as toJson + Assertions.assertEquals(document.toJson(), document.toString()); + } } From 483eb162651b1d205745f75e580f4f710d8943c9 Mon Sep 17 00:00:00 2001 From: Prashant Pandey Date: Wed, 24 Sep 2025 14:04:59 +0530 Subject: [PATCH 16/19] Spotless --- .../org/hypertrace/core/documentstore/JSONDocumentTest.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/document-store/src/test/java/org/hypertrace/core/documentstore/JSONDocumentTest.java b/document-store/src/test/java/org/hypertrace/core/documentstore/JSONDocumentTest.java index f31e47b93..e4ec510d7 100644 --- a/document-store/src/test/java/org/hypertrace/core/documentstore/JSONDocumentTest.java +++ b/document-store/src/test/java/org/hypertrace/core/documentstore/JSONDocumentTest.java @@ -64,7 +64,8 @@ public void testEqualsWithSameContentDifferentDocumentType() throws Exception { JSONDocument flatDoc = new JSONDocument(data, DocumentType.FLAT); // Current implementation only compares JsonNode, not DocumentType - // This test documents the current behavior - documents with same content but different types are equal + // This test documents the current behavior - documents with same content but different types + // are equal Assertions.assertNotEquals(nestedDoc, flatDoc); Assertions.assertEquals(nestedDoc.toJson(), flatDoc.toJson()); } From e5926eaa23b66f65b37d1877fe7e5ebcc1a581a2 Mon Sep 17 00:00:00 2001 From: Prashant Pandey Date: Wed, 24 Sep 2025 14:21:29 +0530 Subject: [PATCH 17/19] Added more test cases --- .../core/documentstore/JSONDocumentTest.java | 33 +++++++++++++++++++ .../postgres/PostgresCollectionTest.java | 2 ++ 2 files changed, 35 insertions(+) diff --git a/document-store/src/test/java/org/hypertrace/core/documentstore/JSONDocumentTest.java b/document-store/src/test/java/org/hypertrace/core/documentstore/JSONDocumentTest.java index e4ec510d7..b07633663 100644 --- a/document-store/src/test/java/org/hypertrace/core/documentstore/JSONDocumentTest.java +++ b/document-store/src/test/java/org/hypertrace/core/documentstore/JSONDocumentTest.java @@ -2,6 +2,7 @@ import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; +import java.io.IOException; import java.util.Map; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; @@ -106,4 +107,36 @@ public void testToStringMethod() throws Exception { // toString should return the same as toJson Assertions.assertEquals(document.toJson(), document.toString()); } + + @Test + public void testJsonNodeConstructors() throws Exception { + JsonNode node = mapper.createObjectNode().put("test", "value"); + + // Test JsonNode constructor without DocumentType + JSONDocument doc1 = new JSONDocument(node); + Assertions.assertEquals(DocumentType.NESTED, doc1.getDocumentType()); + + // Test JsonNode constructor with DocumentType + JSONDocument doc2 = new JSONDocument(node, DocumentType.FLAT); + Assertions.assertEquals(DocumentType.FLAT, doc2.getDocumentType()); + } + + @Test + public void testStringConstructorWithInvalidJson() { + // Test invalid JSON handling + Assertions.assertThrows( + IOException.class, + () -> new JSONDocument("invalid json {")); + + Assertions.assertThrows( + IOException.class, + () -> new JSONDocument("invalid json {", DocumentType.FLAT)); + } + + @Test + public void testNullHandling() throws Exception { + // Test null JsonNode + JSONDocument nullNodeDoc = new JSONDocument((JsonNode) null); + Assertions.assertNotNull(nullNodeDoc.toJson()); // Should handle gracefully + } } diff --git a/document-store/src/test/java/org/hypertrace/core/documentstore/postgres/PostgresCollectionTest.java b/document-store/src/test/java/org/hypertrace/core/documentstore/postgres/PostgresCollectionTest.java index e94f6009b..d9a744f63 100644 --- a/document-store/src/test/java/org/hypertrace/core/documentstore/postgres/PostgresCollectionTest.java +++ b/document-store/src/test/java/org/hypertrace/core/documentstore/postgres/PostgresCollectionTest.java @@ -36,6 +36,7 @@ import java.util.UUID; import org.hypertrace.core.documentstore.CloseableIterator; import org.hypertrace.core.documentstore.Document; +import org.hypertrace.core.documentstore.DocumentType; import org.hypertrace.core.documentstore.Filter; import org.hypertrace.core.documentstore.JSONDocument; import org.hypertrace.core.documentstore.Key; @@ -186,6 +187,7 @@ void testUpdateAtomicWithFilter() throws IOException, SQLException { assertTrue(oldDocument.isPresent()); assertEquals(document, oldDocument.get()); + assertEquals(DocumentType.NESTED, document.getDocumentType()); verify(mockClient, times(1)).getPooledConnection(); verify(mockConnection, times(1)).prepareStatement(selectQuery); From b2614636b4ba77c7120bdf8e4bc1be0498bf1068 Mon Sep 17 00:00:00 2001 From: Prashant Pandey Date: Wed, 24 Sep 2025 14:23:12 +0530 Subject: [PATCH 18/19] Spotless --- .../hypertrace/core/documentstore/JSONDocumentTest.java | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/document-store/src/test/java/org/hypertrace/core/documentstore/JSONDocumentTest.java b/document-store/src/test/java/org/hypertrace/core/documentstore/JSONDocumentTest.java index b07633663..9d26439d6 100644 --- a/document-store/src/test/java/org/hypertrace/core/documentstore/JSONDocumentTest.java +++ b/document-store/src/test/java/org/hypertrace/core/documentstore/JSONDocumentTest.java @@ -124,13 +124,10 @@ public void testJsonNodeConstructors() throws Exception { @Test public void testStringConstructorWithInvalidJson() { // Test invalid JSON handling - Assertions.assertThrows( - IOException.class, - () -> new JSONDocument("invalid json {")); + Assertions.assertThrows(IOException.class, () -> new JSONDocument("invalid json {")); Assertions.assertThrows( - IOException.class, - () -> new JSONDocument("invalid json {", DocumentType.FLAT)); + IOException.class, () -> new JSONDocument("invalid json {", DocumentType.FLAT)); } @Test From 7cf8e17cc644c6f0c03a1b955586d04b443ea41d Mon Sep 17 00:00:00 2001 From: Prashant Pandey Date: Wed, 24 Sep 2025 15:10:09 +0530 Subject: [PATCH 19/19] Add test case for PostgresResultIteratorWithBasicTypes --- .../core/documentstore/JSONDocumentTest.java | 30 ----- .../postgres/PostgresCollectionTest.java | 123 ++++++++++++++++++ 2 files changed, 123 insertions(+), 30 deletions(-) diff --git a/document-store/src/test/java/org/hypertrace/core/documentstore/JSONDocumentTest.java b/document-store/src/test/java/org/hypertrace/core/documentstore/JSONDocumentTest.java index 9d26439d6..e4ec510d7 100644 --- a/document-store/src/test/java/org/hypertrace/core/documentstore/JSONDocumentTest.java +++ b/document-store/src/test/java/org/hypertrace/core/documentstore/JSONDocumentTest.java @@ -2,7 +2,6 @@ import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; -import java.io.IOException; import java.util.Map; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; @@ -107,33 +106,4 @@ public void testToStringMethod() throws Exception { // toString should return the same as toJson Assertions.assertEquals(document.toJson(), document.toString()); } - - @Test - public void testJsonNodeConstructors() throws Exception { - JsonNode node = mapper.createObjectNode().put("test", "value"); - - // Test JsonNode constructor without DocumentType - JSONDocument doc1 = new JSONDocument(node); - Assertions.assertEquals(DocumentType.NESTED, doc1.getDocumentType()); - - // Test JsonNode constructor with DocumentType - JSONDocument doc2 = new JSONDocument(node, DocumentType.FLAT); - Assertions.assertEquals(DocumentType.FLAT, doc2.getDocumentType()); - } - - @Test - public void testStringConstructorWithInvalidJson() { - // Test invalid JSON handling - Assertions.assertThrows(IOException.class, () -> new JSONDocument("invalid json {")); - - Assertions.assertThrows( - IOException.class, () -> new JSONDocument("invalid json {", DocumentType.FLAT)); - } - - @Test - public void testNullHandling() throws Exception { - // Test null JsonNode - JSONDocument nullNodeDoc = new JSONDocument((JsonNode) null); - Assertions.assertNotNull(nullNodeDoc.toJson()); // Should handle gracefully - } } diff --git a/document-store/src/test/java/org/hypertrace/core/documentstore/postgres/PostgresCollectionTest.java b/document-store/src/test/java/org/hypertrace/core/documentstore/postgres/PostgresCollectionTest.java index d9a744f63..fb5a71b01 100644 --- a/document-store/src/test/java/org/hypertrace/core/documentstore/postgres/PostgresCollectionTest.java +++ b/document-store/src/test/java/org/hypertrace/core/documentstore/postgres/PostgresCollectionTest.java @@ -3,6 +3,7 @@ import static java.sql.Types.INTEGER; import static java.sql.Types.VARCHAR; import static java.util.Collections.emptyList; +import static org.bson.assertions.Assertions.assertNotNull; import static org.hypertrace.core.documentstore.expression.operators.LogicalOperator.AND; import static org.hypertrace.core.documentstore.expression.operators.RelationalOperator.EQ; import static org.hypertrace.core.documentstore.expression.operators.RelationalOperator.LT; @@ -25,6 +26,7 @@ import static org.mockito.internal.verification.VerificationModeFactory.times; import java.io.IOException; +import java.sql.Array; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; @@ -939,6 +941,127 @@ private void mockResultSetMetadata(final String id) throws SQLException { when(mockResultSet.getString(5)).thenReturn(id); } + @Test + void testPostgresResultIteratorWithBasicTypesUsage() throws SQLException, IOException { + // Test that PostgresResultIteratorWithBasicTypes can handle various column types + when(mockResultSet.next()).thenReturn(true, false); + when(mockResultSet.getMetaData()).thenReturn(mockResultSetMetaData); + when(mockResultSetMetaData.getColumnCount()).thenReturn(6); + + // Setup different column types to test the addColumnToJsonNode method + when(mockResultSetMetaData.getColumnName(1)).thenReturn("boolean_field"); + when(mockResultSetMetaData.getColumnTypeName(1)).thenReturn("bool"); + when(mockResultSet.getBoolean(1)).thenReturn(true); + when(mockResultSet.wasNull()).thenReturn(false); + + when(mockResultSetMetaData.getColumnName(2)).thenReturn("integer_field"); + when(mockResultSetMetaData.getColumnTypeName(2)).thenReturn("int4"); + when(mockResultSet.getInt(2)).thenReturn(42); + + when(mockResultSetMetaData.getColumnName(3)).thenReturn("bigint_field"); + when(mockResultSetMetaData.getColumnTypeName(3)).thenReturn("int8"); + when(mockResultSet.getLong(3)).thenReturn(123456789L); + + when(mockResultSetMetaData.getColumnName(4)).thenReturn("double_field"); + when(mockResultSetMetaData.getColumnTypeName(4)).thenReturn("float8"); + when(mockResultSet.getDouble(4)).thenReturn(3.14159); + + when(mockResultSetMetaData.getColumnName(5)).thenReturn("text_field"); + when(mockResultSetMetaData.getColumnTypeName(5)).thenReturn("text"); + when(mockResultSet.getString(5)).thenReturn("sample text"); + + when(mockResultSetMetaData.getColumnName(6)).thenReturn("jsonb_field"); + when(mockResultSetMetaData.getColumnTypeName(6)).thenReturn("jsonb"); + when(mockResultSet.getString(6)).thenReturn("{\"nested\":\"value\"}"); + + // Create and test the iterator directly + PostgresCollection.PostgresResultIteratorWithBasicTypes iterator = + new PostgresCollection.PostgresResultIteratorWithBasicTypes( + mockResultSet, DocumentType.FLAT); + + assertTrue(iterator.hasNext()); + Document result = iterator.next(); + + assertNotNull(result); + assertEquals(DocumentType.FLAT, result.getDocumentType()); + + String json = result.toJson(); + assertTrue(json.contains("\"boolean_field\":true")); + assertTrue(json.contains("\"integer_field\":42")); + assertTrue(json.contains("\"bigint_field\":123456789")); + assertTrue(json.contains("\"double_field\":3.14159")); + assertTrue(json.contains("\"text_field\":\"sample text\"")); + assertTrue(json.contains("\"jsonb_field\":{\"nested\":\"value\"}")); + + assertFalse(iterator.hasNext()); + iterator.close(); + } + + @Test + void testPostgresResultIteratorWithBasicTypesNullHandling() throws SQLException, IOException { + // Test null value handling in PostgresResultIteratorWithBasicTypes + when(mockResultSet.next()).thenReturn(true, false); + when(mockResultSet.getMetaData()).thenReturn(mockResultSetMetaData); + when(mockResultSetMetaData.getColumnCount()).thenReturn(3); + + when(mockResultSetMetaData.getColumnName(1)).thenReturn("nullable_int"); + when(mockResultSetMetaData.getColumnTypeName(1)).thenReturn("int4"); + when(mockResultSet.getInt(1)).thenReturn(0); + when(mockResultSet.wasNull()).thenReturn(true); + + when(mockResultSetMetaData.getColumnName(2)).thenReturn("nullable_text"); + when(mockResultSetMetaData.getColumnTypeName(2)).thenReturn("text"); + when(mockResultSet.getString(2)).thenReturn(null); + + when(mockResultSetMetaData.getColumnName(3)).thenReturn("valid_text"); + when(mockResultSetMetaData.getColumnTypeName(3)).thenReturn("text"); + when(mockResultSet.getString(3)).thenReturn("not null"); + + PostgresCollection.PostgresResultIteratorWithBasicTypes iterator = + new PostgresCollection.PostgresResultIteratorWithBasicTypes(mockResultSet); + + assertTrue(iterator.hasNext()); + Document result = iterator.next(); + + assertNotNull(result); + String json = result.toJson(); + + // Null values should not appear in the JSON + assertFalse(json.contains("nullable_int")); + assertFalse(json.contains("nullable_text")); + assertTrue(json.contains("\"valid_text\":\"not null\"")); + + iterator.close(); + } + + @Test + void testPostgresResultIteratorWithBasicTypesArrayColumn() throws SQLException, IOException { + // Test array column handling + when(mockResultSet.next()).thenReturn(true, false); + when(mockResultSet.getMetaData()).thenReturn(mockResultSetMetaData); + when(mockResultSetMetaData.getColumnCount()).thenReturn(1); + + when(mockResultSetMetaData.getColumnName(1)).thenReturn("array_field"); + when(mockResultSetMetaData.getColumnTypeName(1)).thenReturn("_text"); + + Array mockArray = mock(Array.class); + String[] arrayData = {"item1", "item2", "item3"}; + when(mockResultSet.getArray(1)).thenReturn(mockArray); + when(mockArray.getArray()).thenReturn(arrayData); + + PostgresCollection.PostgresResultIteratorWithBasicTypes iterator = + new PostgresCollection.PostgresResultIteratorWithBasicTypes(mockResultSet); + + assertTrue(iterator.hasNext()); + Document result = iterator.next(); + + assertNotNull(result); + String json = result.toJson(); + assertTrue(json.contains("\"array_field\":[\"item1\",\"item2\",\"item3\"]")); + + iterator.close(); + } + private void mockResultSetMetadata() throws SQLException { when(mockResultSetMetaData.getColumnName(1)).thenReturn("quantity"); when(mockResultSetMetaData.getColumnType(1)).thenReturn(INTEGER);