Skip to content

Commit 4adcb5e

Browse files
authored
perf: optimize mongo update impl when return document type is none (#241)
* perf: optimize mongo update impl when return document type is none * test: fix unit test * test: add integration test * Revert "test: fix unit test" This reverts commit a01a694. * Revert "perf: optimize mongo update impl when return document type is none" This reverts commit d85dc00. * perf: optimize update impl for mongo by projecting only _id field when return document types is none
1 parent f0e2bef commit 4adcb5e

File tree

3 files changed

+111
-4
lines changed

3 files changed

+111
-4
lines changed

document-store/src/integrationTest/java/org/hypertrace/core/documentstore/DocStoreQueryV1Test.java

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2510,6 +2510,107 @@ public void testAtomicUpdateDocumentWithoutSelections(final String datastoreName
25102510
"query/updatable_collection_data_without_selection.json",
25112511
9);
25122512
}
2513+
2514+
@ParameterizedTest
2515+
@ArgumentsSource(AllProvider.class)
2516+
public void testAtomicUpdateWithReturnDocumentTypeNone(final String datastoreName)
2517+
throws IOException {
2518+
final Collection collection = getCollection(datastoreName, UPDATABLE_COLLECTION_NAME);
2519+
createCollectionData("query/updatable_collection_data.json", UPDATABLE_COLLECTION_NAME);
2520+
2521+
final Query query =
2522+
Query.builder()
2523+
.setFilter(
2524+
LogicalExpression.builder()
2525+
.operator(AND)
2526+
.operand(
2527+
RelationalExpression.of(
2528+
IdentifierExpression.of("item"), EQ, ConstantExpression.of("Soap")))
2529+
.operand(
2530+
RelationalExpression.of(
2531+
IdentifierExpression.of("date"),
2532+
LT,
2533+
ConstantExpression.of("2022-08-09T18:53:17Z")))
2534+
.build())
2535+
.addSort(SortingSpec.of(IdentifierExpression.of("price"), ASC))
2536+
.addSort(SortingSpec.of(IdentifierExpression.of("date"), DESC))
2537+
.addSelection(IdentifierExpression.of("quantity"))
2538+
.addSelection(IdentifierExpression.of("price"))
2539+
.addSelection(IdentifierExpression.of("date"))
2540+
.addSelection(IdentifierExpression.of("props"))
2541+
.build();
2542+
2543+
final SubDocumentUpdate dateUpdate = SubDocumentUpdate.of("date", "2022-08-09T18:53:17Z");
2544+
final SubDocumentUpdate quantityUpdate = SubDocumentUpdate.of("quantity", 1000);
2545+
final SubDocumentUpdate propsUpdate =
2546+
SubDocumentUpdate.of(
2547+
"props", SubDocumentValue.of(new JSONDocument("{\"brand\": \"Dettol\"}")));
2548+
2549+
// Perform update with ReturnDocumentType.NONE
2550+
final Optional<Document> updateResult =
2551+
collection.update(
2552+
query,
2553+
List.of(dateUpdate, quantityUpdate, propsUpdate),
2554+
UpdateOptions.builder().returnDocumentType(NONE).build());
2555+
2556+
// Verify that no document is returned (as expected with NONE)
2557+
assertFalse(
2558+
updateResult.isPresent(), "Should return empty Optional when ReturnDocumentType.NONE");
2559+
2560+
// Verify that the document was actually updated in the database by querying it
2561+
final Query verificationQuery =
2562+
Query.builder()
2563+
.setFilter(
2564+
LogicalExpression.builder()
2565+
.operator(AND)
2566+
.operand(
2567+
RelationalExpression.of(
2568+
IdentifierExpression.of("item"), EQ, ConstantExpression.of("Soap")))
2569+
.operand(
2570+
RelationalExpression.of(
2571+
IdentifierExpression.of("date"),
2572+
EQ,
2573+
ConstantExpression.of("2022-08-09T18:53:17Z")))
2574+
.build())
2575+
.build();
2576+
2577+
final CloseableIterator<Document> verificationResults = collection.find(verificationQuery);
2578+
assertTrue(verificationResults.hasNext(), "Updated document should exist in database");
2579+
2580+
final Document updatedDocument = verificationResults.next();
2581+
final JsonNode json = new ObjectMapper().readTree(updatedDocument.toJson());
2582+
// Verify that the right document was updated in the database
2583+
assertEquals(null, json.get("sales"), "'sales' field should not be present");
2584+
2585+
// Verify the fields were updated
2586+
assertEquals(1000, json.get("quantity").asInt(), "Quantity should be updated to 1000");
2587+
assertEquals("2022-08-09T18:53:17Z", json.get("date").asText(), "Date should be updated");
2588+
final JsonNode props = json.get("props");
2589+
assertEquals(
2590+
"Dettol", props.get("brand").asText(), "Props brand should be updated to Dettol");
2591+
2592+
verificationResults.close();
2593+
2594+
// Additional test: Verify that the same update with AFTER_UPDATE returns the document
2595+
// Reset the data first
2596+
createCollectionData("query/updatable_collection_data.json", UPDATABLE_COLLECTION_NAME);
2597+
2598+
final Optional<Document> afterUpdateResult =
2599+
collection.update(
2600+
query,
2601+
List.of(dateUpdate, quantityUpdate, propsUpdate),
2602+
UpdateOptions.builder().returnDocumentType(AFTER_UPDATE).build());
2603+
2604+
// This should return the updated document
2605+
assertTrue(
2606+
afterUpdateResult.isPresent(),
2607+
"Should return document when ReturnDocumentType.AFTER_UPDATE");
2608+
2609+
final Document returnedDocument = afterUpdateResult.get();
2610+
final JsonNode returnedJson = new ObjectMapper().readTree(returnedDocument.toJson());
2611+
assertEquals(1000, returnedJson.get("quantity").asInt());
2612+
assertEquals("2022-08-09T18:53:17Z", returnedJson.get("date").asText());
2613+
}
25132614
}
25142615

