From 92affb632ca3958f68291376f8c093ae53062113 Mon Sep 17 00:00:00 2001 From: Shantanu Bhosale <80242052+shantanu-vsbhosale@users.noreply.github.com> Date: Mon, 12 Jun 2023 11:13:40 +0530 Subject: [PATCH] Support for increment update query operator (#153) * Support for increment update query operator * Suggested comments * resolve comments * Update document-store/src/main/java/org/hypertrace/core/documentstore/mongo/update/MongoUpdateExecutor.java * Integration test failure --------- Co-authored-by: Suresh Prakash <93120060+suresh-prakash@users.noreply.github.com> --- .../documentstore/DocStoreQueryV1Test.java | 115 ++++++++++++++++-- .../bulk_update/updated_collection_data.json | 6 +- ...pdated_collection_data_relaxed_filter.json | 8 +- ...ated_collection_response_after_update.json | 8 +- .../query/update_operator/updated1.json | 18 +-- .../model/subdoc/UpdateOperator.java | 1 + .../mongo/update/MongoUpdateExecutor.java | 12 +- .../parser/MongoAddOperationParser.java | 24 ++++ .../update/parser/MongoUpdateParser.java | 1 + .../postgres/PostgresQueryBuilder.java | 3 + .../update/parser/PostgresAddValueParser.java | 29 +++++ 11 files changed, 195 insertions(+), 30 deletions(-) create mode 100644 document-store/src/main/java/org/hypertrace/core/documentstore/mongo/update/parser/MongoAddOperationParser.java create mode 100644 document-store/src/main/java/org/hypertrace/core/documentstore/postgres/update/parser/PostgresAddValueParser.java diff --git a/document-store/src/integrationTest/java/org/hypertrace/core/documentstore/DocStoreQueryV1Test.java b/document-store/src/integrationTest/java/org/hypertrace/core/documentstore/DocStoreQueryV1Test.java index 23bbe646..d5db9221 100644 --- a/document-store/src/integrationTest/java/org/hypertrace/core/documentstore/DocStoreQueryV1Test.java +++ b/document-store/src/integrationTest/java/org/hypertrace/core/documentstore/DocStoreQueryV1Test.java @@ -30,6 +30,7 @@ import static org.hypertrace.core.documentstore.model.options.ReturnDocumentType.AFTER_UPDATE; import static org.hypertrace.core.documentstore.model.options.ReturnDocumentType.BEFORE_UPDATE; import static org.hypertrace.core.documentstore.model.options.ReturnDocumentType.NONE; +import static org.hypertrace.core.documentstore.model.subdoc.UpdateOperator.ADD; import static org.hypertrace.core.documentstore.model.subdoc.UpdateOperator.ADD_TO_LIST_IF_ABSENT; import static org.hypertrace.core.documentstore.model.subdoc.UpdateOperator.APPEND_TO_LIST; import static org.hypertrace.core.documentstore.model.subdoc.UpdateOperator.REMOVE_ALL_FROM_LIST; @@ -2164,9 +2165,16 @@ void testUpdateWithAllOperators(final String datastoreName) throws IOException { .operator(REMOVE_ALL_FROM_LIST) .subDocumentValue(SubDocumentValue.of(new String[] {"Hello"})) .build(); + final SubDocumentUpdate increment = + SubDocumentUpdate.builder() + .subDocument("price") + .operator(ADD) + .subDocumentValue(SubDocumentValue.of(1)) + .build(); final Query query = Query.builder().build(); - final List updates = List.of(set, unset, add, another_add, append, remove); + final List updates = + List.of(set, unset, add, another_add, append, remove, increment); final CloseableIterator iterator = collection.bulkUpdate( @@ -2203,8 +2211,14 @@ void testUpdateWithAllOperators(final String datastoreName) throws IOException { .operator(REMOVE_ALL_FROM_LIST) .subDocumentValue(SubDocumentValue.of(new String[] {"Pluto", "Mars"})) .build(); + final SubDocumentUpdate decrement = + SubDocumentUpdate.builder() + .subDocument("price") + .operator(ADD) + .subDocumentValue(SubDocumentValue.of(-1)) + .build(); final List new_updates = - List.of(set_new, unset_new, add_new, append_new, remove_new); + List.of(set_new, unset_new, add_new, append_new, remove_new, decrement); final CloseableIterator iterator_new = collection.bulkUpdate( @@ -2320,6 +2334,7 @@ void testUpdateWithAllOperatorsOnObject(final String datastoreName) throws IOExc new JSONDocument(Map.of("name", "Mars")) })) .build(); + final List new_updates = List.of(set_new, unset_new, add_new, append_new, remove_new); @@ -2433,6 +2448,62 @@ void testSameHierarchyUpdateThrowsException(final String datastoreName) throws I collection.bulkUpdate( query, updates, UpdateOptions.builder().returnDocumentType(NONE).build())); } + + @ParameterizedTest + @ArgumentsSource(AllProvider.class) + void testAddOperatorThrowExceptionForNonNumericValue(final String datastoreName) + throws IOException { + final Collection collection = getCollection(datastoreName, UPDATABLE_COLLECTION_NAME); + createCollectionData("query/updatable_collection_data.json", UPDATABLE_COLLECTION_NAME); + + // assert exception for string + final SubDocumentUpdate addString = + SubDocumentUpdate.builder() + .subDocument("item") + .operator(ADD) + .subDocumentValue(SubDocumentValue.of("Comb")) + .build(); + + final Query query = Query.builder().build(); + final List updates = List.of(addString); + assertExceptionForNonNumericValues(collection, query, updates); + + // assert exception for list + final SubDocumentUpdate addList = + SubDocumentUpdate.builder() + .subDocument("props.added.list") + .operator(ADD) + .subDocumentValue(SubDocumentValue.of(new Integer[] {5, 1, 5})) + .build(); + final Query query_addList = Query.builder().build(); + final List updates_addList = List.of(addList); + assertExceptionForNonNumericValues(collection, query_addList, updates_addList); + + // assert exception for Object + final SubDocumentUpdate addObject = + SubDocumentUpdate.builder() + .subDocument("props.newObject") + .operator(ADD) + .subDocumentValue( + SubDocumentValue.of( + new Document[] { + new JSONDocument(Map.of("name", "Pluto")), + new JSONDocument(Map.of("name", "Mars")) + })) + .build(); + final Query query_addObject = Query.builder().build(); + final List updates_addObject = List.of(addObject); + assertExceptionForNonNumericValues(collection, query_addObject, updates_addObject); + } + + private void assertExceptionForNonNumericValues( + Collection collection, Query query, List updates) { + assertThrows( + IOException.class, + () -> + collection.bulkUpdate( + query, updates, UpdateOptions.builder().returnDocumentType(NONE).build())); + } } @Nested @@ -2467,6 +2538,12 @@ void testBulkUpdateWithFilterAndGetNoDocuments(final String datastoreName) throw final SubDocumentUpdate dateUpdate = SubDocumentUpdate.of("date", "2022-08-09T18:53:17Z"); final SubDocumentUpdate quantityUpdate = SubDocumentUpdate.of("quantity", 1000); final SubDocumentUpdate propsUpdate = SubDocumentUpdate.of("props.brand", "Dettol"); + final SubDocumentUpdate priceUpdate = + SubDocumentUpdate.builder() + .subDocument("price") + .operator(ADD) + .subDocumentValue(SubDocumentValue.of(1)) + .build(); final SubDocumentUpdate addProperty = SubDocumentUpdate.of( "props.new_property.deep.nested.value", @@ -2475,7 +2552,7 @@ void testBulkUpdateWithFilterAndGetNoDocuments(final String datastoreName) throw final CloseableIterator docIterator = collection.bulkUpdate( query, - List.of(dateUpdate, quantityUpdate, propsUpdate, addProperty), + List.of(dateUpdate, quantityUpdate, propsUpdate, addProperty, priceUpdate), UpdateOptions.builder().returnDocumentType(NONE).build()); assertFalse(docIterator.hasNext()); @@ -2525,6 +2602,12 @@ void testBulkUpdateWithFilterAndGetAfterDocumentsEmpty(final String datastoreNam final SubDocumentUpdate dateUpdate = SubDocumentUpdate.of("date", "2022-08-09T18:53:17Z"); final SubDocumentUpdate quantityUpdate = SubDocumentUpdate.of("quantity", 1000); final SubDocumentUpdate propsUpdate = SubDocumentUpdate.of("props.brand", "Dettol"); + final SubDocumentUpdate priceUpdate = + SubDocumentUpdate.builder() + .subDocument("price") + .operator(ADD) + .subDocumentValue(SubDocumentValue.of(1)) + .build(); final SubDocumentUpdate addProperty = SubDocumentUpdate.of( "props.new_property.deep.nested.value", @@ -2533,7 +2616,7 @@ void testBulkUpdateWithFilterAndGetAfterDocumentsEmpty(final String datastoreNam final CloseableIterator docIterator = collection.bulkUpdate( query, - List.of(dateUpdate, quantityUpdate, propsUpdate, addProperty), + List.of(dateUpdate, quantityUpdate, propsUpdate, addProperty, priceUpdate), UpdateOptions.builder().returnDocumentType(AFTER_UPDATE).build()); // Since the date is updated to conflict with the filter, there will not be any documents @@ -2575,6 +2658,12 @@ void testBulkUpdateWithFilterAndGetAfterDocumentsNonEmpty(final String datastore final SubDocumentUpdate dateUpdate = SubDocumentUpdate.of("date", "2022-08-09T18:53:17Z"); final SubDocumentUpdate quantityUpdate = SubDocumentUpdate.of("quantity", 1000); final SubDocumentUpdate propsUpdate = SubDocumentUpdate.of("props.brand", "Dettol"); + final SubDocumentUpdate priceUpdate = + SubDocumentUpdate.builder() + .subDocument("price") + .operator(ADD) + .subDocumentValue(SubDocumentValue.of(1)) + .build(); final SubDocumentUpdate addProperty = SubDocumentUpdate.of( "props.new_property.deep.nested.value", @@ -2583,7 +2672,7 @@ void testBulkUpdateWithFilterAndGetAfterDocumentsNonEmpty(final String datastore final CloseableIterator docIterator = collection.bulkUpdate( query, - List.of(dateUpdate, quantityUpdate, propsUpdate, addProperty), + List.of(dateUpdate, quantityUpdate, propsUpdate, addProperty, priceUpdate), UpdateOptions.builder().returnDocumentType(AFTER_UPDATE).build()); assertDocsAndSizeEqual( @@ -2632,11 +2721,17 @@ void testBulkUpdateWithFilterAndGetBeforeDocuments(final String datastoreName) SubDocumentUpdate.of( "props.new_property.deep.nested.value", SubDocumentValue.of(new JSONDocument("{\"json\": \"new_value\"}"))); + final SubDocumentUpdate priceUpdate = + SubDocumentUpdate.builder() + .subDocument("price") + .operator(ADD) + .subDocumentValue(SubDocumentValue.of(1)) + .build(); final CloseableIterator docIterator = collection.bulkUpdate( query, - List.of(dateUpdate, quantityUpdate, propsUpdate, addProperty), + List.of(dateUpdate, quantityUpdate, propsUpdate, addProperty, priceUpdate), UpdateOptions.builder().returnDocumentType(BEFORE_UPDATE).build()); assertDocsAndSizeEqual( @@ -2687,11 +2782,17 @@ void testBulkUpdateWithNonMatchingFilterAndGetBeforeDocuments(final String datas SubDocumentUpdate.of( "props.new_property.deep.nested.value", SubDocumentValue.of(new JSONDocument("{\"json\": \"new_value\"}"))); + final SubDocumentUpdate priceUpdate = + SubDocumentUpdate.builder() + .subDocument("price") + .operator(ADD) + .subDocumentValue(SubDocumentValue.of(1)) + .build(); final CloseableIterator docIterator = collection.bulkUpdate( query, - List.of(dateUpdate, quantityUpdate, propsUpdate, addProperty), + List.of(dateUpdate, quantityUpdate, propsUpdate, addProperty, priceUpdate), UpdateOptions.builder().returnDocumentType(BEFORE_UPDATE).build()); assertFalse(docIterator.hasNext()); diff --git a/document-store/src/integrationTest/resources/query/bulk_update/updated_collection_data.json b/document-store/src/integrationTest/resources/query/bulk_update/updated_collection_data.json index 8826da7f..e954846d 100644 --- a/document-store/src/integrationTest/resources/query/bulk_update/updated_collection_data.json +++ b/document-store/src/integrationTest/resources/query/bulk_update/updated_collection_data.json @@ -2,7 +2,7 @@ { "date": "2022-08-09T18:53:17Z", "item": "Soap", - "price": 10, + "price": 11, "quantity": 1000, "props": { "brand": "Dettol", @@ -57,7 +57,7 @@ { "date": "2022-08-09T18:53:17Z", "item": "Soap", - "price": 20, + "price": 21, "quantity": 1000, "props": { "brand": "Dettol", @@ -104,7 +104,7 @@ { "date": "2022-08-09T18:53:17Z", "item": "Soap", - "price": 10, + "price": 11, "quantity": 1000, "props": { "brand": "Dettol", diff --git a/document-store/src/integrationTest/resources/query/bulk_update/updated_collection_data_relaxed_filter.json b/document-store/src/integrationTest/resources/query/bulk_update/updated_collection_data_relaxed_filter.json index 3271cd9e..b8341a66 100644 --- a/document-store/src/integrationTest/resources/query/bulk_update/updated_collection_data_relaxed_filter.json +++ b/document-store/src/integrationTest/resources/query/bulk_update/updated_collection_data_relaxed_filter.json @@ -2,7 +2,7 @@ { "date": "2022-08-09T18:53:17Z", "item": "Soap", - "price": 10, + "price": 11, "quantity": 1000, "props": { "brand": "Dettol", @@ -57,7 +57,7 @@ { "date": "2022-08-09T18:53:17Z", "item": "Soap", - "price": 20, + "price": 21, "quantity": 1000, "props": { "brand": "Dettol", @@ -104,7 +104,7 @@ { "date": "2022-08-09T18:53:17Z", "item": "Soap", - "price": 10, + "price": 11, "quantity": 1000, "props": { "brand": "Dettol", @@ -121,7 +121,7 @@ }, { "item": "Soap", - "price": 88, + "price": 89, "quantity": 1000, "date": "2022-08-09T18:53:17Z", "props": { diff --git a/document-store/src/integrationTest/resources/query/bulk_update/updated_collection_response_after_update.json b/document-store/src/integrationTest/resources/query/bulk_update/updated_collection_response_after_update.json index 1f0182c4..1531b19f 100644 --- a/document-store/src/integrationTest/resources/query/bulk_update/updated_collection_response_after_update.json +++ b/document-store/src/integrationTest/resources/query/bulk_update/updated_collection_response_after_update.json @@ -1,7 +1,7 @@ [ { "date": "2022-08-09T18:53:17Z", - "price": 10, + "price": 11, "quantity": 1000, "props": { "brand": "Dettol", @@ -26,7 +26,7 @@ }, { "date": "2022-08-09T18:53:17Z", - "price": 10, + "price": 11, "quantity": 1000, "props": { "brand": "Dettol", @@ -43,7 +43,7 @@ }, { "date": "2022-08-09T18:53:17Z", - "price": 20, + "price": 21, "quantity": 1000, "props": { "brand": "Dettol", @@ -67,7 +67,7 @@ } }, { - "price": 88, + "price": 89, "quantity": 1000, "date": "2022-08-09T18:53:17Z", "props": { diff --git a/document-store/src/integrationTest/resources/query/update_operator/updated1.json b/document-store/src/integrationTest/resources/query/update_operator/updated1.json index 24b993a7..3851ed18 100644 --- a/document-store/src/integrationTest/resources/query/update_operator/updated1.json +++ b/document-store/src/integrationTest/resources/query/update_operator/updated1.json @@ -1,7 +1,7 @@ [ { "item": "Soap", - "price": 10, + "price": 11, "quantity": 2, "date": "2014-03-01T08:00:00Z", "props": { @@ -41,7 +41,7 @@ }, { "item": "Mirror", - "price": 20, + "price": 21, "quantity": 1, "date": "2014-03-01T09:00:00Z", "props": { @@ -72,7 +72,7 @@ }, { "item": "Shampoo", - "price": 5, + "price": 6, "quantity": 10, "date": "2014-03-15T09:00:00Z", "props": { @@ -112,7 +112,7 @@ }, { "item": "Shampoo", - "price": 5, + "price": 6, "quantity": 20, "date": "2014-04-04T11:21:39.736Z", "props": { @@ -143,7 +143,7 @@ }, { "item": "Soap", - "price": 20, + "price": 21, "quantity": 5, "date": "2014-04-04T21:23:13.331Z", "props": { @@ -183,7 +183,7 @@ }, { "item": "Comb", - "price": 7.5, + "price": 8.5, "quantity": 5, "date": "2015-06-04T05:08:13Z", "props": { @@ -214,7 +214,7 @@ }, { "item": "Comb", - "price": 7.5, + "price": 8.5, "quantity": 10, "date": "2015-09-10T08:43:00Z", "props": { @@ -252,7 +252,7 @@ }, { "item": "Soap", - "price": 10, + "price": 11, "quantity": 5, "date": "2016-02-06T20:20:13Z", "props": { @@ -283,7 +283,7 @@ }, { "item": "Soap", - "price": 88, + "price": 89, "quantity": 50, "date": "2023-08-09T18:53:17Z", "props": { diff --git a/document-store/src/main/java/org/hypertrace/core/documentstore/model/subdoc/UpdateOperator.java b/document-store/src/main/java/org/hypertrace/core/documentstore/model/subdoc/UpdateOperator.java index 776735df..257d5889 100644 --- a/document-store/src/main/java/org/hypertrace/core/documentstore/model/subdoc/UpdateOperator.java +++ b/document-store/src/main/java/org/hypertrace/core/documentstore/model/subdoc/UpdateOperator.java @@ -6,4 +6,5 @@ public enum UpdateOperator { REMOVE_ALL_FROM_LIST, ADD_TO_LIST_IF_ABSENT, APPEND_TO_LIST, + ADD } diff --git a/document-store/src/main/java/org/hypertrace/core/documentstore/mongo/update/MongoUpdateExecutor.java b/document-store/src/main/java/org/hypertrace/core/documentstore/mongo/update/MongoUpdateExecutor.java index 6f3fbe44..6b73ff51 100644 --- a/document-store/src/main/java/org/hypertrace/core/documentstore/mongo/update/MongoUpdateExecutor.java +++ b/document-store/src/main/java/org/hypertrace/core/documentstore/mongo/update/MongoUpdateExecutor.java @@ -111,8 +111,14 @@ public Optional> bulkUpdate( } } - private void logAndUpdate(final BasicDBObject filter, final BasicDBObject setObject) { - log.debug("Updating {} using {} with filter {}", collection.getNamespace(), setObject, filter); - collection.updateMany(filter, setObject); + private void logAndUpdate(final BasicDBObject filter, final BasicDBObject setObject) + throws IOException { + try { + log.debug( + "Updating {} using {} with filter {}", collection.getNamespace(), setObject, filter); + collection.updateMany(filter, setObject); + } catch (Exception e) { + throw new IOException("Error while updating", e); + } } } diff --git a/document-store/src/main/java/org/hypertrace/core/documentstore/mongo/update/parser/MongoAddOperationParser.java b/document-store/src/main/java/org/hypertrace/core/documentstore/mongo/update/parser/MongoAddOperationParser.java new file mode 100644 index 00000000..4e57d5ac --- /dev/null +++ b/document-store/src/main/java/org/hypertrace/core/documentstore/mongo/update/parser/MongoAddOperationParser.java @@ -0,0 +1,24 @@ +package org.hypertrace.core.documentstore.mongo.update.parser; + +import static org.hypertrace.core.documentstore.model.subdoc.UpdateOperator.ADD; +import static org.hypertrace.core.documentstore.mongo.MongoUtils.merge; + +import com.mongodb.BasicDBObject; +import java.util.List; +import lombok.AllArgsConstructor; +import org.hypertrace.core.documentstore.model.subdoc.UpdateOperator; + +@AllArgsConstructor +public class MongoAddOperationParser extends MongoOperationParser { + private static final String INCREMENT_CLAUSE = "$inc"; + + @Override + UpdateOperator operator() { + return ADD; + } + + @Override + BasicDBObject wrapWithOperator(List parsed) { + return new BasicDBObject(INCREMENT_CLAUSE, merge(parsed)); + } +} diff --git a/document-store/src/main/java/org/hypertrace/core/documentstore/mongo/update/parser/MongoUpdateParser.java b/document-store/src/main/java/org/hypertrace/core/documentstore/mongo/update/parser/MongoUpdateParser.java index e942c620..d7bd9344 100644 --- a/document-store/src/main/java/org/hypertrace/core/documentstore/mongo/update/parser/MongoUpdateParser.java +++ b/document-store/src/main/java/org/hypertrace/core/documentstore/mongo/update/parser/MongoUpdateParser.java @@ -21,6 +21,7 @@ public class MongoUpdateParser { new MongoAppendToListOperationParser(), new MongoRemoveAllFromListOperationParser(), new MongoSetOperationParser(), + new MongoAddOperationParser(), new MongoUnsetOperationParser()); private final Clock clock; diff --git a/document-store/src/main/java/org/hypertrace/core/documentstore/postgres/PostgresQueryBuilder.java b/document-store/src/main/java/org/hypertrace/core/documentstore/postgres/PostgresQueryBuilder.java index 641c5b01..91107cc7 100644 --- a/document-store/src/main/java/org/hypertrace/core/documentstore/postgres/PostgresQueryBuilder.java +++ b/document-store/src/main/java/org/hypertrace/core/documentstore/postgres/PostgresQueryBuilder.java @@ -1,6 +1,7 @@ package org.hypertrace.core.documentstore.postgres; import static java.util.Map.entry; +import static org.hypertrace.core.documentstore.model.subdoc.UpdateOperator.ADD; import static org.hypertrace.core.documentstore.model.subdoc.UpdateOperator.ADD_TO_LIST_IF_ABSENT; import static org.hypertrace.core.documentstore.model.subdoc.UpdateOperator.APPEND_TO_LIST; import static org.hypertrace.core.documentstore.model.subdoc.UpdateOperator.REMOVE_ALL_FROM_LIST; @@ -21,6 +22,7 @@ import org.hypertrace.core.documentstore.postgres.Params.Builder; import org.hypertrace.core.documentstore.postgres.query.v1.PostgresQueryParser; import org.hypertrace.core.documentstore.postgres.update.parser.PostgresAddToListIfAbsentParser; +import org.hypertrace.core.documentstore.postgres.update.parser.PostgresAddValueParser; import org.hypertrace.core.documentstore.postgres.update.parser.PostgresAppendToListParser; import org.hypertrace.core.documentstore.postgres.update.parser.PostgresRemoveAllFromListParser; import org.hypertrace.core.documentstore.postgres.update.parser.PostgresSetValueParser; @@ -35,6 +37,7 @@ public class PostgresQueryBuilder { Map.ofEntries( entry(SET, new PostgresSetValueParser()), entry(UNSET, new PostgresUnsetPathParser()), + entry(ADD, new PostgresAddValueParser()), entry(REMOVE_ALL_FROM_LIST, new PostgresRemoveAllFromListParser()), entry(ADD_TO_LIST_IF_ABSENT, new PostgresAddToListIfAbsentParser()), entry(APPEND_TO_LIST, new PostgresAppendToListParser())); diff --git a/document-store/src/main/java/org/hypertrace/core/documentstore/postgres/update/parser/PostgresAddValueParser.java b/document-store/src/main/java/org/hypertrace/core/documentstore/postgres/update/parser/PostgresAddValueParser.java new file mode 100644 index 00000000..ac808887 --- /dev/null +++ b/document-store/src/main/java/org/hypertrace/core/documentstore/postgres/update/parser/PostgresAddValueParser.java @@ -0,0 +1,29 @@ +package org.hypertrace.core.documentstore.postgres.update.parser; + +import static org.hypertrace.core.documentstore.postgres.utils.PostgresUtils.formatSubDocPath; +import static org.hypertrace.core.documentstore.postgres.utils.PostgresUtils.prepareFieldDataAccessorExpr; + +import org.hypertrace.core.documentstore.postgres.Params; +import org.hypertrace.core.documentstore.postgres.subdoc.PostgresSubDocumentValueParser; + +public class PostgresAddValueParser implements PostgresUpdateOperationParser { + @Override + public String parseInternal(UpdateParserInput input) { + return new PostgresSetValueParser(this, 1).parseInternal(input); + } + + @Override + public String parseLeaf(UpdateParserInput input) { + final Params.Builder paramsBuilder = input.getParamsBuilder(); + final PostgresSubDocumentValueParser valueParser = + new PostgresSubDocumentValueParser(paramsBuilder); + + paramsBuilder.addObjectParam(formatSubDocPath(input.getPath()[0])); + final String parsedValue = input.getUpdate().getSubDocumentValue().accept(valueParser); + final String fieldAccess = + prepareFieldDataAccessorExpr(input.getPath()[0], input.getBaseField()); + return String.format( + "jsonb_set(%s, ?::text[], (COALESCE(%s, '0')::float + %s::float)::text::jsonb)", + input.getBaseField(), fieldAccess, parsedValue); + } +}