Patching: How to Perform Single Document Patch Operations
The Patch operation is used to perform partial document updates without having to load, modify, and save a full document. The whole operation is executed on the server side and is useful as a performance enhancement or for updating denormalized data in entities.
This page deals with patch operations on single documents.
Patching has three possible interfaces: Session API, Session API using defer, and Operations API.
Patching can be done from the client as well as in the studio.
- In this page:
API overview
Session API
A session interface that allows performing the most common patch operations.
The patch request will be sent to server only after the call to saveChanges
, this way it's possible to perform multiple operations in one request to the server.
Increment Field Value
session.advanced().increment
<T, U> void increment(String id, String path, U valueToAdd);
<T, U> void increment(T entity, String path, U valueToAdd);
Parameters | ||
---|---|---|
T | Class |
Entity class |
U | Class |
Field class, must be of numeric type, or a String of char for string concatenation |
entity | T |
Entity on which the operation should be performed. The entity should be one that was returned by the current session in a load or query operation, this way, the session can track down the entity's ID |
entity id | String |
Entity ID on which the operation should be performed. |
delta | U |
Value to be added. |
- Note the numeric values restrictions in JavaScript
Set Field Value
session.advanced().patch
<T, U> void patch(String id, String path, U value);
<T, U> void patch(T entity, String path, U value);
Parameters | ||
---|---|---|
T | Class |
Entity Class |
U | Class |
Field class |
entity | T |
Entity on which the operation should be performed. The entity should be one that was returned by the current session in a load or query operation, this way, the session can track down the entity's ID |
entity id | String |
Entity ID on which the operation should be performed. |
delta | U |
Value to set. |
Array Manipulation
session.advanced().patch
<T, U> void patch(T entity, String pathToArray, Consumer<JavaScriptArray<U>> arrayAdder);
<T, U> void patch(String id, String pathToArray, Consumer<JavaScriptArray<U>> arrayAdder);
Parameters | ||
---|---|---|
T | Class |
Entity class |
U | Class |
Field class |
entity | T |
Entity on which the operation should be performed. The entity should be one that was returned by the current session in a Load or Query operation, this way, the session can track down the entity's ID |
entity id | String |
Entity ID on which the operation should be performed. |
arrayAdder | Consumer<JavaScriptArray<U>> |
Lambda that modifies the array, see JavaScriptArray below. |
JavaScriptArray
JavaScriptArray
allows building lambdas representing array manipulations for patches.
Method Signature | Return Type | Description |
---|---|---|
put(T item) | JavaScriptArray |
Allows adding item to an array. |
put(T... items) | JavaScriptArray |
Items to be added to the array. |
put(Collection |
JavaScriptArray |
Items to be added to the array. |
removeAt(int index) | JavaScriptArray |
Removes item in position index in array. |
Session API using defer
The low level session api for patches uses the session.advanced().defer
function that allows registering one or more commands.
One of the possible commands is the PatchCommandData
, describing single document patch command.
The patch request will be sent to server only after the call to saveChanges
, this way it's possible to perform multiple operations in one request to the server.
session.advanced().defer
void defer(ICommandData[] commands);
PatchCommandData
Constructor | ||
---|---|---|
id | String |
ID of the document to be patched. |
changeVector | String |
[Can be null] Change vector of the document to be patched, used to verify that the document was not changed before the patch reached it. |
patch | PatchRequest |
Patch request to be performed on the document. |
patchIfMissing | PatchRequest |
[Can be null] Patch request to be performed if no document with the given ID was found. |
PatchRequest
We highly recommend using scripts with parameters. This allows RavenDB to cache scripts and boost performance. Parameters can be accessed in the script through the "args" object, and passed using PatchRequest's "Values" parameter.
Members | ||
---|---|---|
Script | String |
JavaScript code to be run. |
Values | Map<String, Object> |
Parameters to be passed to the script. The parameters can be accessed using the '$' prefix. Parameter starting with a '$' will be used as is, without further concatenation . |
Operations API
PatchStatus send(PatchOperation operation);
PatchStatus send(PatchOperation operation, SessionInfo sessionInfo);
<TEntity> PatchOperation.Result<TEntity> send(Class<TEntity> entityClass, PatchOperation operation);
<TEntity> PatchOperation.Result<TEntity> send(Class<TEntity> entityClass, PatchOperation operation, SessionInfo sessionInfo);
PatchOperation
Constructor | ||
---|---|---|
id | String |
ID of the document to be patched. |
changeVector | String |
[Can be null] Change vector of the document to be patched, used to verify that the document was not changed before the patch reached it. |
patch | PatchRequest |
Patch request to be performed on the document. |
patchIfMissing | PatchRequest |
[Can be null] Patch request to be performed if no document with the given ID was found. Will run only if no changeVector was passed. |
skipPatchIfChangeVectorMismatch | boolean |
If false and changeVector has value, and document with that ID and change vector was not found, will throw exception. |
List of Script Methods
This is a list of a few of the javascript methods that can be used in patch scripts. See the more comprehensive list at Knowledge Base: JavaScript Engine.
Method | Arguments | Description |
---|---|---|
load | string or string[] |
Loads one or more documents into the context of the script by their document IDs |
loadPath | A document and a path to an ID within that document | Loads a related document by the path to its ID |
del | Document ID; change vector | Delete the given document by its ID. If you add the expected change vector and the document's current change vector does not match, the document will not be deleted. |
put | Document ID; document; change vector | Create or overwrite a document with a specified ID and entity. If you try to overwrite an existing document and pass the expected change vector, the put will fail if the specified change vector does not match the document's current change vector. |
cmpxchg | Key | Load a compare exchange value into the context of the script using its key |
getMetadata | Document | Returns the document's metadata |
id | Document | Returns the document's ID |
lastModified | Document | Returns the DateTime of the most recent modification made to the given document |
Examples
Change Field's Value
// change firstName to Robert
session
.advanced()
.patch("employees/1", "FirstName", "Robert");
// change firstName to Robert
PatchRequest patchRequest = new PatchRequest();
patchRequest.setScript("this.FirstName = args.firstName");
patchRequest.setValues(Collections.singletonMap("firstName", "Robert"));
PatchCommandData patchCommandData = new PatchCommandData("employees/1", null, patchRequest, null);
session.advanced().defer(patchCommandData);
session.saveChanges();
// change firstName to Robert
PatchRequest patchRequest = new PatchRequest();
patchRequest.setScript("this.FirstName = args.firstName;");
patchRequest.setValues(Collections.singletonMap("firstName", "Robert"));
PatchOperation patchOperation = new PatchOperation("employees/1", null, patchRequest);
store.operations().send(patchOperation);
Change Values of Two Fields
// change firstName to Robert and lastName to Carter in single request
// note that in this case, we create single request, but two separate batch operations
// in order to achieve atomicity, please use the non generic APIs
session.advanced().patch("employees/1", "FirstName", "Robert");
session.advanced().patch("employees/1", "LastName", "Carter");
session.saveChanges();
// change firstName to Robert and lastName to Carter in single request
// note that here we do maintain the atomicity of the operation
PatchRequest patchRequest = new PatchRequest();
patchRequest.setScript("this.FirstName = args.firstName;" +
"this.LastName = args.lastName");
Map<String, Object> values = new HashMap<>();
values.put("FirstName", "Robert");
values.put("LastName", "Carter");
patchRequest.setValues(values);
session.advanced().defer(new PatchCommandData("employees/1", null, patchRequest, null));
session.saveChanges();
// change FirstName to Robert and LastName to Carter in single request
// note that here we do maintain the atomicity of the operation
PatchRequest patchRequest = new PatchRequest();
patchRequest.setScript("this.FirstName = args.firstName; " +
"this.LastName = args.lastName");
Map<String, Object> values = new HashMap<>();
values.put("FirstName", "Robert");
values.put("LastName", "Carter");
patchRequest.setValues(values);
store.operations().send(new PatchOperation("employees/1", null, patchRequest));
Increment Value
// increment UnitsInStock property value by 10
session.advanced().increment("products/1-A", "UnitsInStock", 10);
session.saveChanges();
PatchRequest request = new PatchRequest();
request.setScript("this.UnitsInStock += args.UnitsToAdd");
request.setValues(Collections.singletonMap("UnitsToAdd", 10));
session.advanced().defer(
new PatchCommandData("products/1-A", null, request, null));
session.saveChanges();
PatchRequest request = new PatchRequest();
request.setScript("this.UnitsInStock += args.unitsToAdd");
request.setValues(Collections.singletonMap("unitsToAdd", 10));
store.operations().send(new PatchOperation("products/1-A", null, request));
Add Item to Array
BlogComment comment = new BlogComment();
comment.setContent("Lore ipsum");
comment.setTitle("Some title");
session.advanced()
.patch("blgoposts/1", "comments", comments -> comments.add(comment));
session.saveChanges();
// add a new comment to comments
BlogComment comment = new BlogComment();
comment.setContent("Lore ipsum");
comment.setTitle("Some title");
PatchRequest patchRequest = new PatchRequest();
patchRequest.setScript("this.comments.push(args.comment");
patchRequest.setValues(Collections.singletonMap("comment", comment));
session.advanced().defer(new PatchCommandData("blogposts/1", null, patchRequest, null));
session.saveChanges();
// add a new comment to comments
BlogComment comment = new BlogComment();
comment.setContent("Lore ipsum");
comment.setTitle("Some title");
PatchRequest patchRequest = new PatchRequest();
patchRequest.setScript("this.comments.push(args.comment");
patchRequest.setValues(Collections.singletonMap("comment", comment));
store.operations().send(new PatchOperation("blogposts/1", null, patchRequest));
Insert Item into Specific Position in Array
Inserting item into specific position is supported only by the non-typed APIs
BlogComment comment = new BlogComment();
comment.setContent("Lore ipsum");
comment.setTitle("Some title");
PatchRequest patchRequest = new PatchRequest();
patchRequest.setScript("this.comments.splice(1, 0, args.comment)");
patchRequest.setValues(Collections.singletonMap("comment", comment));
session.advanced().defer(new PatchCommandData("blogposts/1", null, patchRequest, null));
session.saveChanges();
BlogComment comment = new BlogComment();
comment.setContent("Lore ipsum");
comment.setTitle("Some title");
PatchRequest patchRequest = new PatchRequest();
patchRequest.setScript("this.comments.splice(1, 0, args.comment)");
patchRequest.setValues(Collections.singletonMap("comment", comment));
store.operations().send(new PatchOperation("blogposts/1", null, patchRequest));
Modify Item in Specific Position in Array
Inserting item into specific position is supported only by the non-typed APIs
// modify a comment at position 3 in Comments
BlogComment comment = new BlogComment();
comment.setContent("Lore ipsum");
comment.setTitle("Some title");
PatchRequest patchRequest = new PatchRequest();
patchRequest.setScript("this.comments.splice(3, 1, args.comment)");
patchRequest.setValues(Collections.singletonMap("comment", comment));
session.advanced().defer(new PatchCommandData("blogposts/1", null, patchRequest, null));
session.saveChanges();
// modify a comment at position 3 in Comments
BlogComment comment = new BlogComment();
comment.setContent("Lore ipsum");
comment.setTitle("Some title");
PatchRequest patchRequest = new PatchRequest();
patchRequest.setScript("this.comments.splice(3, 1, args.comment)");
patchRequest.setValues(Collections.singletonMap("comment", comment));
store.operations().send(new PatchOperation("blogposts/1", null, patchRequest));
Filter out Items from an Array
Filtering items from an array supported only by the non-typed APIs
//filter out all comments of a blogpost which contains the word "wrong" in their contents
PatchRequest patchRequest = new PatchRequest();
patchRequest.setScript("this.comments = this.comments.filter(comment " +
"=> comment.content.includes(args.titleToRemove));");
patchRequest.setValues(Collections.singletonMap("titleToRemove", "wrong"));
session.advanced().defer(
new PatchCommandData("blogposts/1", null, patchRequest, null));
session.saveChanges();
// filter out all comments of a blogpost which contains the word "wrong" in their contents
PatchRequest patchRequest = new PatchRequest();
patchRequest.setScript("this.comments = this.comments.filter(comment " +
"=> comment.content.includes(args.titleToRemove));");
patchRequest.setValues(Collections.singletonMap("titleToRemove", "wrong"));
store.operations().send(new PatchOperation("blogposts/1", null, patchRequest));
Loading Documents in a Script
Loading documents supported only by non-typed APIs
// update product names in order, according to loaded product documents
PatchRequest patchRequest = new PatchRequest();
patchRequest.setScript("this.Lines.forEach(line => {" +
" var productDoc = load(line.Product);" +
" line.ProductName = productDoc.Name;" +
"});");
session.advanced().defer(
new PatchCommandData("orders/1", null, patchRequest, null));
session.saveChanges();
// update product names in order, according to loaded product documents
PatchRequest patchRequest = new PatchRequest();
patchRequest.setScript("this.Lines.forEach(line => {" +
" var productDoc = load(line.Product);" +
" line.ProductName = productDoc.Name;" +
"});");
store.operations().send(new PatchOperation("orders/1", null, patchRequest));
Remove Property
Removing property supported only by the non-typed APIs
// rename firstName to First
Map<String, Object> value = new HashMap<>();
value.put("old", "FirstName");
value.put("new", "Name");
PatchRequest patchRequest = new PatchRequest();
patchRequest.setScript("var firstName = this[args.rename.old];" +
"delete this[args.rename.old];" +
"this[args.rename.new] = firstName;");
patchRequest.setValues(Collections.singletonMap("rename", value));
session.advanced().defer(new PatchCommandData("employees/1", null, patchRequest, null));
session.saveChanges();
Map<String, Object> value = new HashMap<>();
value.put("old", "FirstName");
value.put("new", "Name");
PatchRequest patchRequest = new PatchRequest();
patchRequest.setScript("var firstName = this[args.rename.old];" +
"delete this[args.rename.old];" +
"this[args.rename.new] = firstName;");
patchRequest.setValues(Collections.singletonMap("rename", value));
store.operations().send(new PatchOperation("employees/1", null, patchRequest));
Rename Property
Renaming property supported only by the non-typed APIs
// rename firstName to First
Map<String, Object> value = new HashMap<>();
value.put("old", "FirstName");
value.put("new", "Name");
PatchRequest patchRequest = new PatchRequest();
patchRequest.setScript("var firstName = this[args.rename.old];" +
"delete this[args.rename.old];" +
"this[args.rename.new] = firstName;");
patchRequest.setValues(Collections.singletonMap("rename", value));
session.advanced().defer(new PatchCommandData("employees/1", null, patchRequest, null));
session.saveChanges();
Map<String, Object> value = new HashMap<>();
value.put("old", "FirstName");
value.put("new", "Name");
PatchRequest patchRequest = new PatchRequest();
patchRequest.setScript("var firstName = this[args.rename.old];" +
"delete this[args.rename.old];" +
"this[args.rename.new] = firstName;");
patchRequest.setValues(Collections.singletonMap("rename", value));
store.operations().send(new PatchOperation("employees/1", null, patchRequest));
Add Document
Adding a new document is supported only by the non-typed APIs
PatchRequest patchRequest = new PatchRequest();
patchRequest.setScript("put('orders/', { Employee: id(this) });");
PatchCommandData commandData =
new PatchCommandData("employees/1-A", null, patchRequest, null);
session.advanced().defer(commandData);
session.saveChanges();
PatchRequest patchRequest = new PatchRequest();
patchRequest.setScript("put('orders/', { Employee: id(this) });");
store.operations().send(new PatchOperation("employees/1-A", null, patchRequest));
Clone Document
In order to clone a document use put method as follows
PatchRequest patchRequest = new PatchRequest();
patchRequest.setScript("put('employees/', this);");
PatchCommandData commandData =
new PatchCommandData("employees/1-A", null, patchRequest, null);
session.advanced().defer(commandData);
session.saveChanges();
PatchRequest patchRequest = new PatchRequest();
patchRequest.setScript("put('employees/', this);");
store.operations().send(new PatchOperation("employees/1-A", null, patchRequest));
Cloning & Attachments
The attachments from source document will not be copied to the new one automatically.