Skip to content

Commit 1f1876c

Browse files
authored
Make JsonDocument Underlying Storage Type Aware (Document store / SQL store) (#233)
1 parent 107cda0 commit 1f1876c

File tree

7 files changed

+276
-6
lines changed

7 files changed

+276
-6
lines changed

document-store/src/main/java/org/hypertrace/core/documentstore/Document.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,4 +3,8 @@
33
public interface Document {
44

55
String toJson();
6+
7+
default DocumentType getDocumentType() {
8+
return DocumentType.NESTED;
9+
}
610
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
package org.hypertrace.core.documentstore;
2+
3+
public enum DocumentType {
4+
// FLAT documents contain individual columns for each attribute
5+
FLAT,
6+
// NESTED documents contains attributes as a nested JSON document
7+
NESTED
8+
}

document-store/src/main/java/org/hypertrace/core/documentstore/JSONDocument.java

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,19 +11,40 @@ public class JSONDocument implements Document {
1111

1212
private static ObjectMapper mapper = new ObjectMapper();
1313
private JsonNode node;
14+
private DocumentType documentType = DocumentType.NESTED;
1415

1516
public JSONDocument(String json) throws IOException {
1617
node = mapper.readTree(json);
1718
}
1819

20+
public JSONDocument(String json, DocumentType documentType) throws IOException {
21+
node = mapper.readTree(json);
22+
this.documentType = documentType;
23+
}
24+
1925
public JSONDocument(Object object) throws IOException {
2026
node = mapper.readTree(mapper.writeValueAsString(object));
2127
}
2228

29+
public JSONDocument(Object object, DocumentType documentType) throws IOException {
30+
node = mapper.readTree(mapper.writeValueAsString(object));
31+
this.documentType = documentType;
32+
}
33+
2334
public JSONDocument(JsonNode node) {
2435
this.node = node;
2536
}
2637

38+
public JSONDocument(JsonNode node, DocumentType documentType) {
39+
this.node = node;
40+
this.documentType = documentType;
41+
}
42+
43+
@Override
44+
public DocumentType getDocumentType() {
45+
return this.documentType;
46+
}
47+
2748
@Override
2849
public String toJson() {
2950
try {
@@ -54,6 +75,6 @@ public boolean equals(Object obj) {
5475
}
5576

5677
JSONDocument other = (JSONDocument) obj;
57-
return Objects.equals(node, other.node);
78+
return Objects.equals(node, other.node) && documentType == other.documentType;
5879
}
5980
}

document-store/src/main/java/org/hypertrace/core/documentstore/postgres/PostgresCollection.java

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@
6161
import org.hypertrace.core.documentstore.Collection;
6262
import org.hypertrace.core.documentstore.CreateResult;
6363
import org.hypertrace.core.documentstore.Document;
64+
import org.hypertrace.core.documentstore.DocumentType;
6465
import org.hypertrace.core.documentstore.Filter;
6566
import org.hypertrace.core.documentstore.JSONDocument;
6667
import org.hypertrace.core.documentstore.Key;
@@ -1264,6 +1265,10 @@ public PostgresResultIteratorWithBasicTypes(ResultSet resultSet) {
12641265
super(resultSet);
12651266
}
12661267

1268+
public PostgresResultIteratorWithBasicTypes(ResultSet resultSet, DocumentType documentType) {
1269+
super(resultSet, documentType);
1270+
}
1271+
12671272
@Override
12681273
public Document next() {
12691274
try {
@@ -1299,7 +1304,7 @@ protected Document prepareDocument() throws SQLException, IOException {
12991304
jsonNode.remove(DOCUMENT_ID);
13001305
}
13011306

1302-
return new JSONDocument(MAPPER.writeValueAsString(jsonNode));
1307+
return new JSONDocument(MAPPER.writeValueAsString(jsonNode), documentType);
13031308
}
13041309

13051310
private void addColumnToJsonNode(
@@ -1386,17 +1391,29 @@ static class PostgresResultIterator implements CloseableIterator<Document> {
13861391

13871392
protected final ObjectMapper MAPPER = new ObjectMapper();
13881393
protected ResultSet resultSet;
1389-
private final boolean removeDocumentId;
13901394
protected boolean cursorMovedForward = false;
13911395
protected boolean hasNext = false;
13921396

1397+
private final boolean removeDocumentId;
1398+
protected DocumentType documentType;
1399+
13931400
public PostgresResultIterator(ResultSet resultSet) {
13941401
this(resultSet, true);
13951402
}
13961403

13971404
PostgresResultIterator(ResultSet resultSet, boolean removeDocumentId) {
1405+
this(resultSet, removeDocumentId, DocumentType.NESTED);
1406+
}
1407+
1408+
public PostgresResultIterator(ResultSet resultSet, DocumentType documentType) {
1409+
this(resultSet, true, documentType);
1410+
}
1411+
1412+
PostgresResultIterator(
1413+
ResultSet resultSet, boolean removeDocumentId, DocumentType documentType) {
13981414
this.resultSet = resultSet;
13991415
this.removeDocumentId = removeDocumentId;
1416+
this.documentType = documentType;
14001417
}
14011418

14021419
@Override
@@ -1447,7 +1464,7 @@ protected Document prepareDocument() throws SQLException, IOException {
14471464
jsonNode.put(CREATED_AT, String.valueOf(createdAt));
14481465
jsonNode.put(UPDATED_AT, String.valueOf(updatedAt));
14491466

1450-
return new JSONDocument(MAPPER.writeValueAsString(jsonNode));
1467+
return new JSONDocument(MAPPER.writeValueAsString(jsonNode), documentType);
14511468
}
14521469

14531470
protected void closeResultSet() {
@@ -1508,7 +1525,7 @@ protected Document prepareDocument() throws SQLException, IOException {
15081525
}
15091526
}
15101527
}
1511-
return new JSONDocument(MAPPER.writeValueAsString(jsonNode));
1528+
return new JSONDocument(MAPPER.writeValueAsString(jsonNode), documentType);
15121529
}
15131530

15141531
private String getColumnValue(

document-store/src/main/java/org/hypertrace/core/documentstore/postgres/PostgresQueryExecutor.java

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
import lombok.extern.slf4j.Slf4j;
1111
import org.hypertrace.core.documentstore.CloseableIterator;
1212
import org.hypertrace.core.documentstore.Document;
13+
import org.hypertrace.core.documentstore.DocumentType;
1314
import org.hypertrace.core.documentstore.postgres.PostgresCollection.PostgresResultIterator;
1415
import org.hypertrace.core.documentstore.postgres.PostgresCollection.PostgresResultIteratorWithMetaData;
1516
import org.hypertrace.core.documentstore.postgres.query.v1.transformer.PostgresQueryTransformer;
@@ -18,6 +19,7 @@
1819
@Slf4j
1920
@AllArgsConstructor
2021
public class PostgresQueryExecutor {
22+
2123
private final PostgresTableIdentifier tableIdentifier;
2224

2325
public CloseableIterator<Document> execute(final Connection connection, final Query query) {
@@ -37,7 +39,8 @@ public CloseableIterator<Document> execute(
3739
final ResultSet resultSet = preparedStatement.executeQuery();
3840

3941
if ((tableIdentifier.getTableName().equals(flatStructureCollectionName))) {
40-
return new PostgresCollection.PostgresResultIteratorWithBasicTypes(resultSet);
42+
return new PostgresCollection.PostgresResultIteratorWithBasicTypes(
43+
resultSet, DocumentType.FLAT);
4144
}
4245
return query.getSelections().size() > 0
4346
? new PostgresResultIteratorWithMetaData(resultSet)

document-store/src/test/java/org/hypertrace/core/documentstore/JSONDocumentTest.java

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,15 @@
11
package org.hypertrace.core.documentstore;
22

3+
import com.fasterxml.jackson.databind.JsonNode;
4+
import com.fasterxml.jackson.databind.ObjectMapper;
35
import java.util.Map;
46
import org.junit.jupiter.api.Assertions;
57
import org.junit.jupiter.api.Test;
68

79
public class JSONDocumentTest {
810

11+
private static final ObjectMapper mapper = new ObjectMapper();
12+
913
@Test
1014
public void testJSONDocument() throws Exception {
1115
Map<String, String> data = Map.of("key1", "value1", "key2", "value2");
@@ -14,4 +18,92 @@ public void testJSONDocument() throws Exception {
1418
Assertions.assertEquals(document1, document2);
1519
Assertions.assertEquals(document1.toJson(), document2.toJson());
1620
}
21+
22+
@Test
23+
public void testJSONDocumentWithDefaultDocumentType() throws Exception {
24+
Map<String, String> data = Map.of("key1", "value1", "key2", "value2");
25+
JSONDocument document = new JSONDocument(data);
26+
Assertions.assertEquals(DocumentType.NESTED, document.getDocumentType());
27+
}
28+
29+
@Test
30+
public void testJSONDocumentWithExplicitDocumentType() throws Exception {
31+
Map<String, String> data = Map.of("key1", "value1", "key2", "value2");
32+
33+
JSONDocument nestedDocument = new JSONDocument(data, DocumentType.NESTED);
34+
Assertions.assertEquals(DocumentType.NESTED, nestedDocument.getDocumentType());
35+
36+
JSONDocument flatDocument = new JSONDocument(data, DocumentType.FLAT);
37+
Assertions.assertEquals(DocumentType.FLAT, flatDocument.getDocumentType());
38+
}
39+
40+
@Test
41+
public void testJSONDocumentConstructorsWithDocumentType() throws Exception {
42+
String json = "{\"key1\":\"value1\",\"key2\":\"value2\"}";
43+
JsonNode node = mapper.readTree(json);
44+
Map<String, String> data = Map.of("key1", "value1", "key2", "value2");
45+
46+
// Test string constructor with DocumentType
47+
JSONDocument stringDoc = new JSONDocument(json, DocumentType.FLAT);
48+
Assertions.assertEquals(DocumentType.FLAT, stringDoc.getDocumentType());
49+
50+
// Test object constructor with DocumentType
51+
JSONDocument objectDoc = new JSONDocument(data, DocumentType.FLAT);
52+
Assertions.assertEquals(DocumentType.FLAT, objectDoc.getDocumentType());
53+
54+
// Test JsonNode constructor with DocumentType
55+
JSONDocument nodeDoc = new JSONDocument(node, DocumentType.FLAT);
56+
Assertions.assertEquals(DocumentType.FLAT, nodeDoc.getDocumentType());
57+
}
58+
59+
@Test
60+
public void testEqualsWithSameContentDifferentDocumentType() throws Exception {
61+
Map<String, String> data = Map.of("key1", "value1", "key2", "value2");
62+
63+
JSONDocument nestedDoc = new JSONDocument(data, DocumentType.NESTED);
64+
JSONDocument flatDoc = new JSONDocument(data, DocumentType.FLAT);
65+
66+
// Current implementation only compares JsonNode, not DocumentType
67+
// This test documents the current behavior - documents with same content but different types
68+
// are equal
69+
Assertions.assertNotEquals(nestedDoc, flatDoc);
70+
Assertions.assertEquals(nestedDoc.toJson(), flatDoc.toJson());
71+
}
72+
73+
@Test
74+
public void testEqualsWithSameContentSameDocumentType() throws Exception {
75+
Map<String, String> data = Map.of("key1", "value1", "key2", "value2");
76+
77+
JSONDocument doc1 = new JSONDocument(data, DocumentType.FLAT);
78+
JSONDocument doc2 = new JSONDocument(data, DocumentType.FLAT);
79+
80+
Assertions.assertEquals(doc1, doc2);
81+
Assertions.assertEquals(doc1.getDocumentType(), doc2.getDocumentType());
82+
}
83+
84+
@Test
85+
public void testErrorDocument() throws Exception {
86+
String errorMessage = "Test error message";
87+
JSONDocument errorDoc = JSONDocument.errorDocument(errorMessage);
88+
89+
// Verify default document type
90+
Assertions.assertEquals(DocumentType.NESTED, errorDoc.getDocumentType());
91+
92+
// Verify error message is in the JSON
93+
String expectedJson = "{\"errorMessage\":\"" + errorMessage + "\"}";
94+
Assertions.assertEquals(expectedJson, errorDoc.toJson());
95+
96+
// Test error document equality
97+
JSONDocument anotherErrorDoc = JSONDocument.errorDocument(errorMessage);
98+
Assertions.assertEquals(errorDoc, anotherErrorDoc);
99+
}
100+
101+
@Test
102+
public void testToStringMethod() throws Exception {
103+
Map<String, String> data = Map.of("key1", "value1");
104+
JSONDocument document = new JSONDocument(data, DocumentType.FLAT);
105+
106+
// toString should return the same as toJson
107+
Assertions.assertEquals(document.toJson(), document.toString());
108+
}
17109
}

0 commit comments

Comments
 (0)