Skip to main content

Patching: How to Perform Single Document Patch Operations

  • The Patch operation is used to perform partial document updates with one trip to the server, instead of loading, modifying, and saving 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.

  • Since the operation is executed in a single request to the database,
    the patch command is performed in a single write transaction.

  • The current page covers patch operations on single documents.

  • Patching has three possible interfaces: Typed Session API, Non-Typed Session API, and Operations API.

  • Patching can be done from the client API as well as in the studio.

In this page:

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 the 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);
ParametersTypeDescription
TTypeEntity type
UTypeField type, must be of numeric type or a string of char for string concatenation
entityTEntity 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 idstringEntity ID on which the operation should be performed.
fieldPathExpression<Func<T, U>>Lambda describing the path to the field.
deltaUValue to be added.
  • Note the numeric values restrictions in JavaScript Session.Advanced.AddOrIncrement
void AddOrIncrement<T, TU>(string id, T entity, Expression<Func<T, TU>> path, TU valToAdd);
ParametersTypeDescription
TTypeEntity type
TUTypeField type, must be of numeric type or a string of char for string concatenation
entityTEntity 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 idstringEntity ID on which the operation should be performed.
pathExpression<Func<T, TU>>Lambda describing the path to the field.
valToAddUValue to be added.

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);
ParametersTypeDescription
TTypeEntity type
UTypeField type
entityTEntity 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 idstringEntity ID on which the operation should be performed.
fieldPathExpression<Func<T, U>>Lambda describing the path to the field.
deltaUValue to set.
Session.Advanced.AddOrPatch
void AddOrPatch<T, TU>(string id, T entity, Expression<Func<T, TU>> path, TU value);
ParametersTypeDescription
TTypeEntity type
TUTypeField type
entityTEntity 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 idstringEntity ID on which the operation should be performed.
fieldPathExpression<Func<T, TU>>Lambda describing the path to the field.
valueUValue to set.

Array Manipulation

Session.Advanced.Patch

void Patch<T, U>(T entity, Expression<Func<T, IEnumerable<U>>> fieldPath,
Expression<Func<JavaScriptArray<U>, object>> arrayModificationLambda);

void Patch<T, U>(string id, Expression<Func<T, IEnumerable<U>>> fieldPath,
Expression<Func<JavaScriptArray<U>, object>> arrayModificationLambda);
ParametersTypeDescription
TTypeEntity type
UTypeField type
entityTEntity 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 idstringEntity ID on which the operation should be performed.
fieldPathExpression<Func<T, U>>Lambda describing the path to the field.
arrayMofificationLambdaExpression<Func<JavaScriptArray<U>, object>>Lambda that modifies the array, see JavaScriptArray below.
Session.Advanced.AddOrPatch
void AddOrPatch<T, TU>(string id, T entity, Expression<Func<T, List<TU>>> path, 
Expression<Func<JavaScriptArray<TU>, object>> arrayAdder);
ParametersTypeDescription
TTypeEntity type
TUTypeField type
entityTEntity 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 idstringEntity ID on which the operation should be performed.
pathExpression<Func<T, TU>>Lambda describing the path to the field.
Expression<Func<JavaScriptArray>Expression<Func<JavaScriptArray<TU>, object>>Lambda that modifies the array, see JavaScriptArray below.
arrayAdderAdd()Values to add to array.

JavaScriptArray allows building lambdas representing array manipulations for patches.

Method SignatureReturn TypeDescription
Put(T item)JavaScriptArrayAllows adding item to an array.
Put(params T[] items)JavaScriptArrayItems to be added to the array.
RemoveAt(int index)JavaScriptArrayRemoves item in position index in array.
RemoveAll(Func<T, bool> predicate)JavaScriptArrayRemoves all the items in the array that satisfy the given predicate.

Non-Typed Session API

