Set-Based Patch Operations
-
Set-based patch operations allow you to apply changes to a set of documents that match specific criteria instead of separately targeting each document.
-
To perform patch operations on a single document see Single Document Patch Operations.
Set-based patching can also be done from the Studio. -
In this page:
Overview
-
In other databases, a simple SQL query that updates a set of documents can look like this:
UPDATE Users SET IsActive = 0 WHERE LastLogin < '2020-01-01'
-
To achieve that in RavenDB, define the following two components within a
PatchByQueryOperation
:-
The query:
An RQL query that defines the set of documents to update.
Use the exact same syntax as you would when querying the database/indexes for usual data retrieval. -
The update:
A JavaScript clause that defines the updates to perform on the documents resulting from the query.
-
-
When sending the
PatchByQueryOperation
to the server, the server will run the query and perform the requested update on the query results.// A "query & update" sample // Update the set of documents from the Orders collection that match the query criteria: // ===================================================================================== // The RQL part: from Orders where Freight < 10 // The UPDATE part: update { this.Freight += 10; }
-
Transactional batches:
The patching of documents matching a specified query is run in batches of size 1024.
Each batch is handled in a separate write transaction. -
Dynamic behavior:
During the patching process, documents that are added/modified after the patching operation has started
may also be patched if they match the query criteria. -
Concurrency:
RavenDB doesn't perform concurrency checks during the patching process so it can happen that a document
has been modified or deleted while patching is in progress. -
Patching stale indexes:
By default, set-based patch operations will only succeed if the index is Not stale.
For indexes that are frequently updated, you can explicitly allow patching on stale results if needed.
An example can be seen in the Allow updating stale results example. -
Manage lengthy patch operations:
The set-based patch operation (PatchByQueryOperation
) runs in the server background may take a long time to complete.
Executing the operation via theSend
method return an object that can be awaited for completion or aborted (killed). Learn more about this and see dedicated examples in Manage length operations.
Examples
// Update all documents in a collection
// ====================================
// Define the Patch by Query Operation, pass the "query & update" string:
const patchByQueryOp = new PatchByQueryOperation(
`from Orders as o
update
{
// Increase the Freight in ALL documents in the Orders collection:
o.Freight += 10;
}`);
// Execute the operation by passing it to operations.send:
const operation = await documentStore.operations.send(patchByQueryOp);
Update by collection query - access metadata:
// Update the collection name for all documents in the collection
// ==============================================================
// Delete the document before recreating it with a different collection name:
const patchByQueryOp = new PatchByQueryOperation(
`from Orders as c
update
{
del(id(c));
this["@metadata"]["@collection"] = "New_Orders";
put(id(c), this);
}`);
const operation = await documentStore.operations.send(patchByQueryOp);
// Update all documents matching a dynamic query
// =============================================
// Update the Discount in all orders that match the dynamic query predicate:
const patchByQueryOp = new PatchByQueryOperation(`from Orders as o
where o.Employee = 'employees/4-A'
update
{
o.Lines.forEach(line=> line.Discount = 0.3);
}`);
const operation = await documentStore.operations.send(patchByQueryOp);
// Note: An AUTO-INDEX will be created when the dynamic query is executed on the server.
// Update all documents matching a static index query
// ==================================================
// Modify the Supplier to 'suppliers/13-A' for all products that have 'suppliers/12-A':
const patchByQueryOp = new PatchByQueryOperation(`from index 'Products/BySupplier' as p
where p.Supplier = 'suppliers/12-A'
update
{
p.Supplier = 'suppliers/13-A'
}`);
const operation = await documentStore.operations.send(patchByQueryOp);
class Products_BySupplier extends AbstractJavaScriptIndexCreationTask {
constructor() {
super();
// Define the index-fields
this.map("Products", p => ({
Supplier : e.Supplier
}));
}
}
// Update all documents matching an @all_docs query
// ================================================
// Patch the 'Updated' field to ALL documents (query is using the @all_docs keyword):
const patchByQueryOp = new PatchByQueryOperation(`from @all_docs
update
{
this.Updated = true;
}`);
const operation = await documentStore.operations.send(patchByQueryOp);
// Update all documents matching a query by ID
// ===========================================
// Patch the 'Updated' field to all documents that have the specified IDs:
const patchByQueryOp = new PatchByQueryOperation(`from @all_docs as d
where id() in ('orders/1-A', 'companies/1-A')
update
{
d.Updated = true;
}`);
const operation = await documentStore.operations.send(patchByQueryOp);
Update by document ID using parameters:
// Update all documents matching a query by ID using query parmeters
// =================================================================
// Define an IndexQuery object:
const indexQuery = new IndexQuery();
// Define the "query & update" string
// Patch the 'Updated' field to all documents that have the specified IDs
// Parameter ($ids) contains the listed IDs:
indexQuery.query = `from @all_docs as d
where id() in ($ids)
update {
d.Updated = true
}`;
// Define the parameters for the script:
indexQuery.queryParameters = {
ids: ["orders/830-A", "companies/91-A"]
};
// Pass the indexQuery to the operation definition
const patchByQueryOp = new PatchByQueryOperation(indexQuery);
// Execute the operation
const operation = await documentStore.operations.send(patchByQueryOp);
-
Set
allowStale
to true to allow patching of stale results. -
The RQL in this example is using an auto-index.
Use allowStale in exactly the same way when querying a static-index.
// Update documents matching a dynamic query even if auot-index is stale
// =====================================================================
// Define an IndexQuery object:
const indexQuery = new IndexQuery();
// Define the "query & update" string
// Modify company to 'companies/13-A' for all orders that have 'companies/12-A':
indexQuery.query = `from Orders as o
where o.Company = 'companies/12-A'
update
{
o.Company = 'companies/13-A'
}`;
// Define query options:
const queryOptions = {
// The query uses an auto-index (index is created if it doesn't exist yet).
// Allow patching on all matching documents even if the auto-index is still stale.
allowStale: true
};
// Pass indexQuery & queryOptions to the operation definition
const patchByQueryOp = new PatchByQueryOperation(indexQuery, queryOptions);
// Execute the operation
const operation = await documentStore.operations.send(patchByQueryOp);
Syntax
Send syntax
await send(operation);
Parameter | Type | Description |
---|---|---|
operation | PatchByQueryOperation |
The operation object describing the query and the patch to perform. |
Return value | |
---|---|
Promise<OperationCompletionAwaiter> |
A promise that resolves to an object that allows waiting for the operation to complete. |
PatchByQueryOperation syntax
// Available overload:
// ===================
patchByQueryOp = new PatchByQueryOperation(queryToUpdate);
patchByQueryOp = new PatchByQueryOperation(queryToUpdate, options);
Parameter | Type | Description | |
---|---|---|---|
queryToUpdate | string |
The query & patch definition. The RQL query starts as any other RQL query with a "from" statement. It continues with an "update" clause that contains the Javascript patching code. |
|
queryToUpdate | IndexQuery |
Object containing the query & the patching string, with the option to use parameters. |
|
options | object |
Options for the PatchByQueryOperation. |
class IndexQuery {
query; // string
queryParameters; // Record<string, object>
}
// Options for 'PatchByQueryOperation'
{
// Limit the amount of base operation per second allowed.
maxOpsPerSecond; // number
// Indicate whether operations are allowed on stale indexes.
allowStale; // boolean
// If AllowStale is set to false and index is stale,
// then this is the maximum timeout to wait for index to become non-stale.
// If timeout is exceeded then exception is thrown.
staleTimeout; // number
// Set whether operation details about each document should be returned by server.
retrieveDetails; // boolean
}