Can I use couchbase mutateIn and MutateInSpec.arrayAppend for update sub documents in Eventing or is there another way?
Eventing doesn’t work on subdocuments it’s API is not as rich as the other SDKs (as in Node or Java).
For more information refer to Basic Bucket Accessors in Language Constructs | Couchbase Docs and also Advanced Bucket Accessors Advanced Bucket Accessors | Couchbase Docs this is the sum total of the SDK in the Eventing Service to interact direclty with KV.
However when you consider how Eventing works it already has a copy of the JSON document when the handler is invoked. It gets this data straight from DCP you can either use Basic Bucket Accessors and manipulate exposed KV maps, Advanced Bucket Accessors and utilize advanced CAS and TTL logic, or use N1QL to interact with your data.
Consider this function that converts and XML property and adds a new property of type JSON
function parseXmlToJson(xml) {
const json = {};
for (const res of xml.matchAll(/(?:<(\w*)(?:\s[^>]*)*>)((?:(?!<\1).)*)(?:<\/\1>)|<(\w*)(?:\s*)*\/>/gm)) {
const key = res[1] || res[3];
const value = res[2] && parseXmlToJson(res[2]);
json[key] = ((value && Object.keys(value).length) ? value : res[2]) || null;
}
return json;
}
function OnUpdate(doc, meta) {
// filter out non XML
if (!meta.id.startsWith("xml:")) return;
// The KEY started with "xml" try to process it
// ===========================================================
// *** Do other work required here on non .in_xml changes ***
// ===========================================================
// let's see if we need to re-create our json representation.
var xmlchksum = crc64(doc.in_xml);
// ===========================================================
// Don't reprocess if the doc.in_xml has not changed this could be
// a big performance win if the doc has other fields that mutate.
// We do this via a checksum of the .in_xml property.
if (doc.xmlchksum && doc.xmlchksum === xmlchksum) return;
// Either this is the first pass, or the .in_xml property changed.
var jsonDoc = parseXmlToJson(doc.in_xml);
log(meta.id,"1. INPUT xml doc.in_xml :", doc.in_xml);
log(meta.id,"2. CHECKSUM doc.in_xml :", xmlchksum);
log(meta.id,"3. OUTPUT doc.out_json :", jsonDoc);
doc.out_json = jsonDoc;
doc.xmlchksum = xmlchksum;
// ===========================================================
// enrich the source bucket with .out_json and .xmlchksum
src_bkt[meta.id] = doc;
}
Now give it an input document with a KEY xml::1
{
"type": "xml",
"id": 1,
"in_xml": "<CD><TITLE>EmpireBurlesque</TITLE><ARTIST>BobDylan</ARTIST><COUNTRY>USA</COUNTRY><COMPANY>Columbia</COMPANY><PRICE>10.90</PRICE><YEAR>1985</YEAR></CD>"
}
You get an output document as follows
{
"type": "xml",
"id": 2,
"in_xml": "<CD><TITLE>EmpireBurlesque</TITLE><ARTIST>BobDylan</ARTIST><COUNTRY>USA</COUNTRY><COMPANY>Columbia</COMPANY><PRICE>10.90</PRICE><YEAR>1985</YEAR></CD>",
"out_json": {
"CD": {
"TITLE": "EmpireBurlesque",
"ARTIST": "BobDylan",
"COUNTRY": "USA",
"COMPANY": "Columbia",
"PRICE": "10.90",
"YEAR": "1985"
}
},
"xmlchksum": "02087b7be275d0d8"
}
Did we do a subdocument mutation no, did we do the equivalent yes (by overwriting the full document when we want to make a change).
The OnUpdate(doc, meta) entry point received the entire document ‘doc’ we either manipulate it and write the full document back to the Data Service or we don’t do anything at all if the KEY has the wrong prefix or the crc64() or the XML input property hasn’t changed via checking the “xmlchksum” property we also add. It is important to note ‘doc’ came from the firehose that is DCP and it is essentially free to analyze without any further KV operations needed.
Now back to answer your question you can do array appends by using CAS with retry logic via that Advanced Bucket Accessors. Look at couchbase.insert (for the initial insert) and couchbase.replace (for the append operation).
@Lenty_Me we added native subdoc operations support into Couchbase in the 7.6 release Advanced Keyspace Accessors | Couchbase Docs