The non-typed Session API for patches uses the Session.Advanced.Defer function which 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 the 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);
ConstructorTypeDescription
idstringID of the document to be patched.
changeVectorstring[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.
patchPatchRequestPatch request to be performed on the document.
patchIfMissingPatchRequest[Can be null] Patch request to be performed if no document with the given ID was found.

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.

MembersTypeDescription
ScriptstringJavaScript code to be run.
ValuesDictionary<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

An operations interface that exposes the full functionality and allows performing ad-hoc patch operations without creating a session.

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));
ConstructorTypeDescription
idstringID of the document to be patched.
changeVectorstring[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.
patchPatchRequestPatch request to be performed on the document.
patchIfMissingPatchRequest[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.
skipPatchIfChangeVectorMismatchboolIf false and changeVector has value, and document with that ID and change vector was not found, will throw an 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.

MethodArgumentsDescription
loadstring or string[]Loads one or more documents into the context of the script by their document IDs
loadPathA document and a path to an ID within that documentLoads a related document by the path to its ID
delDocument ID; change vectorDelete 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.
putDocument ID; document; change vectorCreate 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.
cmpxchgKeyLoad a compare exchange value into the context of the script using its key
getMetadataDocumentReturns the document's metadata
idDocumentReturns the document's ID
lastModifiedDocumentReturns the DateTime of the most recent modification made to the given document
counterDocument; counter nameReturns the value of the specified counter in the specified document
counterRawDocument; counter nameReturns the specified counter in the specified document as a key-value pair
incrementCounterDocument; counter nameIncreases the value of the counter by one
deleteCounterDocument; counter nameDeletes the counter
spatial.distanceTwo points by latitude and longitude; spatial unitsFind the distance between to points on the earth
timeseriesDocument; the time series' nameReturns the specified time series object

Examples

Change Field's Value

// change FirstName to Robert                
session.Advanced.Patch<Employee, string>(
"employees/1",
x => x.FirstName, "Robert");

session.SaveChanges();

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();

Increment Value

// increment UnitsInStock property value by 10
session.Advanced.Increment<Product, int>("products/1-A", x => x.UnitsInStock, 10);

session.SaveChanges();

Add or Increment

AddOrIncrement increments an existing field or adds a new one in documents where they didn't exist.

// While running AddOrIncrement specify <entity type, field type>
session.Advanced.AddOrIncrement<User, int>(

// Specify document id and entity on which the operation should be performed.
id,
new User
{
FirstName = "John",
LastName = "Doe",
LoginCount = 1

// The path to the field and value to be added.
}, x => x.LoginCount, 1);

session.SaveChanges();

Add or Patch

AddOrPatch adds or edits field(s) in a single document.

If the document doesn't yet exist, this operation adds the document but doesn't patch it.

// While running AddOrPatch specify <entity type, field type>
session.Advanced.AddOrPatch<User, DateTime>(

// Specify document id and entity on which the operation should be performed.
id,
new User
{
FirstName = "John",
LastName = "Doe",
LastLogin = DateTime.Now
},
// The path to the field and value to set.
x => x.LastLogin, new DateTime(2021, 9, 12));

session.SaveChanges();

Add or Patch to an Existing Array

This sample shows how to patch an existing array or add it to documents where it doesn't yet exist.

// While running AddOrPatch specify <entity type, field type>
session.Advanced.AddOrPatch<User, DateTime>(

// Specify document id and entity on which the operation should be performed.
id,
new User
{
FirstName = "John",
LastName = "Doe",
LoginTimes =
new List<DateTime>
{
DateTime.UtcNow
}
},
// The path to the field
x => x.LoginTimes,
// Modifies the array
u => u.Add(new DateTime(1993, 09, 12), new DateTime(2000, 01, 01)));

session.SaveChanges();

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();

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();

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();

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();

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();

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();

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();

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();

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();

The attachments and/or counters from the source document will not be copied to the new one automatically.

Increment Counter

In order to increment or create a counter use <code>incrementCounter</code> method as follows

var order = session.Load<Order>("orders/1-A");
session.CountersFor(order).Increment("Likes", 1);
session.SaveChanges();

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 <code>deleteCounter</code> method as follows

session.CountersFor("orders/1-A").Delete("Likes");
session.SaveChanges();

The method can be called by document ID or by document reference

Get Counter

In order to get a counter while patching use <code>counter</code> method as follows

var order = session.Load<Order>("orders/1-A");
var counters = session.Advanced.GetCountersFor(order);

The method can be called by document ID or by document reference