You are currently browsing legacy 4.1 version of documentation. Click here to switch to the newest 5.1 version.
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.
The current page deals with patch operations on single documents.
Patching has three possible interfaces: Typed Session API, Non-Typed Session API, and Operations API.
In this page:
API overview
Examples
API Overview
Typed Session API
A type safe 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
void Increment<T, U>(T entity, Expression<Func<T, U>> fieldPath, U delta);
void Increment<T, U>(string id, Expression<Func<T, U>> fieldPath, U delta);
Parameters | ||
---|---|---|
T | Type |
Entity type |
U | Type |
Field type, 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. |
fieldPath | Expression<Func<T, U>> |
Lambda describing the path to the field. |
delta | U |
Value to be added. |
- Note the numeric values restrictions in JavaScript
Set Field Value
Session.Advanced.Patch
void Patch<T, U>(string id, Expression<Func<T, U>> fieldPath, U value);
void Patch<T, U>(T entity, Expression<Func<T, U>> fieldPath, U value);
Parameters | ||
---|---|---|
T | Type |
Entity type |
U | Type |
Field type |
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. |
fieldPath | Expression<Func<T, U>> |
Lambda describing the path to the field. |
delta | U |
Value to set. |
Array Manipulation
Session.Advanced.Patch
void Patch<T, U>(T entity, Expression<Func<T, IEnumerable<U>>> fieldPath,
Expression<Func<JavaScriptArray<U>, object>> arrayMofificationLambda);
void Patch<T, U>(string id, Expression<Func<T, IEnumerable<U>>> fieldPath,
Expression<Func<JavaScriptArray<U>, object>> arrayMofificationLambda);
Parameters | ||
---|---|---|
T | Type |
Entity type |
U | Type |
Field type |
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. |
fieldPath | Expression<Func<T, U>> |
Lambda describing the path to the field. |
arrayMofificationLambda | Expression<Func<JavaScriptArray<U>, object>> |
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(params T[] items) | JavaScriptArray |
Items to be added to the array. |
RemoveAt(int index) | JavaScriptArray |
Removes item in position index in array. |
RemoveAll(Func<T, bool> predicate) | JavaScriptArray |
Removes all the items in the array that satisfy the given predicate. |
Non-Typed Session API
Session.Advanced.Defer
function that allows registering single or several 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 | Dictionary<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
Raven.Client.Documents.Operations.Send
Raven.Client.Documents.Operations.SendAsync
PatchStatus Send(PatchOperation operation);
Task<PatchStatus> SendAsync(PatchOperation operation, SessionInfo sessionInfo = null, CancellationToken token = default(CancellationToken));
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 | bool |
If false and changeVector has value, and document with that ID and change vector was not found, will throw exception. |
Examples
Change Field's Value
// change FirstName to Robert
session.Advanced.Patch<Employee, string>(
"employees/1",
x => x.FirstName, "Robert");
session.SaveChanges();
// change FirstName to Robert
session.Advanced.Defer(new PatchCommandData(
id: "employees/1",
changeVector: null,
patch: new PatchRequest
{
Script = @"this.FirstName = args.FirstName;",
Values =
{
{"FirstName", "Robert"}
}
},
patchIfMissing: null));
session.SaveChanges();
// change FirstName to Robert
store.Operations.Send(new PatchOperation(
id: "employees/1",
changeVector: null,
patch: new PatchRequest
{
Script = @"this.FirstName = args.FirstName;",
Values =
{
{"FirstName", "Robert"}
}
},
patchIfMissing: null));
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 seperate batch operations
// in order to achieve atomicity, please use the non generic APIs
session.Advanced.Patch<Employee, string>("employees/1", x => x.FirstName, "Robert");
session.Advanced.Patch<Employee, string>("employees/1", x => x.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
session.Advanced.Defer(new PatchCommandData(
id: "employees/1",
changeVector: null,
patch: new PatchRequest
{
Script = @"
this.FirstName = args.UserName.FirstName
this.LastName = args.UserName.LastName",
Values =
{
{
"UserName", new
{
FirstName = "Robert",
LastName = "Carter"
}
}
}
},
patchIfMissing: 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
store.Operations.Send(new PatchOperation(
id: "employees/1",
changeVector: null,
patch: new PatchRequest
{
Script = @"
this.FirstName = args.UserName.FirstName
this.LastName = args.UserName.LastName",
Values =
{
{
"UserName", new
{
FirstName = "Robert",
LastName = "Carter"
}
}
}
}, patchIfMissing: null));
Increment Value
// increment UnitsInStock property value by 10
session.Advanced.Increment<Product, int>("products/1-A", x => x.UnitsInStock, 10);
session.SaveChanges();
session.Advanced.Defer(new PatchCommandData(
id: "products/1-A",
changeVector: null,
patch: new PatchRequest
{
Script = @"this.UnitsInStock += args.UnitsToAdd",
Values =
{
{"UnitsToAdd", 10}
}
},
patchIfMissing: null));
session.SaveChanges();
store.Operations.Send(new PatchOperation(
id: "products/1-A",
changeVector: null,
patch: new PatchRequest
{
Script = @"this.UnitsInStock += args.UnitsToAdd",
Values =
{
{"UnitsToAdd", 10}
}
},
patchIfMissing: null));
Add Item to Array
// add a new comment to Comments
session.Advanced.Patch<BlogPost, BlogComment>("blogposts/1",
x => x.Comments,
comments => comments.Add(new BlogComment
{
Content = "Lore ipsum",
Title = "Some title"
}));
session.SaveChanges();
// add a new comment to Comments
session.Advanced.Defer(new PatchCommandData(
id: "blogposts/1",
changeVector: null,
patch: new PatchRequest
{
Script = "this.Comments.push(args.Comment)",
Values =
{
{
"Comment", new BlogComment
{
Content = "Lore ipsum",
Title = "Some title"
}
}
}
},
patchIfMissing: null));
session.SaveChanges();
// add a new comment to Comments
store.Operations.Send(new PatchOperation(
id: "blogposts/1",
changeVector: null,
patch: new PatchRequest
{
Script = "this.Comments.push(args.Comment)",
Values =
{
{
"Comment", new BlogComment
{
Content = "Lore ipsum",
Title = "Some title"
}
}
}
},
patchIfMissing: null));
Insert Item into Specific Position in Array
Inserting item into specific position is supported only by the non-typed APIs
// insert a new comment at position 1 to Comments
session.Advanced.Defer(new PatchCommandData(
id: "blogposts/1",
changeVector: null,
patch: new PatchRequest
{
Script = "this.Comments.splice(1,0,args.Comment)",
Values =
{
{
"Comment", new BlogComment
{
Content = "Lore ipsum",
Title = "Some title"
}
}
}
},
patchIfMissing: null));
session.SaveChanges();
store.Operations.Send(new PatchOperation(
id: "blogposts/1",
changeVector: null,
patch: new PatchRequest
{
Script = "this.Comments.splice(1,0,args.Comment)",
Values =
{
{
"Comment", new BlogComment
{
Content = "Lore ipsum",
Title = "Some title"
}
}
}
},
patchIfMissing: null));
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
session.Advanced.Defer(new PatchCommandData(
id: "blogposts/1",
changeVector: null,
patch: new PatchRequest
{
Script = "this.Comments.splice(3,1,args.Comment)",
Values =
{
{
"Comment", new BlogComment
{
Content = "Lore ipsum",
Title = "Some title"
}
}
}
},
patchIfMissing: null));
session.SaveChanges();
// modify a comment at position 3 in Comments
store.Operations.Send(new PatchOperation(
id: "blogposts/1",
changeVector: null,
patch: new PatchRequest
{
Script = "this.Comments.splice(1,0,args.Comment)",
Values =
{
{
"Comment", new BlogComment
{
Content = "Lore ipsum",
Title = "Some title"
}
}
}
},
patchIfMissing: null));
Filter out Items from an Array
// filter out all comments of a blogpost which contains the word "wrong" in their contents
session.Advanced.Patch<BlogPost, BlogComment>("blogposts/1",
x => x.Comments,
comments => comments.RemoveAll(y => y.Content.Contains("wrong")));
session.SaveChanges();
// filter out all comments of a blogpost which contains the word "wrong" in their contents
session.Advanced.Defer(new PatchCommandData(
id: "blogposts/1",
changeVector: null,
patch: new PatchRequest
{
Script = @"this.Comments = this.Comments.filter(comment=>
comment.Content.includes(args.TitleToRemove));",
Values =
{
{"TitleToRemove", "wrong"}
}
},
patchIfMissing: null));
session.SaveChanges();
// filter out all comments of a blogpost which contains the word "wrong" in their contents
store.Operations.Send(new PatchOperation(
id: "blogposts/1",
changeVector: null,
patch: new PatchRequest
{
Script = @"this.Comments = this.Comments.filter(comment=>
comment.Content.includes(args.TitleToRemove));",
Values =
{
{"TitleToRemove", "wrong"}
}
},
patchIfMissing: null));
Loading Documents in a Script
Loading documents supported only by non-typed APIs
// update product names in order, according to loaded product documents
session.Advanced.Defer(new PatchCommandData(
id: "orders/1",
changeVector: null,
patch: new PatchRequest
{
Script = @"this.Lines.forEach(line=> {
var productDoc = load(line.Product);
line.ProductName = productDoc.Name;
});"
}, patchIfMissing: null));
session.SaveChanges();
// update product names in order, according to loaded product documents
store.Operations.Send(new PatchOperation(
id: "blogposts/1",
changeVector: null,
patch: new PatchRequest
{
Script = @"this.Lines.forEach(line=> {
var productDoc = load(line.Product);
line.ProductName = productDoc.Name;
});"
},
patchIfMissing: null));
Remove Property
Removing property supported only by the non-typed APIs
// remove property Age
session.Advanced.Defer(new PatchCommandData(
id: "employees/1",
changeVector: null,
patch: new PatchRequest
{
Script = @"delete this.Age"
},
patchIfMissing: null));
session.SaveChanges();
// remove property Age
store.Operations.Send(new PatchOperation(
id: "employees/1",
changeVector: null,
patch: new PatchRequest
{
Script = @"delete this.Age"
},
patchIfMissing: null));
Rename Property
Renaming property supported only by the non-typed APIs
// rename FirstName to First
session.Advanced.Defer(new PatchCommandData(
id: "employees/1",
changeVector: null,
patch: new PatchRequest
{
Script = @" var firstName = this[args.Rename.Old];
delete this[args.Rename.Old];
this[args.Rename.New] = firstName",
Values =
{
{
"Rename", new
{
Old = "FirstName",
New = "Name"
}
}
}
},
patchIfMissing: null));
session.SaveChanges();
store.Operations.Send(new PatchOperation(
id: "employees/1",
changeVector: null,
patch: new PatchRequest
{
Script = @" var firstName = this[args.Rename.Old];
delete this[args.Rename.Old];
this[args.Rename.New] = firstName",
Values =
{
{
"Rename", new
{
Old = "FirstName",
New = "Name"
}
}
}
},
patchIfMissing: null));
Add Document
Adding a new document is supported only by the non-typed APIs
session.Advanced.Defer(new PatchCommandData("employees/1-A", null,
new PatchRequest
{
Script = "put('orders/', { Employee: id(this) });",
}, null));
session.SaveChanges();
store.Operations.Send(new PatchOperation("employees/1-A", null, new PatchRequest
{
Script = "put('orders/', { Employee: id(this) });",
}));
Clone Document
In order to clone a document use put method as follows
session.Advanced.Defer(new PatchCommandData("employees/1-A", null,
new PatchRequest
{
Script = "put('employees/', this);",
}, null));
session.SaveChanges();
store.Operations.Send(new PatchOperation("employees/1-A", null, new PatchRequest
{
Script = "put('employees/', this);",
}));
Cloning, Attachments & Counters
The attachments and/or counters from source document will not be copied to the new one automatically.
Increment Counter
In order to increment or create a counter use incrementCounter
method as follows
var order = session.Load<Order>("orders/1-A");
session.CountersFor(order).Increment("Likes", 1);
session.SaveChanges();
session.Advanced.Defer(new PatchCommandData("orders/1-A", null,
new PatchRequest
{
Script = "incrementCounter(this.Company, args.name, args.val);",
Values =
{
{ "name", "Likes" },
{ "val", 20 }
}
}, null));
session.SaveChanges();
store.Operations.Send(new PatchOperation("orders/1-A", null, new PatchRequest
{
Script = "incrementCounter(this.Company, args.name, args.val);",
Values =
{
{ "name", "Likes" },
{ "val", -1 }
}
}));
Method Overloading & Value restrictions
The method can be called by document ID or by document reference and the value can be negative
Delete Counter
In order to delete a counter use deleteCounter
method as follows
session.CountersFor("orders/1-A").Delete("Likes");
session.SaveChanges();
session.Advanced.Defer(new PatchCommandData("products/1-A", null,
new PatchRequest
{
Script = "deleteCounter(this, args.name);",
Values =
{
{ "name", "Likes" },
}
}, null));
session.SaveChanges();
store.Operations.Send(new PatchOperation("products/1-A", null, new PatchRequest
{
Script = "deleteCounter(this, args.name);",
Values =
{
{ "name", "Likes" },
}
}));
Method Overloading
The method can be called by document ID or by document reference
Get Counter
In order to get a counter while patching use counter
method as follows
var order = session.Load<Order>("orders/1-A");
var counters = session.Advanced.GetCountersFor(order);
session.Advanced.Defer(new PatchCommandData("orders/1-A", null,
new PatchRequest
{
Script = @"var likes = counter(this.Company, args.name);
put('result/', {company: this.Company, likes: likes});",
Values =
{
{ "name", "Likes" },
}
}, null));
session.SaveChanges();
store.Operations.Send(new PatchOperation("orders/1-A", null, new PatchRequest
{
Script = @"var likes = counter(this.Company, args.name);
put('result/', {company: this.Company, likes: likes});",
Values =
{
{ "name", "Likes" },
}
}));
Method Overloading
The method can be called by document ID or by document reference