-
Notifications
You must be signed in to change notification settings - Fork 2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
cosmos spring data - patch support #32630
cosmos spring data - patch support #32630
Conversation
...ta-cosmos-test/src/test/java/com/azure/spring/data/cosmos/core/ReactiveCosmosTemplateIT.java
Show resolved
Hide resolved
...azure-spring-data-cosmos/src/main/java/com/azure/spring/data/cosmos/core/CosmosTemplate.java
Show resolved
Hide resolved
* @return the patched entity | ||
* @throws IllegalArgumentException in case the given {@code id} is {@literal null}. | ||
*/ | ||
<S extends T> S save(S entityToPatch, CosmosPatchOperations patchOperations, CosmosPatchItemRequestOptions options); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
should we also introduce an API without the requestOptions? save(S entityToPatch, CosmosPatchOperations patchOperations);
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No we will not, we want to maintain the least amount of API functions possible. Allowing the requestOptions to be null is acceptable and follows our best practices in Spring.
public void testPatchPreConditionSuccess() { | ||
options.setFilterPredicate("FROM person p WHERE p.lastName = '"+LAST_NAME+"'"); | ||
Person patchedPerson = cosmosTemplate.patch(insertedPerson.getId(), new PartitionKey(insertedPerson.getLastName()), Person.class, operations, options); | ||
assertEquals(patchedPerson.getAge(), patchedPerson.getAge()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This check will always succeed as you are checking "a = a". Let's change the second param to the "Age" variable.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Corrected this
try { | ||
options.setFilterPredicate("FROM person p WHERE p.lastName = 'dummy'"); | ||
Person patchedPerson = cosmosTemplate.patch(insertedPerson.getId(), new PartitionKey(insertedPerson.getLastName()), Person.class, operations, options); | ||
assertEquals(patchedPerson.getAge(), patchedPerson.getAge()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same as above.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
corrected
/azp run java - cosmos - tests |
Azure Pipelines successfully started running 1 pipeline(s). |
/check-enforcer override |
* @param <T> type class of domain type | ||
* @return the patched item | ||
*/ | ||
<T> T patch(Object id, PartitionKey partitionKey, Class<T> domainType, CosmosPatchOperations patchOperations, CosmosPatchItemRequestOptions options); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If this has been discussed in the API review please just add brief comment and resolve this. But why are we adding an overload with request options here when none of the other operations allow specifying request options? I am not against it - just curious why we now decide t add it and skipped it for all other operations? Is there a more common scenario that would need it? Or is the idea to eventually add overload to other operations with request options as well?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Most of the discussion was around whether to add an overload, or force caller to pass null (we went with overload, so that the method call in most cases will be less verbose). But I think here you are asking why we add request options at all since we don't expose that elsewhere, which is a good question? It is purely to facilitate setFilterPredicate for patch. If we generally don't want to expose request options, then I guess we should encapsulate this and just have a string parameter in the overload, something like String filterPredicate.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
my 2c here is to avoid adding overloads with individual parameters and keep one with options if needed.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@lmolkova - actually that's what we had earlier :)
We had one API with requestOptions
included and requestOptions
can be nullable / optional. However, we got initial customer feedback from the PR review that if they need to pass in null, why are we forcing them to pass null and we should expose one API for simple use case. That's why we switched back to having 2 APIs.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
two thanks for the clarification, @kushagraThapar ! Two methods are fine (one with options, one without). I meant that the verbose one (with extra options), should take an extendable options class and not an individual filterPredicate
parameter.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think that is just in the example / doc on the API, not an actual API itself, no?
* @param <T> type class of domain type | ||
* @return Mono with the patched item | ||
*/ | ||
<T> Mono<T> patch(Object id, PartitionKey partitionKey, Class<T> domainType, CosmosPatchOperations patchOperations, CosmosPatchItemRequestOptions options); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
See comment in CosmosOperations
|
/check-enforcer override |
/check-enforcer override |
* @param <T> type class of domain type | ||
* @return the patched item | ||
*/ | ||
<T> T patch(Object id, PartitionKey partitionKey, Class<T> domainType, CosmosPatchOperations patchOperations, CosmosPatchItemRequestOptions options); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
my 2c here is to avoid adding overloads with individual parameters and keep one with options if needed.
* patches item | ||
* @param id must not be {@literal null} | ||
* @param partitionKey must not be {@literal null} | ||
* @param domainType must not be {@literal null} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
would be nice to add a description on what domainType
is
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
agreed.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks great @TheovanKraay
We should add more tests to repository tests, like AddressRepositoryIT
, etc.
...azure-spring-data-cosmos/src/main/java/com/azure/spring/data/cosmos/core/CosmosTemplate.java
Show resolved
Hide resolved
* @param <T> type class of domain type | ||
* @return the patched item | ||
*/ | ||
<T> T patch(Object id, PartitionKey partitionKey, Class<T> domainType, CosmosPatchOperations patchOperations, CosmosPatchItemRequestOptions options); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@lmolkova - actually that's what we had earlier :)
We had one API with requestOptions
included and requestOptions
can be nullable / optional. However, we got initial customer feedback from the PR review that if they need to pass in null, why are we forcing them to pass null and we should expose one API for simple use case. That's why we switched back to having 2 APIs.
* patches item | ||
* @param id must not be {@literal null} | ||
* @param partitionKey must not be {@literal null} | ||
* @param domainType must not be {@literal null} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
agreed.
Added |
/azp run java - cosmos - tests |
Azure Pipelines successfully started running 1 pipeline(s). |
/check-enforcer override |
Description
This PR adds an overload to Respository.save() method that allows entity to be patched (partial update). When this is called, only the fields defined in CosmosPatchOperations will be sent to Cosmos DB for update.
Note: RU cost for patch() is equivalent to the RU for replace() because the physics in terms of physical resources (cpu, memory, etc.) are roughly the same. The benefits from using patch come from being able to limit the number of updated fields sent over the wire, avoiding the need to retrieve the to-be-patched record from the database before patching, and avoiding the need to manually handle etag contention from concurrent updates.
All SDK Contribution checklist:
General Guidelines and Best Practices
Testing Guidelines