25152616
@Nested

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

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
import static org.hypertrace.core.documentstore.model.options.DataFreshness.REALTIME_FRESHNESS;
44
import static org.hypertrace.core.documentstore.model.options.ReturnDocumentType.NONE;
5+
import static org.hypertrace.core.documentstore.mongo.MongoCollection.ID_KEY;
56
import static org.hypertrace.core.documentstore.mongo.MongoUtils.getReturnDocument;
67
import static org.hypertrace.core.documentstore.mongo.query.parser.MongoFilterTypeExpressionParser.getFilter;
78
import static org.hypertrace.core.documentstore.mongo.query.parser.MongoSelectTypeExpressionParser.getSelections;
@@ -54,7 +55,6 @@ public Optional<Document> update(
5455
updateValidator.validate(updates);
5556

5657
try {
57-
final BasicDBObject selections = getSelections(query);
5858
final BasicDBObject sorts = getOrders(query);
5959
final FindOneAndUpdateOptions options = new FindOneAndUpdateOptions();
6060
options.upsert(
@@ -65,8 +65,14 @@ public Optional<Document> update(
6565

6666
options.returnDocument(getReturnDocument(returnDocumentType));
6767

68-
if (!selections.isEmpty()) {
69-
options.projection(selections);
68+
if (returnDocumentType == NONE) {
69+
// Optimize by projecting only the _id field when we don't need to return the document
70+
options.projection(new BasicDBObject(ID_KEY, 1));
71+
} else {
72+
final BasicDBObject selections = getSelections(query);
73+
if (!selections.isEmpty()) {
74+
options.projection(selections);
75+
}
7076
}
7177

7278
if (!sorts.isEmpty()) {

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -322,7 +322,7 @@ void testUpdateAtomicWithFilter_getNone() throws IOException {
322322

323323
assertFalse(result.isPresent());
324324

325-
assertEquals(selections, options.getValue().getProjection());
325+
assertEquals(new BasicDBObject("_id", 1), options.getValue().getProjection());
326326
assertEquals(sort, options.getValue().getSort());
327327
assertEquals(AFTER, options.getValue().getReturnDocument());
328328

0 commit comments

Comments
 (0)