Skip to content

Commit 9501fde

Browse files
Handle empty object setting via createOrReplace method (#176)
* Handle empty object setting via createOrReplace method
1 parent 0cb34a7 commit 9501fde

File tree

6 files changed

+30
-15
lines changed

6 files changed

+30
-15
lines changed

document-store/src/integrationTest/resources/create/document_one.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,8 @@
88
{
99
"name": "Mars",
1010
"color": "Red",
11-
"humans_live": "possibly in future"
11+
"humans_live": "possibly in future",
12+
"tags": {}
1213
}
1314
]
1415
}

document-store/src/integrationTest/resources/create/document_one_response.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,8 @@
99
{
1010
"name": "Mars",
1111
"color": "Red",
12-
"humans_live": "possibly in future"
12+
"humans_live": "possibly in future",
13+
"tags": {}
1314
}
1415
]
1516
}

document-store/src/integrationTest/resources/create/document_two.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,5 +2,6 @@
22
"planet_name": "Mars",
33
"planet_color": "Red",
44
"humans_live": true,
5-
"settlement_since": 2099
5+
"settlement_since": 2099,
6+
"tags": {}
67
}

document-store/src/integrationTest/resources/create/document_two_response.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
"planet_name": "Mars",
44
"planet_color": "Red",
55
"humans_live": true,
6-
"settlement_since": 2099
6+
"settlement_since": 2099,
7+
"tags": {}
78
}
89
]

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

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -432,11 +432,7 @@ public BulkUpdateResult bulkOperationOnArrayValue(BulkArrayValueUpdateRequest re
432432
private BasicDBObject getSubDocumentUpdateObject(
433433
final String subDocPath, final Document subDocument) {
434434
try {
435-
/* Wrapping the subDocument with $literal to be able to provide empty object "{}" as value
436-
* Throws error otherwise if empty object is provided as value.
437-
* https://jira.mongodb.org/browse/SERVER-54046 */
438-
BasicDBObject literalObject =
439-
new BasicDBObject("$literal", getSanitizedBasicDBObject(subDocument));
435+
BasicDBObject literalObject = getSanitizedBasicDBObject(subDocument);
440436
BasicDBObject dbObject = new BasicDBObject(subDocPath, literalObject);
441437
dbObject.append(LAST_UPDATED_TIME, System.currentTimeMillis());
442438
return new BasicDBObject("$set", dbObject);

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

Lines changed: 21 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@
2424
import java.util.Map.Entry;
2525
import java.util.Optional;
2626
import java.util.Set;
27-
import java.util.function.Function;
27+
import java.util.function.UnaryOperator;
2828
import org.bson.json.JsonMode;
2929
import org.bson.json.JsonWriterSettings;
3030
import org.hypertrace.core.documentstore.Document;
@@ -34,6 +34,7 @@
3434
public final class MongoUtils {
3535
public static final String FIELD_SEPARATOR = ".";
3636
public static final String PREFIX = "$";
37+
private static final String LITERAL = PREFIX + "literal";
3738
private static final String UNSUPPORTED_OPERATION = "No MongoDB support available for: '%s'";
3839
private static final ObjectMapper MAPPER = new ObjectMapper();
3940
private static final Map<ReturnDocumentType, ReturnDocument> RETURN_DOCUMENT_MAP =
@@ -66,7 +67,8 @@ public static String decodeKey(final String key) {
6667
public static String sanitizeJsonString(final String jsonString) throws JsonProcessingException {
6768
final JsonNode jsonNode = MAPPER.readTree(jsonString);
6869
// escape "." and "$" in field names since Mongo DB does not like them
69-
final JsonNode sanitizedJsonNode = recursiveClone(jsonNode, MongoUtils::encodeKey);
70+
final JsonNode sanitizedJsonNode =
71+
recursiveClone(jsonNode, MongoUtils::encodeKey, MongoUtils::wrapInLiteral);
7072
return MAPPER.writeValueAsString(sanitizedJsonNode);
7173
}
7274

@@ -79,7 +81,10 @@ public static BasicDBObject merge(final List<BasicDBObject> basicDbObjects) {
7981
toUnmodifiableMap(Entry::getKey, Entry::getValue), BasicDBObject::new));
8082
}
8183

82-
public static JsonNode recursiveClone(JsonNode src, Function<String, String> function) {
84+
public static JsonNode recursiveClone(
85+
JsonNode src,
86+
UnaryOperator<String> function,
87+
final UnaryOperator<ObjectNode> emptyObjectConverter) {
8388
if (!src.isObject()) {
8489
return src;
8590
}
@@ -92,11 +97,11 @@ public static JsonNode recursiveClone(JsonNode src, Function<String, String> fun
9297
JsonNode value = next.getValue();
9398
JsonNode newValue = value;
9499
if (value.isObject()) {
95-
newValue = recursiveClone(value, function);
100+
newValue = recursiveClone(value, function, emptyObjectConverter);
96101
}
97102
tgt.set(newFieldName, newValue);
98103
}
99-
return tgt;
104+
return tgt.isEmpty() ? emptyObjectConverter.apply(tgt) : tgt;
100105
}
101106

102107
public static Document dbObjectToDocument(BasicDBObject dbObject) {
@@ -110,7 +115,8 @@ public static Document dbObjectToDocument(BasicDBObject dbObject) {
110115
JsonWriterSettings.builder().outputMode(JsonMode.RELAXED).build();
111116
jsonString = dbObject.toJson(relaxed);
112117
JsonNode jsonNode = MAPPER.readTree(jsonString);
113-
JsonNode decodedJsonNode = recursiveClone(jsonNode, MongoUtils::decodeKey);
118+
JsonNode decodedJsonNode =
119+
recursiveClone(jsonNode, MongoUtils::decodeKey, UnaryOperator.identity());
114120
return new JSONDocument(decodedJsonNode);
115121
} catch (IOException e) {
116122
// throwing exception is not very useful here.
@@ -125,4 +131,13 @@ public static ReturnDocument getReturnDocument(final ReturnDocumentType returnDo
125131
new UnsupportedOperationException(
126132
String.format("Unhandled return document type: %s", returnDocumentType)));
127133
}
134+
135+
private static ObjectNode wrapInLiteral(final ObjectNode objectNode) {
136+
/* Wrapping the subDocument with $literal to be able to provide empty object "{}" as value
137+
* Throws error otherwise if empty object is provided as value.
138+
* https://jira.mongodb.org/browse/SERVER-54046 */
139+
final ObjectNode node = JsonNodeFactory.instance.objectNode();
140+
node.set(LITERAL, objectNode);
141+
return node;
142+
}
128143
}

0 commit comments

Comments
 (0)