Skip to main content

Single-Document Patching Examples: Advanced Scripts

Patching examples

Patching using inline string compilation

  • JavaScript patches submitted through Session.Advanced.Defer or the Operations API can compile and execute code stored in a string by using new Function(...) or eval(...).

  • String compilation is disabled by default.
    To enable it, set the Patching.AllowStringCompilation configuration key to true.

  • Compile only strings controlled by your application. Do not build compiled code from untrusted input.

session.Advanced.Defer(new PatchCommandData(
id: "products/1-A",
changeVector: null,
patch: new PatchRequest
{
Script = @"
// Build the function body as a string.
const functionBody =
'return doc.UnitsInStock < lowStock ' +
'? doc.PricePerUnit * discount ' +
': doc.PricePerUnit;';

// Compile the string, then execute the generated function.
const calculatePrice = new Function(
'doc',
'lowStock',
'discount',
functionBody);

this.PricePerUnit = calculatePrice(
this,
args.LowStock,
args.Discount);",

Values =
{
{ "LowStock", 10 },
{ "Discount", 0.8 }
}
},
patchIfMissing: null));

session.SaveChanges();

Generate a random UUID

Use crypto.randomUUID() to generate a UUID v4 string.
The following patch sets ExternalId only when the field has no value, so running the patch again preserves an existing ID.

For the full list of supported cryptographic methods, see JavaScript Engine - Cryptographic methods.

var patchRequest = new PatchRequest
{
Script = @"
// Preserve an existing ExternalId; generate one only when it is not set.
if (!this.ExternalId) {
this.ExternalId = crypto.randomUUID();
}"
};

session.Advanced.Defer(new PatchCommandData(
id: "companies/1-A",
changeVector: null,
patch: patchRequest,
patchIfMissing: null));

session.SaveChanges();

Store a SHA-256 hash of a field

Use crypto.digest() to compute a SHA-256 hash of a normalized email address and store the Base64-encoded result in a separate field.
In this example, trimming and lowercasing ensure that values differing only in casing or surrounding whitespace produce the same hash.

A deterministic, unsalted hash is not suitable for storing passwords or as a substitute for encrypting confidential values.

For the full list of supported cryptographic methods, see JavaScript Engine - Cryptographic methods.

var patchRequest = new PatchRequest
{
Script = @"
if (this.Email) {
// Normalize the value so casing and surrounding whitespace do not affect the hash.
const normalizedEmail = this.Email.trim().toLowerCase();

// crypto.digest returns the hash as a Base64-encoded string.
this.EmailHash = crypto.digest('SHA-256', normalizedEmail);
this.EmailHashAlgorithm = 'SHA-256';
}"
};

session.Advanced.Defer(new PatchCommandData(
id: "users/1",
changeVector: null,
patch: patchRequest,
patchIfMissing: null));

session.SaveChanges();

Encrypt a field

Use crypto.encryptAesGcm() to encrypt a field value. The method returns the encrypted value as a Base64-encoded string.

Decrypting the value later requires the encrypted value, the IV, and the same encryption key.
Generate a new 12-byte IV for each encryption and store it alongside the encrypted value. Load the encryption key from a secure location and do not store it in the document.

For illustration, the following code reads a Base64-encoded 256-bit AES key from an environment variable. Use your application's secret-management solution in production.

For the full list of supported cryptographic methods, see JavaScript Engine - Cryptographic methods.

var encryptionKey = Environment.GetEnvironmentVariable("PATCH_ENCRYPTION_KEY")
?? throw new InvalidOperationException(
"PATCH_ENCRYPTION_KEY must contain a Base64-encoded 256-bit AES key.");

var patchRequest = new PatchRequest
{
Script = @"
if (this.SensitiveData != null) {
// Generate a fresh 96-bit IV for this encryption.
const iv = crypto.getRandomValuesBase64(12);

// The key comes from PatchRequest.Values; do not store it in the document.
this.SensitiveDataEncrypted = crypto.encryptAesGcm(
iv,
args.EncryptionKey,
this.SensitiveData);

this.SensitiveDataIv = iv;

// Remove the plaintext only after encryption succeeds.
delete this.SensitiveData;
}",

Values =
{
{ "EncryptionKey", encryptionKey }
}
};

session.Advanced.Defer(new PatchCommandData(
id: "users/1",
changeVector: null,
patch: patchRequest,
patchIfMissing: null));

session.SaveChanges();

In this article