diff --git a/modules/eventing/pages/eventing-advanced-keyspace-accessors.adoc b/modules/eventing/pages/eventing-advanced-keyspace-accessors.adoc index 720d74a9..de7a7b19 100644 --- a/modules/eventing/pages/eventing-advanced-keyspace-accessors.adoc +++ b/modules/eventing/pages/eventing-advanced-keyspace-accessors.adoc @@ -1,100 +1,86 @@ = Advanced Keyspace Accessors -:description: pass:q[_Advanced Keyspace Accessors_ make it possible to access advanced KV functionality using the following built-in operators.] -:page-edition: Enterprise Edition +:description: Use Advanced Keyspace Accessors to access advanced Key Value functionality. :page-aliases: eventing:eventing-advanced-bucket-accessors.adoc +[abstract] {description} -They utilize the same bucket bindings defined in the handler as _Basic Keyspace Accessors_, but expose a richer set of options and operators that can be used to 1) set or retrieve expirations, 2) solve race conditions via CAS and 3) manipulate hot KV items under high contention. -NOTE: The xref:eventing-language-constructs.adoc#bucket_accessors[Basic Keyspace Accessors] are much easier to use, having a trivial API, and are also a bit faster than the corresponding Advanced Keyspace Accessors. +Advanced Keyspace Accessors use the same bucket bindings defined in the handler as xref:eventing-language-constructs.adoc#bucket_accessors[Basic Keyspace Accessors], but they expose a larger set of options and operators. +These operators can be used to: -The following Advanced Keyspace Accessors are supported: +* Set or retrieve document expirations in the Data Service +* Solve race conditions through CAS +* Manipulate KV items under high contention using JavaScript inside Eventing Functions +* Perform distributed atomic counter operations -* <> -* <> -* <> -* <> -* <> -* <> -* <> +Couchbase supports the following Advanced Keyspace Accessors: -These seven (7) Advanced Keyspace Accessors make it possible to utilize and leverage CAS directly and/or set a document expiration (or TTL) in the Data Service (or KV) via Eventing plus perform distributed atomic counter operations. - -For example rather than blindly relying on the Basic Keyspace Accessors for an upsert like operation ‘src_col[id_str] = some_doc’ the Advanced Accessors allow you to resolve contention (or possible contention) with JavaScript driven logic in your Eventing Function. - -* If the document doesn't exist you can use ‘couchbase.insert(src_col, {"id: id_str}, some_doc)’ and check the return value for success -* You can use ‘couchbase.upsert(src_col, {"id: id_str}, some_doc)’ to update a document if it already exists. -If the document doesn't exist, then a new document will be created. -Check the return value to see if the operation was successful. -* If the document exists you can use ‘couchbase.replace(src_col, {"id: id_str, "cas": current_cas}, some_doc)’ and check the return value for success or a CAS mismatch. - -To view complete examples including JavaScript, input mutations, output mutations and/or log messages for each Advanced Bucket Accessor refer to -xref:eventing-examples.adoc#examples-scriptlets-advanced-accessors[Scriptlets: Advanced Accessor Handlers] in the examples section of the documentation. - -*Functions that Listen to Multiple Collections*: may require the _meta.keyspace_ parameter - -For Eventing Functions that listen to multiple collections via a wildcard of `{asterisk}` for the scope and/or the collection, an additional parameter is required if the binding used by the Advanced Keyspace Accessor also contains a wildcard of `{asterisk}` for the scope and/or the collection. - -* ‘couchbase.insert(src_col, {"id: id_str, "keyspace":{"bucket_name": "bkt01","scope_name":"scp01","collection_name":"col01"}}, some_doc)’ - -The example above adds a _keyspace_ to the _meta_ object to specify win which keyspace the insert is to take place. -Note as of 7.1.1 the entry points of OnUpdate() and OnDelete() provide _meta.keyspace_ with every mutation. -For a detailed example refer to the multiCollectionEventing example in xref:eventing-examples.adoc#examples-scriptlets-advanced-accessors[Scriptlets: Advanced Accessor Handlers] +* <> +* <> +* <> +* <> +* <> +* <> +* <> +* <> [#advanced-get-op] +== Advanced GET Operation -*The Advanced GET operation*: _result = couchbase.get(binding, meta)_ +`result = couchbase.get(binding, meta)` -This operation allows reading a document along with metadata from the bucket and subsequent operations to utilize CAS or check/modify the expiry_date. - -Contrast this to the _Basic Bucket Accessor_ GET operation which merely exposes a JavaScript binding or map, "_var adoc = src_col[meta.id]_", where the return value is just the document without any metadata. - -Below is an example of the _Advanced GET operation_. +The GET operation lets you read a document with metadata from your bucket. +It also allows any subsequent operations to use CAS and check or modify the expiration date of the document. +==== Example +==== +.Operation [source,javascript] ---- function OnUpdate(doc, meta) { - log('input doc ', doc); + log('input doc', doc); log('input meta', meta); // could be the same or different - var new_meta = {"id":"test_adv_get::1"}; - var result = couchbase.get(src_col,new_meta); + var new_meta = {"id": "test_adv_get::1"}; + var result = couchbase.get(src_col, new_meta); if (result.success) { - log('success adv. get: result',result); + log('success adv. get: result', result); } else { - log('failure adv. get: id',new_meta.id,'result',result); + log('failure adv. get: id', new_meta.id, 'result',result); } } ---- +==== + +=== Optional `{ "cache": true }` parameter -[.status]#Couchbase Server 7.0.2# -In version 7.0.2 an optional third parameter *{"cache": true}* enables a bucket backed cache to hold the document for at most 1 second. -This cache exists on each eventing node and is shared across all Eventing Functions (or threads) on the same node. +You can use an optional third parameter `{ "cache": true }` to enable a bucket backed cache to hold the documents for one second. +This cache exists on each Eventing node and is shared across all Eventing Functions in the same node. -The performance is typically 18X-25X faster than reading data directly from the Data Service (or KV). -A common use case is to load near static data such as a lookup table from the Data Service where the external data is needed on every document mutation to drive the Eventing Function's business logic. +The cache has "read your own write" (RYOW) semantics. Writing and then reading the same document with `{ "cache": true }` always retrieves the value that has just been written. -This cache has RYOW (read your own write) semantics. -This implies that a thread of execution writing and then subsequently reading the same document with *{"cache": true}* will always retreive the value just written. +This parameter loads near static data from the Data Service, where every mutation needs external data to drive the business logic of Eventing Functions. +The performance of this operator is usually 18 to 25 times faster than reading data directly from the Data Service. +==== Example using the optional parameter `{ "cache": true }` +==== +.Operation [source,javascript] ---- function OnUpdate(doc, meta) { - log('input doc ', doc); + log('input doc', doc); log('input meta', meta); - // could be the same or different - var new_meta = {"id":"test_adv_get::1"}; - var result = couchbase.get(src_col,new_meta,{"cache": true}); + // Can be the same or different + var new_meta = {"id": "test_adv_get::1"}; + var result = couchbase.get(src_col, new_meta, {"cache": true}); if (result.success) { - log('success adv. get: result',result); + log('success adv. get: result', result); } else { - log('failure adv. get: id',new_meta.id,'result',result); + log('failure adv. get: id', new_meta.id, 'result',result); } } ---- - -Some example return values: - +.Results [source,javascript] ---- { @@ -134,111 +120,41 @@ Some example return values: "success": false } ---- -* _binding_ -+ -The name of the binding that references the target bucket. The binding can have an access level of _"read"_ or _"read/write"_. - -* _meta (type: Object)_ -+ -The positional parameter (denoted by _meta_ in the prototype above) represents the metadata of the operation. At a minimum, the document key must be specified in this object. - -** _meta.id (type: string)_ -+ -The key of the document to be used for the operation. This is a mandatory parameter and must be of JavaScript _string_ type. - -** _meta.keyspace (type: Object)_ -+ -include::partial$eventing-common.adoc[tags=keyspace-description] - -* _result – the return value (type: Object)_ -+ -The return object indicates success/failure of the operation, and the data fetched if successful, or details of the error in case of a failure. - -** _result.success (type: boolean)_ -+ -This field indicates if the operation was successful or not. It is always present in the return object. - -** _result.meta (type: Object)_ -+ -This field is present only if the operation succeeded. It contains metadata about the object that was fetched. If the specified key is not present in the bucket, the operation fails and _key_not_found_ will be set in the error object. - -*** _result.meta.id (type: string)_ -+ -The key of the document that was fetched by this operation. - -*** _result.meta.cas (type: string)_ -+ -The CAS value of the document that was fetched by this operation. - -*** _result.meta.expiry_date (type: Date)_ -+ -The expiration date of the document. If no expiration is set on the document, this field will be absent. - -*** _result.meta.datatype (type: string)_ -+ -An indicator of whther the document is "json" or "binary". [.status]#Couchbase Server 6.6.2# - -** _result.doc (type: string, number, boolean, null, Object or Array)_ -+ -If the operation is successful, this field contains the content of the requested document. - -** _result.error (type: Object)_ -+ -This field is populated only if the operation failed. The content of the error varies based on the type of error encountered, and commonly occurring fields are documented below. - -*** _result.error.key_not_found (type: boolean)_ -+ -If present and set to true, this indicates that the operation failed because the specified key did not exist in the bucket. - -*** _result.error.code (type: number)_ -+ -If present, represents the SDK error code that triggered this operation to fail. This is typically an internal numeric code. - -*** _result.error.name (type: string)_ -+ -If present, the key is a token indicating the error that SDK encountered that caused this operation to fail. - -*** _result.error.desc (type: string)_ -+ -If present, a human readable description of the error that occurred. The description is for diagnostics and logging purposes only and may change over time. No programmatic logic should be tied to specific contents from this field. - -* _exceptions_ -+ -This API indicates errors via the error object in the return value. Exceptions are thrown only during system failure conditions. +==== [#advanced-insert-op] +== Advanced INSERT Operation -*The Advanced INSERT operation*: _result = couchbase.insert(binding, meta, doc)_ - -This operation allows creating a fresh document in the bucket. This operation will fail if the document with the specified key already exists. -It allows specifying an expiration time (or TTL) to be set on the document. +`result = couchbase.insert(binding, meta, doc)` -There is no analogous _Basic Bucket Accessor_ operation to the Advanced INSERT operation (as "_src_col[meta.id] = adoc_" is more like an upsert). +The INSERT operation lets you create a new document in your bucket. +It also lets you to specify an expiration date (or TTL) for the document. -Below is an example of the _Advanced INSERT operation_. +The operation fails if the document with the key you specified already exists. +==== Example +==== +.Operation [source,javascript] ---- function OnUpdate(doc, meta) { log('input meta', meta); - log('input doc ', doc); - // could be the same or different - var new_meta = {"id":"test_adv_insert:1"}; - // optional set an expiry 60 seconds in the future + log('input doc', doc); + // Can be the same or different + var new_meta = {"id": "test_adv_insert:1"}; + // (Optional) Set an expiry time of 60 seconds in the future // new_meta.expiry_date = new Date(Date.now() + 60 * 1000); var new_doc = doc; new_doc.random = Math.random(); - var result = couchbase.insert(src_col,new_meta,new_doc); + var result = couchbase.insert(src_col, new_meta, new_doc); if (result.success) { - log('success adv. insert: result',result); + log('success adv. insert: result', result); } else { - log('failure adv. insert: id',new_meta.id,'result',result); + log('failure adv. insert: id', new_meta.id, 'result', result); } } ---- - -Some example return values: - +.Results [source,javascript] ---- { @@ -259,118 +175,43 @@ Some example return values: "success": false } ---- - -* _binding_ -+ -The name of the binding that references the target bucket. The binding must have access level of _"read/write"_. - -* _meta (type: Object)_ -+ -The positional parameter (denoted by _meta_ in the prototype above) represents the metadata of the operation. The document key must be specified in this meta object. - -** _meta.id (type: string)_ -+ -The key of the document to be used for the operation. This is a mandatory parameter and must be of JavaScript _string_ type. If a document already exists with the specified key, the operation fails and _key_already_exists_ will be set in the error object. - -** _meta.keyspace (type: Object)_ -+ -include::partial$eventing-common.adoc[tags=keyspace-description] - -** _meta.expiry_date (type: Date)_ -+ -This is an optional parameter, and if specified must be of JavaScript _Date_ object type. The document will be marked to expire at the specified time. If no _expiry_date_ is passed, no expiration will be set on the document. - -* _doc (type: any JSON serializable))_ -+ -This is the document content for the operation. This can be any JavaScript object that can be serialized to JSON (i.e., number, string, boolean, null, object and array). - -* _result – the return value (type: Object)_ -+ -The return object indicates success/failure of the operation, and the data fetched if successful, or the error details if failure. - -** _result.success (type: boolean)_ -+ -This field indicates if the operation was successful or not. It is always present in the return object. - -** _result.meta (type: Object)_ -+ -This field is present only if the operation succeeded. It contains metadata about the object that was inserted. - -*** _result.meta.id (type: string)_ -+ -The key of the document that was inserted by this operation. - -*** _result.meta.cas (type: string)_ -+ -The CAS value of the document that was created by this operation. - -*** _result.meta.expiry_date (type: Date)_ -+ -The expiration field of the document, if one was set. If no expiration is set on the document, this field will be absent. - -** _result.doc (type: string, number, boolean, null, Object or Array)_ -+ -If the operation is successful, this field contains the content of the requested document. - -** _result.error (type: Object)_ -+ -This field is populated only if the operation failed. The content of the error varies based on the type of error encountered, and commonly occurring fields are documented below. - -*** _result.error.key_already_exists (type: boolean)_ -+ -If present and set to true, this indicates that the operation failed because the specified key already existed. - -*** _result.error.code (type: number)_ -+ -If present, the code of the SDK error that triggered this operation to fail. This is typically an internal numeric code. - -*** _result.error.name (type: string)_ -+ -If present, the key is a token indicating the error that SDK encountered that caused this operation to fail. - -*** _result.error.desc (type: string)_ -+ -If present, a human readable description of the error that occurred. The description is for diagnostics and logging purposes only and may change over time. No programmatic logic should be tied to specific contents from this field. - -* _exceptions_ -+ -This API indicates errors via the error object in the return value. Exceptions are thrown only during system failure conditions. +==== [#advanced-upsert-op] +== Advanced UPSERT Operation -*The Advanced UPSERT operation*: _result = couchbase.upsert(binding, meta, doc)_ +`result = couchbase.upsert(binding, meta, doc)` -This operation allows updating an existing document in the bucket, or if absent, creating a fresh document with the specified key. -The operation does not allow specifying CAS (it will be silently ignored). -It also allows specifying an expiration time (or TTL) to be set on the document. +The UPSERT operation lets you update an existing document in your bucket. +It also lets you specify an expiration date (or TTL) for the document. -Contrast this to the _Basic Bucket Accessor_ SET operation which merely uses an exposed JavaScript map defined via a bucket binding alias "src_col[meta.id] = adoc". -For the basic SET operation there is no return value (no status and no metadata) thus no way to check the CAS value. +If no documents exist in your bucket, the operation creates a new document with the key you specified. -Below is an example of the _Advanced UPSERT operation_. +The operation does not allow you to specify CAS. +==== Example +==== +.Operation [source,javascript] ---- function OnUpdate(doc, meta) { log('input meta', meta); - log('input doc ', doc); - // could be the same or different - var new_meta = {"id":"test_adv_upsert:1"}; // CAS if supplied will be ignored - // optional set an expiry 60 seconds in the future + log('input doc', doc); + // Can be the same or different + var new_meta = {"id": "test_adv_upsert:1"}; // If supplied, the CAS is ignored + // (Optional) Set an expiry time of 60 seconds in the future // new_meta.expiry_date = new Date(Date.now() + 60 * 1000); var new_doc = doc; new_doc.random = Math.random(); - var result = couchbase.upsert(src_col,new_meta,new_doc); + var result = couchbase.upsert(src_col, new_meta, new_doc); if (result.success) { - log('success adv. upsert: result',result); + log('success adv. upsert: result', result); } else { - log('failure adv. upsert: id',new_meta.id,'result',result); + log('failure adv. upsert: id', new_meta.id, 'result', result); } } ---- - -An example return value: - +.Results [source,javascript] ---- { @@ -381,133 +222,66 @@ An example return value: "success": true } ---- - -* _binding_ -+ -The name of the binding that references the target bucket. The binding must have access level of _"read/write"_. - -* _meta (type: Object)_ -+ -The positional parameter (denoted by _meta_ in the prototype above) represents the metadata of the operation. At minimum, the document key must be specified in this object. - -** _meta.id (type: string)_ -+ -The key of the document to be used for the operation. This is a mandatory parameter and must be of JavaScript _string_ type. - -** _meta.keyspace (type: Object)_ -+ -include::partial$eventing-common.adoc[tags=keyspace-description] - -** _meta.expiry_date (type: Date)_ -+ -This is an optional parameter. If specified, it must be of JavaScript _Date_ object type. The document created or updated by this operation will be marked to expire at the specified time. If no _expiry_date_ is passed, and if the document had a prior expiration set, the prior expiration will be cleared. - -* _doc (type: any JSON serializable))_ -+ -This is the document content for the operation. This can be any JavaScript object that can be serialized to JSON (i.e., number, string, boolean, null, object and array). - -* _result – the return value (type: Object)_ -+ -The return object indicates success/failure of the operation, and the metadata of the operation, or the error details if failure. - -** _result.success (type: boolean)_ -+ -This field indicates if the operation was successful or not. It is always present in the return object. - -** _result.meta (type: Object)_ -+ -This field is present only if the operation succeeded. It contains metadata about the object that was inserted or updated. - -*** _result.meta.id (type: string)_ -+ -The key of the document that was inserted or updated by this operation. - -*** _result.meta.cas (type: string)_ -+ -The CAS value of the document that was inserted or updated by this operation. - -*** _result.meta.expiry_date (type: Date)_ -+ -The expiration field of the document, if one was set. If no expiration is set on the document, this field will be absent. - -** _result.error (type: Object)_ -+ -This field is populated only if the operation failed. The content of the error varies based on the type of error encountered, and commonly occurring fields are documented below. - -*** _result.error.code (type: number)_ -+ -If present, the code of the SDK error that triggered this operation to fail. This is typically an internal numeric code. - -*** _result.error.name (type: string)_ -+ -If present, the key is a token indicating the error that SDK encountered that caused this operation to fail. - -*** _result.error.desc (type: string)_ -+ -If present, a human readable description of the error that occurred. The description is for diagnostics and logging purposes only and may change over time. No programmatic logic should be tied to specific contents from this field. - -* _exceptions_ -+ -This API indicates errors via the error object in the return value. Exceptions are thrown only during system failure conditions. +==== [#advanced-replace-op] +== Advanced REPLACE Operation -*The Advanced REPLACE operation*: _result = couchbase.replace(binding, meta, doc)_ - -This operation replaces an existing document in the bucket. This operation will fail if the document with the specified key does not exist. -This operation allows specifying a CAS value that must be matched as a pre-condition before proceeding with the operation. -It also allows specifying an expiration time (or TTL) to be set on the document. +`result = couchbase.replace(binding, meta, doc)` -There is no analogous _Basic Bucket Accessor_ operation to the _Advanced REPLACE operation_ (as "_src_col[meta.id] = adoc_" is more like an upsert). +The REPLACE operation lets you replace an existing document in your bucket with a new document. +It also lets you specify the following: -Below is an example of the _Advanced REPLACE operation_. +* An expiration date (or TTL) for the document +* A CAS value to be used as a pre-condition for the operation +==== Example +==== +.Operation [source,javascript] ---- function OnUpdate(doc, meta) { log('input meta', meta); - log('input doc ', doc); + log('input doc', doc); - var mode = 3; // 1-> no CA, 2-> mismatch in CA, 3-> good CAS + var mode = 3; // 1-> no CAS, 2-> mismatch in CAS, 3-> good CAS - // Setup, make sure we have our doc to "replace", ignore any errors - couchbase.insert(src_col,{"id":"test_adv_replace:10"},{"a:": 1}); + // Set up the operation, make sure there is a document to be replaced, ignore any errors + couchbase.insert(src_col,{"id": "test_adv_replace:10"},{"a:": 1}); var new_meta; if (mode === 1) { - // If we pass no CAS it will succeed - new_meta = {"id":"test_adv_replace:10"}; - // optional set an expiry 60 seconds in the future + // If no CAS is passed, the operation succeeds + new_meta = {"id": "test_adv_replace:10"}; + // (Optional) Set an expiry time of 60 seconds in the future // new_meta.expiry_date = new Date(Date.now() + 60 * 1000); } if (mode === 2) { - // If we pass a non-matching CAS it will fail, so test this - new_meta = {"id":"test_adv_replace:10", "cas":"1111111111111111111"}; + // If a non-matching CAS is passed, the operation fails + new_meta = {"id": "test_adv_replace:10", "cas": "1111111111111111111"}; } if (mode === 3) { - // If we pass the matching or current CAS it will succeed - var tmp_r = couchbase.get(src_col,{"id":"test_adv_replace:10"}); + // If the current or matching CAS is passed, the operation succeeds + var tmp_r = couchbase.get(src_col, {"id": "test_adv_replace:10"}); if (tmp_r.success) { - // Here we use the current CAS just read via couchbase.get(...) - new_meta = {"id":"test_adv_replace:10", "cas": tmp_r.meta.cas}; + // Use the current CAS to read through the couchbase.get(...) operation + new_meta = {"id": "test_adv_replace:10", "cas": tmp_r.meta.cas}; } else { - log('Cannot replace non-existing key that create it and rerun',"test_adv_replace:10"); + log('Cannot replace a non-existing key. Recreate the key and rerun the operation.', "test_adv_replace:10"); return; } } var new_doc = doc; new_doc.random = Math.random(); - var result = couchbase.replace(src_col,new_meta,new_doc); + var result = couchbase.replace(src_col, new_meta, new_doc); if (result.success) { - log('success adv. replace: result',result); + log('success adv. replace: result', result); } else { - log('failure adv. replace: id',new_meta.id,'result',result); + log('failure adv. replace: id', new_meta.id, 'result', result); } } ---- - -Some example return values: - +.Results [source,javascript] ---- { @@ -522,152 +296,74 @@ Some example return values: "error": { "code": 272, "name": "LCB_KEY_EEXISTS", - "desc": "The document key exists with a CAS value different than specified", + "desc": "The document key exists but it has a CAS value that is different from the specified value.", "cas_mismatch": true }, "success": false } ---- - -* _binding_ -+ -The name of the binding that references the target bucket. The binding must have access level of _"read/write"_. - -* _meta (type: Object)_ -+ -The positional parameter (denoted by _meta_ in the prototype above) represents the metadata of the operation. At a minimum, the document key must be specified in this object. - -** _meta.id (type: string)_ -+ -The key of the document to be used for the operation. This is a mandatory parameter and must be of JavaScript _string_ type. If the specified key is not present in the bucket, the operation fails and _key_not_found_ will be set in the error object. - -** _meta.keyspace (type: Object)_ -+ -include::partial$eventing-common.adoc[tags=keyspace-description] - -** _meta.cas (type: string)_ -+ -This is an optional parameter that specifies the CAS value to be used as a pre-condition for the operation. If the document's CAS value does not match the CAS value specified here, the operation will fail, setting the parameter _cas_mismatch_ to true in the error object of the return object. - -** _meta.expiry_date (type: Date)_ -+ -This is an optional parameter. If specified, it must be of JavaScript _Date_ object type. The document updated by this operation will be marked to expire at the specified time. If no expiration is provided, and if the document had a prior expiration set, the prior expiration will be cleared. - -* _doc (type: any JSON serializable))_ -+ -This is the document content for the operation. This can be any JavaScript object that can be serialized to JSON (i.e., number, string, boolean, null, object and array). - -* _result – the return value (type: Object)_ -+ -The return object indicates success/failure of the operation, and the metadata of the operation, or the error details if failure. - -** _result.success (type: boolean)_ -+ -This field indicates if the operation was successful or not. It is always present in the return object. - -** _result.meta (type: Object)_ -+ -This field is present only if the operation succeeded. It contains metadata about the object that was replaced. - -*** _result.meta.id (type: string)_ -+ -The key of the document that was replaced by this operation. - -*** _result.meta.cas (type: string)_ -+ -The CAS value of the document that was replaced by this operation. - -*** _result.meta.expiry_date (type: Date)_ -+ -The expiration field of the document, if one was set. If no expiration is set on the document, this field will be absent. - -** _result.error (type: Object)_ -+ -This field is populated only if the operation failed. The content of the error varies based on the type of error encountered, and commonly occurring fields are documented below. - -*** _result.error.error.cas_mismatch (type: boolean)_ -+ -If present and set to true, this indicates that the operation failed because a CAS value was specified, and the CAS value on the object did not match the requested value. - -*** _result.error.key_not_found (type: boolean)_ -+ -If present and set to true, this indicates that the operation failed because the specified key did not exist in the bucket. - -*** _result.error.code (type: number)_ -+ -If present, the code of the SDK error that triggered this operation to fail. This is typically an internal numeric code. - -*** _result.error.name (type: string)_ -+ -If present, the key is a token indicating the error that SDK encountered that caused this operation to fail. - -*** _result.error.desc (type: string)_ -+ -If present, a human readable description of the error that occurred. The description is for diagnostics and logging purposes only and may change over time. No programmatic logic should be tied to specific contents from this field. - -* _exceptions_ -+ -This API indicates errors via the error object in the return value. Exceptions are thrown only during system failure conditions. +==== [#advanced-delete-op] +== Advanced DELETE Operation -*The Advanced DELETE operation*: _result = couchbase.delete(binding, meta)_ +`result = couchbase.delete(binding, meta)` -This operation allows deleting a document in the bucket specified by key. Optionally, a CAS value may be specified which will be matched as a pre-condition to proceed with the operation. +The DELETE operation lets you delete a document in your bucket. +You can use the document key to specify the document you want to delete. -Contrast this to the _Basic Bucket Accessor_ DEL operation which merely uses an exposed a JavaScript binding or map, "_delete src_col[meta.id]_", where there is no return value (no status and no metadata). - -Below is an example of the _Advanced DELETE operation_. +This operation also lets you specify a CAS value to be matched as a pre-condition to proceed with the operation. +==== Example +==== +.Operation [source,javascript] ---- function OnUpdate(doc, meta) { log('input meta', meta); - log('input doc ', doc); + log('input doc', doc); - var mode = 4; // 1-> no CA, 2-> mismatch in CA, 3-> good CAS, 4-> no such key + var mode = 4; // 1-> no CAS, 2-> mismatch in CAS, 3-> good CAS, 4-> no CAS key - // Setup, make sure we have our doc to "delete", ignore any errors - couchbase.insert(src_col,{"id":"test_adv_delete:10"},{"a:": 1}); + // Set up the operation, make sure there is a document to be deleted, ignore any errors + couchbase.insert(src_col,{"id": "test_adv_delete:10"},{"a:": 1}); var new_meta; if (mode === 1) { - // If we pass no CAS it will succeed - new_meta = {"id":"test_adv_delete:10"}; - // optional set an expiry 60 seconds in the future + // If no CAS is passed, the operation succeeds + new_meta = {"id": "test_adv_delete:10"}; + // (Optional) Set an expiry time of 60 seconds in the future // new_meta.expiry_date = new Date(Date.now() + 60 * 1000); } if (mode === 2) { - // If we pass a non-matching CAS it will fail, so test this - new_meta = {"id":"test_adv_delete:10", "cas":"1111111111111111111"}; + // If a non-matching CAS is passed, the operation fails + new_meta = {"id": "test_adv_delete:10", "cas": "1111111111111111111"}; } if (mode === 3) { - // If we pass the matching or current CAS it will succeed - var tmp_r = couchbase.get(src_col,{"id":"test_adv_delete:10"}); + // If the current or matching CAS is passed, the operation succeeds + var tmp_r = couchbase.get(src_col,{"id": "test_adv_delete:10"}); if (tmp_r.success) { - // Here we use the current CAS just read via couchbase.get(...) - new_meta = {"id":"test_adv_delete:10", "cas": tmp_r.meta.cas}; + // Use the current CAS to read through the couchbase.get(...) operation + new_meta = {"id": "test_adv_delete:10", "cas": tmp_r.meta.cas}; } else { - log('Cannot delete non-existing key that create it and rerun',"test_adv_delete:10"); + log('Cannot delete a non-existing key. Recreate the key and rerun the operation.',"test_adv_delete:10"); return; } } if (mode === 4) { // Remove so that we have: no such key - delete src_col["test_adv_delete:10"] - new_meta = {"id":"test_adv_delete:10"}; + delete src_col["test_adv_delete: 10"] + new_meta = {"id": "test_adv_delete:10"}; } - var result = couchbase.delete(src_col,new_meta); + var result = couchbase.delete(src_col, new_meta); if (result.success) { - log('success adv. delete: result',result); + log('success adv. delete: result', result); } else { - log('failure adv. delete: id',new_meta.id,'result',result); + log('failure adv. delete: id', new_meta.id, 'result', result); } } ---- - -Some example return values: - +.Results [source,javascript] ---- { @@ -682,7 +378,7 @@ Some example return values: "error": { "code": 272, "name": "LCB_KEY_EEXISTS", - "desc": "The document key exists with a CAS value different than specified", + "desc": "The document key exists with a CAS value different than the specified value", "cas_mismatch": true }, "success": false @@ -698,286 +394,317 @@ Some example return values: "success": false } ---- +==== -* _binding_ -+ -The name of the binding that references the target bucket. The binding must have access level of _"read/write"_. - -* _meta (type: Object)_ -+ -The positional parameter (denoted by _meta_ in the prototype above) represents the metadata of the operation. At a minimum, the document key must be specified in this object. - -** _meta.id (type: string)_ -+ -The key of the document to be used for the operation. This is a mandatory parameter and must be of JavaScript _string_ type. If the specified key is not present in the bucket, the operation fails and _key_not_found_ will be set in the error object. - -** _meta.keyspace (type: Object)_ -+ -include::partial$eventing-common.adoc[tags=keyspace-description] - -** _meta.cas (type: string)_ -+ -This is an optional parameter that specifies the CAS value to be used as a pre-condition for the operation. If the document's CAS value does not match the CAS value specified here, the operation will fail, setting the parameter _cas_mismatch_ to true in the error object of the return object. +[#advanced-increment-op] +== Advanced INCREMENT Operation -* _result – the return value (type: Object)_ -+ -The return object indicates success/failure of the operation, and the metadata of the operation, or the error details if failure. +`result = couchbase.increment(binding, meta)` -** _result.success (type: boolean)_ -+ -This field indicates if the operation was successful or not. It is always present in the return object. +The INCREMENT operation lets you increment the `count` field in a specific document. -** _result.meta (type: Object)_ -+ -This field is present only if the operation succeeded. It contains metadata about the object that was deleted. +The document must have the structure `{ count: 23 }`, where 23 is the example counter value. -*** _result.meta.id (type: string)_ -+ -The key of the document that was deleted by this operation. +If the specified counter document does not exist, the operation creates a new document with a `count` value of 0. If the `count` value is 0, the first returned value is 1. -** _result.error (type: Object)_ -+ -This field is populated only if the operation failed. The content of the error varies based on the type of error encountered, and commonly occurring fields are documented below. +The INCREMENT operation cannot manipulate full document counters because of limitations in the KV engine API. -*** _result.error.error.cas_mismatch (type: boolean)_ -+ -If present and set to true, this indicates that the operation failed because a CAS value was specified, and the CAS value on the object did not match the requested value. +==== Example +==== +.Operation +[source,javascript] +---- +function OnUpdate(doc, meta) { + log('input meta', meta); + log('input doc', doc); -*** _result.error.key_not_found (type: boolean)_ -+ -If present and set to true, this indicates that the operation failed because the specified key did not exist in the bucket. + // The operation creates a doc.count if it does not already exist + var ctr_meta = {"id": "my_atomic_counter:1" }; + var result = couchbase.increment(src_col, ctr_meta); + if (result.success) { + log('success adv. increment: result', result); + } else { + log('failure adv. increment: id', ctr_meta.id, 'result', result); + } +} +---- +.Results +[source,javascript] +---- +{ + "doc": { + "count": 24 + }, + "meta": { + "id": "key::1", + "cas": "1609374571840471040" + }, + "success": true +} +---- +==== -*** _result.error.code (type: number)_ -+ -If present, the code of the SDK error that triggered this operation to fail. This is typically an internal numeric code. +[#advanced-decrement-op] +== Advanced DECREMENT Operation -*** _result.error.name (type: string)_ -+ -If present, the key is a token indicating the error that SDK encountered that caused this operation to fail. +`result = couchbase.decrement(binding, meta)` -*** _result.error.desc (type: string)_ -+ -If present, a human readable description of the error that occurred. The description is for diagnostics and logging purposes only and may change over time. No programmatic logic should be tied to specific contents from this field. +The DECREMENT operation lets you decrement the `count` field in a specific document. -* _exceptions_ -+ -This API indicates errors via the error object in the return value. Exceptions are thrown only during system failure conditions. +The document must have the structure `{ count: 23 }`, where 23 is the example counter value. -[#advanced-increment-op] +If the specified counter document does not exist, the operation creates a new document with a `count` value of 0. If the `count` value is 0, the first returned value is -1. -*The Advanced INCREMENT operation*: _result = couchbase.incrment(binding, meta)_ +The DECREMENT operation cannot manipulate full document counters because of limitations in the KV engine API. -This operation atomically increments the field _"count"_ in the specified document. The document must have the below structure: +==== Example +==== +.Operation +[source,javascript] +---- +function OnUpdate(doc, meta) { + log('input meta', meta); + log('input doc', doc); + // The operation creates a doc.count if it does not already exist + var ctr_meta = {"id": "my_atomic_counter:1" }; + var result = couchbase.decrement(src_col, ctr_meta); + if (result.success) { + log('success adv. decrement: result', result); + } else { + log('failure adv. decrement: id', ctr_meta.id, 'result', result); + } +} +---- +.Results [source,javascript] ---- -{"count": 23} // 23 is the current counter value +{ + "doc": { + "count": 22 + }, + "meta": { + "id": "key::1", + "cas": "1609374770297176064" + }, + "success": true +} ---- +==== -The _increment_ operation returns the post-increment value. +[#advanced-touch-op] +== Advanced TOUCH Operation -If the specified counter document does not exist, one is created with _count_ value as 0 and the structure noted above. And so, the first returned value will be 1. +ifeval::['{page-component-version}' == '7.6'] +_(Introduced in Couchbase Server 7.6)_ +endif::[] -Due to limitations in KV engine API, this operation cannot currently manipulate full document counters. +`result = couchbase.touch(binding, meta)` -There is no analogous _Basic Bucket Accessor_ operation to the _Advanced INCREMENT operation_. +The TOUCH operation lets you modify the expiration time of a document without the need to access that document first. -Below is an example of the _Advanced INCREMENT operation_. +You can use this operation if your application does not need to access the database when handling a user session. +==== Example +==== +.Operation [source,javascript] ---- function OnUpdate(doc, meta) { log('input meta', meta); - log('input doc ', doc); + log('input doc', doc); - // if doc.count doesn't exist it will be created - var ctr_meta = {"id": "my_atomic_counter:1" }; - var result = couchbase.increment(src_col,ctr_meta); + var expiry = new Date(); + expiry.setSeconds(expiry.getSeconds() + 10); + + var req = {"id": "doc1", "expiry_date": expiry}; + var result = couchbase.touch(dst_bucket, req); if (result.success) { - log('success adv. increment: result',result); + log('success adv. touch: result', result); } else { - log('failure adv. increment: id',ctr_meta.id,'result',result); + log('failure adv. touch: id', req.id, 'result', result); } } ---- - -An example return value, assume you create this KEY "my_atomic_counter:1" DOC {"count": 23} -if the Eventing function above is deployed the count will be immediately incremented : - +.Results [source,javascript] ---- { - "doc": { - "count": 24 - }, - "meta": { - "id": "key::1", - "cas": "1609374571840471040" - }, - "success": true + "meta": { + "id": "doc1", + "cas": "1708978502129614848" + }, + "success": true +} + +{ + "error": { + "code": 1, + "name": "LCB_KEY_ENOENT", + "desc": "The document key does not exist on the server", + "key_not_found": true + }, + "success": false } ---- +==== -* _binding_ -+ -The name of the binding that references the target bucket. The binding must have access level of _"read/write"_. +[#multiple-collection-functions] +== Eventing Functions that Listen to Multiple Collections -* _meta (type: Object)_ -+ -The positional parameter (denoted by _meta_ in the prototype above) represents the metadata of the operation. At minimum, the document key must be specified in this object. +You can use the wildcard `{asterisk}` in an Eventing Function's scope or collection to listen to multiple collections. -** _meta.id (type: string)_ -+ -The key of the document to be used for the operation. This is a mandatory parameter and must be of JavaScript _string_ type. +If the binding used by the Advanced Keyspace Accessor also contains a wildcard `{asterisk}` for its scope or collection, you must use the additional `meta.keyspace` parameter. -** _meta.keyspace (type: Object)_ -+ -include::partial$eventing-common.adoc[tags=keyspace-description] +The following example includes a `meta.keyspace` parameter that specifies the keyspace in which the INSERT operation is to take place: -* _result – the return value (type: Object)_ -+ -The return object indicates success/failure of the operation, and the metadata of the operation, or the error details if failure. +==== Example +==== +.Operation +[source,javascript] +---- +couchbase.insert( + src_col, { + "id": id_str, + "keyspace": { + "bucket_name": "bkt01", + "scope_name": "scp01", + "collection_name": "col01" + } + }, + some_doc +) +---- +==== -** _result.success (type: boolean)_ -+ -This field indicates if the operation was successful or not. It is always present in the return object. +See the xref:eventing-examples.adoc#examples-scriptlets-advanced-accessors[multiCollectionEventing example] for a detailed example of Eventing Functions that listen to multiple collections. -** _result.meta (type: Object)_ -+ -This field is present only if the operation succeeded. It contains metadata about the counter that was incremented (or created and incremented). +== Return Values -*** _result.meta.id (type: string)_ -+ -The key of the document that was incremented (or created and incremented) by this operation. +[cols="40,25,60",options="header"] -** _result.error (type: Object)_ -+ -This field is populated only if the operation failed. The content of the error varies based on the type of error encountered, and commonly occurring fields are documented below. -+ -Note: if you will be handling large counts (more than 15 digits), please refer to xref:eventing-Terminologies.adoc#json_number_percision[JSON Number Precision]. +|=== +|Value +|Type +|Description -*** _result.error.code (type: number)_ -+ -If present, the code of the SDK error that triggered this operation to fail. This is typically an internal numeric code. +|`binding` +|- +|The name of the binding that references the target bucket. -*** _result.error.name (type: string)_ -+ -If present, the key is a token indicating the error that SDK encountered that caused this operation to fail. +For the Advanced GET operation, the binding can have an access level of read or read/write. -*** _result.error.desc (type: string)_ -+ -If present, a human readable description of the error that occurred. The description is for diagnostics and logging purposes only and may change over time. No programmatic logic should be tied to specific contents from this field. +For all other operations, the binding must have an access level of read/write. -* _exceptions_ -+ -This API indicates errors via the error object in the return value. Exceptions are thrown only during system failure conditions. +|`meta` +|Object +|The positional parameter that represents the metadata of the operation. -[#advanced-decrement-op] +|`meta.id` +|string +|The key of the document to be used in the operation. +This is a mandatory parameter that must be a JavaScript string. -*The Advanced DECREMENT operation*: _result = couchbase.decrement(binding, meta)_ +|`meta.keyspace` +|Object +|The keyspace of the document to be used for the operation. -This operation atomically decrements the field _"count"_ in the specified document. The document must have the below structure: +Must be in the JavaScript format `"keyspace": { "bucket_name": string, "scope_name": string, "collection_name": string }`. -[source,javascript] ----- -{"count": 23} // 23 is the current counter value ----- +|`meta.cas` +|string +|(Optional) Specifies the CAS value to be used as a pre-condition for the operation. -The _decrement_ operation returns the post-decrement value. +If the CAS value of the document does not match the CAS value specified in this field, the operation fails and sets the parameter `cas_mismatch` to true in the error return object. -If the specified counter document does not exist, one is created with _count_ value as 0 and the structure noted above. And so, the first returned value will be -1. +|`meta.expiry_date` +|Date +|(Optional) Sets the expiry time for the document. +If specified, must be in the JavaScript format `Date`. -Due to limitations in KV engine API, this operation cannot currently manipulate full document counters. +|`doc` +|string, number, boolean, null, Object, or Array +|The document content of the operation. -There is no analogous _Basic Bucket Accessor_ operation to the _Advanced DECREMENT operation_. +|`result` +|Object +|Indicates the success or failure of the operation. -Below is an example of the _Advanced DECREMENT operation_. +If the operation is successful, it returns the data that was fetched. +If the operation fails, it returns the details of the error. -[source,javascript] ----- -function OnUpdate(doc, meta) { - log('input meta', meta); - log('input doc ', doc); +|`result.success` +|boolean +|Indicates if the operation is successful or not. +This field is always present in the return object. - // if doc.count doesn't exist it will be created - var ctr_meta = {"id": "my_atomic_counter:1" }; - var result = couchbase.decrement(src_col,ctr_meta); - if (result.success) { - log('success adv. decrement: result',result); - } else { - log('failure adv. decrement: id',ctr_meta.id,'result',result); - } -} ----- +|`result.meta` +|Object +|Contains metadata about the object that was fetched. +This field is only present is the operation is successful. -An example return value, assume you create this KEY "my_atomic_counter:1" DOC {"count": 23} -if the Eventing function above is deployed the count will be immediately decremented : +If the specified key is not present in the bucket, the operation fails and returns `key_not_found` in the error object. -[source,javascript] ----- -{ - "doc": { - "count": 22 - }, - "meta": { - "id": "key::1", - "cas": "1609374770297176064" - }, - "success": true -} ----- +|`result.meta.id` +|string +|The key of the document fetched by the operation. + +|`result.meta.cas` +|string +|The CAS value of the document fetched by the operation. + +|`result.meta.expiry_date` +|Date +|The expiration date of the document. +This field is only present if an expiration is set on the document. -* _binding_ -+ -The name of the binding that references the target bucket. The binding must have access level of _"read/write"_. +|`result.meta.datatype` +|string +|Indicates whether the document is `json` or `binary`. -* _meta (type: Object)_ -+ -The positional parameter (denoted by _meta_ in the prototype above) represents the metadata of the operation. At minimum, the document key must be specified in this object. +|`result.doc` +|string, number, boolean, null, Object, or Array +|Returns the content of the requested document if the operation is successful. -** _meta.id (type: string)_ -+ -The key of the document to be used for the operation. This is a mandatory parameter and must be of JavaScript _string_ type. +|`result.error` +|Object +|Returns an error if the operation fails. -** _meta.keyspace (type: Object)_ -+ -include::partial$eventing-common.adoc[tags=keyspace-description] +|`result.error.cas_mismatch` +|boolean +|If true, this field indicates that the operation failed because a CAS value was not specified or because the CAS value on the object did not match the CAS value in the request. -* _result – the return value (type: Object)_ -+ -The return object indicates success/failure of the operation, and the metadata of the operation, or the error details if failure. +|`result.error.key_not_found` +|boolean +|If true, this field indicates that the operation failed because the specified key did not exist in the bucket. -** _result.success (type: boolean)_ -+ -This field indicates if the operation was successful or not. It is always present in the return object. +|`result.error.key_already_exists` +|boolean +|If true, this field indicates that the operation failed because the specified key already exists in the bucket. -** _result.meta (type: Object)_ -+ -This field is present only if the operation succeeded. It contains metadata about the counter that was decremented (or created and decremented). +|`result.error.code` +|number +|Represents the SDK error code that triggered the operation to fail. +Usually returns an internal numeric code. -*** _result.meta.id (type: string)_ -+ -The key of the document that was decremented (or created and decremented) by this operation. +|`result.error.name` +|string +|Indicates the error that the SDK triggered and that caused the operation to fail. -** _result.error (type: Object)_ -+ -This field is populated only if the operation failed. The content of the error varies based on the type of error encountered, and commonly occurring fields are documented below. -+ -Note: if you will be handling large counts (more than 15 digits), please refer to xref:eventing-Terminologies.adoc#json_number_percision[JSON Number Precision]. +|`result.error.desc` +|string +|A description of the error. +This description can be used for diagnostics and logging, and can change over time. +Programming logic should not be tied to the specific contents of this field. -*** _result.error.code (type: number)_ -+ -If present, the code of the SDK error that triggered this operation to fail. This is typically an internal numeric code. +|`exceptions` +|- +|Indicates errors through the error object in the return value. +Exceptions are only thrown during system failure conditions. -*** _result.error.name (type: string)_ -+ -If present, the key is a token indicating the error that SDK encountered that caused this operation to fail. +|=== -*** _result.error.desc (type: string)_ -+ -If present, a human readable description of the error that occurred. The description is for diagnostics and logging purposes only and may change over time. No programmatic logic should be tied to specific contents from this field. +== See Also -* _exceptions_ -+ -This API indicates errors via the error object in the return value. Exceptions are thrown only during system failure conditions. -//**** +* xref:eventing-examples.adoc#examples-scriptlets-advanced-accessors[Advanced Accessor Handler scriptlets] for complete examples of Advanced Keyspace Accessors, including JavaScript, input mutations, output mutations, and log messages +* xref:eventing-language-constructs.adoc#bucket_accessors[Basic Keyspace Accessors] diff --git a/modules/eventing/pages/eventing-examples.adoc b/modules/eventing/pages/eventing-examples.adoc index e6e4de3c..41efbc4c 100644 --- a/modules/eventing/pages/eventing-examples.adoc +++ b/modules/eventing/pages/eventing-examples.adoc @@ -88,10 +88,13 @@ | xref:eventing:eventing-handler-advancedDeleteOp.adoc[advancedDeleteOp] | xref:eventing:eventing-handler-advancedIncrementOp.adoc[advancedIncrementOp] | xref:eventing:eventing-handler-advancedDecrementOp.adoc[advancedDecrementOp] +| xref:eventing:eventing-handler-advancedTouchOp.adoc[advancedTouchOp] | xref:eventing:eventing-handler-advanced-keepLastN.adoc[advancedKeepLastN] | xref:eventing:eventing-handler-advanced-docControlledSelfExpiry.adoc[advancedDocControlledSelfExpiry] | xref:eventing:eventing-handler-multiCollectionEventing.adoc[multiCollectionEventing] -| +| xref:eventing:eventing-handler-advancedSelfRecursion.adoc[advancedSelfRecursion] +| xref:eventing:eventing-handler-advancedMutateInField.adoc[advancedMutateInField] +| xref:eventing:eventing-handler-advancedMutateInArray.adoc[advancedMutateInArray] |=== [#examples-scriptlets-binary-documents] diff --git a/modules/eventing/pages/eventing-handler-advancedMutateInArray.adoc b/modules/eventing/pages/eventing-handler-advancedMutateInArray.adoc new file mode 100644 index 00000000..89369c1c --- /dev/null +++ b/modules/eventing/pages/eventing-handler-advancedMutateInArray.adoc @@ -0,0 +1,81 @@ += Function: Advanced Sub-Document MUTATEIN array operation +:description: pass:q[Perform the Advanced Sub-Document MUTATEIN operation on an array where Eventing interacts with the Data service.] +:page-edition: Enterprise Edition +:tabs: + +*Goal*: {description} + +* This function *advancedMutateInArrayd* merely demonstrates the CAS free MutateIn operation on a document array field. +* Requires Eventing Storage (or metadata collection) and a "source" collection. +* Needs a Binding of type "bucket alias" (as documented in the Scriptlet). +* Will operate on any mutation where meta.id or the KEY equals "combine_landmark_names". +* You need to great an input document with KEY *combine_landmark_names* DATA *{ "id": "combine_landmark_names", "landmark_names": [] }* +* Set a high number of workers in the Eventing Function's settings like 18 +* The function will add 4,495 landmark names to an array without conflict in no particular order. +* For more information refer to xref:eventing-advanced-keyspace-accessors.adoc#sub-document-mutatein-operation[Sub-Document MUTATEIN Operation] in the detailed documentation. + +[{tabs}] +==== +advancedMutateInArray:: ++ +-- +[source,javascript] +---- +// To run configure the settings for this Function, advancedTouchOp, as follows: +// +// Version 7.6+ +// "Function Scope" +// *.* (or try bulk.data if non-privileged) +// "Listen to Location" +// travel-sample.inventory.landmark +// "Eventing Storage" +// rr100.eventing.metadata +// Binding(s) +// 1. "binding type", "alias name...", "bucket.scope.collection", "Access" +// "bucket alias", "dst_col", "bulk.data.source", "read and write" + +function OnUpdate(doc, meta) { + var accum_meta = {"id": "combine_landmark_names" }; + couchbase.mutateIn(dst_col, accum_meta, [ + couchbase.MutateInSpec.arrayAppend("landmark_names", doc.name), + ]); +} +---- +-- + +Input Data Before Deploy:: ++ +-- +[source,json] +---- +INPUT: KEY combine_landmark_names + +{ + "id": "combine_landmark_names", + "landmark_names": [] +} +---- +-- + +Output Data After Deploy:: ++ +-- +[source,json] +---- +OUTPUT: KEY combine_landmark_names + +{ + "id": "combine_landmark_names", + "landmark_names": [ + "Gabriel's Wharf", + "Blue Bear Performance Hall", + "Circle Bar", + *** 4490 lines removed *** + "Quarry Bank Mill & Styal Estate", + "Mad Cat Brewery", + "Casbah Café" + ] +} +---- +-- +==== diff --git a/modules/eventing/pages/eventing-handler-advancedMutateInField.adoc b/modules/eventing/pages/eventing-handler-advancedMutateInField.adoc new file mode 100644 index 00000000..fcebafba --- /dev/null +++ b/modules/eventing/pages/eventing-handler-advancedMutateInField.adoc @@ -0,0 +1,88 @@ += Function: Advanced Sub-Document MUTATEIN operation +:description: pass:q[Perform the Advanced Sub-Document MUTATEIN operation on a field where Eventing interacts with the Data service.] +:page-edition: Enterprise Edition +:tabs: + +*Goal*: {description} + +* This function *advancedMutateInField* merely demonstrates the CAS free MutateIn operation on a document field. +* Requires Eventing Storage (or metadata collection) and a "source" collection. +* Needs a Binding of type "bucket alias" (as documented in the Scriptlet). +* Will operate on any mutation where meta.id or the KEY starts with "mutateinfield:". +* For more information refer to xref:eventing-advanced-keyspace-accessors.adoc#sub-document-mutatein-operation[Sub-Document MUTATEIN Operation] in the detailed documentation. + +[{tabs}] +==== +advancedMutateInField:: ++ +-- +[source,javascript] +---- +// To run configure the settings for this Function, advancedTouchOp, as follows: +// +// Version 7.6+ +// "Function Scope" +// *.* (or try bulk.data if non-privileged) +// "Listen to Location" +// bulk.data.source +// "Eventing Storage" +// rr100.eventing.metadata +// Binding(s) +// 1. "binding type", "alias name...", "bucket.scope.collection", "Access" +// "bucket alias", "src_col", "bulk.data.source", "read and write" + +function OnUpdate(doc, meta) { + if (meta.id.startsWith("mutateinfield:") === false) return; + + var meta = { "id": meta.id }; + var res; + var opcnt = 1; + + res = + couchbase.mutateIn(src_col, meta, [ + couchbase.MutateInSpec.insert("testField", "insert") + ]); + log(opcnt++,res); + + res = + couchbase.mutateIn(src_col, meta, [ + couchbase.MutateInSpec.replace("testField", "replace") + ]); + log(opcnt++,res); + + res = + couchbase.mutateIn(src_col, meta, [ + couchbase.MutateInSpec.remove("testField") + ]); + log(opcnt++,res); +} +---- +-- + +Input Data/Mutation:: ++ +-- +[source,json] +---- +INPUT: KEY mutateinfield:001 + +{ + "id": "mutateinfield:001", +} + +---- +-- + +Output Data/Logged:: ++ +-- +[source,json] +---- +2024-03-15T14:42:53.314-07:00 [INFO] 1 {"meta":{"id":"mutateinfield:001","cas":"1710538973313433600"},"success":true} + +2024-03-15T14:42:53.316-07:00 [INFO] 2 {"meta":{"id":"mutateinfield:001","cas":"1710538973315596288"},"success":true} + +2024-03-15T14:42:53.317-07:00 [INFO] 3 {"meta":{"id":"mutateinfield:001","cas":"1710538973316841472"},"success":true} +---- +-- +==== diff --git a/modules/eventing/pages/eventing-handler-advancedSelfRecursion.adoc b/modules/eventing/pages/eventing-handler-advancedSelfRecursion.adoc new file mode 100644 index 00000000..c7bc6422 --- /dev/null +++ b/modules/eventing/pages/eventing-handler-advancedSelfRecursion.adoc @@ -0,0 +1,195 @@ += Function: Advanced Self Recursion option +:description: pass:q[Perform the Advanced Self Recursion option where Eventing interacts with the Data service.] +:page-edition: Enterprise Edition +:tabs: + +*Goal*: {description} + +* This function *advancedSelfRecursion* merely demonstrates the Advanced Self Recursion option. +* Requires Eventing Storage (or metadata collection) and a "source" collection. +* Needs a Binding of type "bucket alias" (as documented in the Scriptlet). +* Will operate on any mutation where meta.id or the KEY starts with "doquery:". +* The example shows how to break up and restart a long running process like a N1QL query. +** The logic counts up the number of hotels that start with a particular letter. +* For more information refer to xref:eventing-advanced-keyspace-accessors.adoc#optional-params-recursion[Advanced Self Recursion option] in the detailed documentation. + +[{tabs}] +==== +advancedSelfRecursion:: ++ +-- +[source,javascript] +---- +// To run configure the settings for this Function, advancedSelfRecursion, as follows: +// +// Version 7.6+ +// "Function Scope" +// *.* (or try bulk.data if non-privileged) +// "Listen to Location" +// bulk.data.source +// "Eventing Storage" +// rr100.eventing.metadata +// Binding(s) +// 1. "binding type", "alias name...", "bucket.scope.collection", "Access" +// "bucket alias", "src_col", "bulk.data.source", "read and write" +// +// Must have the sample dataset travel-sample installed + +function OnUpdate(doc, meta) { + if ( meta.id.startsWith("doquery:") === false ) return; + if (doc.done && doc.done === true) return; + + if (! doc.continue) { + doc.queryBeg = new Date(); + doc.queryCnt = 0; + doc.currentQueryOffset = 0; + doc.namesProcessed = 0; + doc.letterHash = {}; + log(meta.id,'Query initialized at ' + doc.queryBeg); + } + + var offset = doc.currentQueryOffset; + var results = + SELECT name + FROM `travel-sample`.`inventory`.`hotel` + LIMIT 100 + OFFSET $offset; + + doc.queryCnt++; + doc.currentQueryOffset = doc.currentQueryOffset + 100; + + var loopCnt = 0; + for (var item of results) { + loopCnt++; + doc.namesProcessed++; + var name = item.name; + if (name && name.length > 0) { + // Extract the first character and convert it to lowercase + var firstChar = name[0].toLowerCase(); + + // If the letter exists in the hash, increment its count, otherwise initialize it to 1 + if (doc.letterHash[firstChar]) { + doc.letterHash[firstChar]++; + } else { + doc.letterHash[firstChar] = 1; + } + } + } + results.close(); + + if (loopCnt < 100) { + // we are done + if (doc.continue) delete doc.continue + doc.done = true; + doc.queryEnd = new Date(); + log(meta.id,'Query cnt complete mutations ' + doc.queryCnt + ' namesProcessed ' + doc.namesProcessed ); + log(meta.id,'Query completed at ' + doc.queryEnd); + log(meta.id,'Result hotels starting with "a" ' + doc.letterHash['a'] + ', hotels starting with "b" ' + doc.letterHash['b'] + ', ...'); + // no self recursion + src_col[meta.id] = doc; + } else { + // we are not done + doc.continue = true; + log(meta.id,'Query cnt in progress mutations ' + doc.queryCnt + ' namesProcessed ' + doc.namesProcessed ); + // use self recursion, will reslt in a continuation of the query + couchbase.upsert(src_col, meta, doc, { "self_recursion": true }); + } +} +---- +-- + +Input Data/Mutation:: ++ +-- +[source,json] +---- +INPUT: KEY doquery:001 + +{ + "id": "doquery:001" +} + +---- +-- + +Output Data/Logged:: ++ +-- +[source,json] +---- +OUTPUT: KEY doquery:001 +{ + "id": "doquery:001", + "queryBeg": "2024-03-15T21:07:38.114Z", + "queryCnt": 10, + "currentQueryOffset": 1000, + "namesProcessed": 917, + "letterHash": { + "1": 1, + "5": 2, + "8": 1, + "m": 58, + "t": 127, + "l": 41, + "g": 25, + "w": 27, + "a": 33, + "b": 48, + "r": 35, + "h": 168, + "n": 19, + "o": 15, + "p": 41, + "s": 64, + "c": 84, + "i": 23, + "u": 8, + "k": 15, + "j": 7, + "'": 1, + "e": 16, + "d": 21, + "q": 4, + "f": 16, + "y": 5, + "v": 12 + }, + "done": true, + "queryEnd": "2024-03-15T21:07:38.425Z" +} +---- +-- + +Output Log:: ++ +-- +[source,json] +---- +2024-03-15T14:07:38.116-07:00 [INFO] "doquery:001" "Query initialized at Fri Mar 15 2024 14:07:38 GMT-0700 (Pacific Daylight Time)" + +2024-03-15T14:07:38.159-07:00 [INFO] "doquery:001" "Query cnt in progress mutations 1 namesProcessed 100" + +2024-03-15T14:07:38.175-07:00 [INFO] "doquery:001" "Query cnt in progress mutations 2 namesProcessed 200" + +2024-03-15T14:07:38.191-07:00 [INFO] "doquery:001" "Query cnt in progress mutations 3 namesProcessed 300" + +2024-03-15T14:07:38.204-07:00 [INFO] "doquery:001" "Query cnt in progress mutations 4 namesProcessed 400" + +2024-03-15T14:07:38.217-07:00 [INFO] "doquery:001" "Query cnt in progress mutations 5 namesProcessed 500" + +2024-03-15T14:07:38.351-07:00 [INFO] "doquery:001" "Query cnt in progress mutations 6 namesProcessed 600" + +2024-03-15T14:07:38.376-07:00 [INFO] "doquery:001" "Query cnt in progress mutations 7 namesProcessed 700" + +2024-03-15T14:07:38.396-07:00 [INFO] "doquery:001" "Query cnt in progress mutations 8 namesProcessed 800" + +2024-03-15T14:07:38.413-07:00 [INFO] "doquery:001" "Query cnt in progress mutations 9 namesProcessed 900" + +2024-03-15T14:07:38.425-07:00 [INFO] "doquery:001" "Query cnt complete mutations 10 namesProcessed 917" + +2024-03-15T14:07:38.425-07:00 [INFO] "doquery:001" "Query completed at Fri Mar 15 2024 14:07:38 GMT-0700 (Pacific Daylight Time)" + +2024-03-15T14:07:38.425-07:00 [INFO] "doquery:001" "Result hotels starting with \"a\" 33, hotels starting with \"b\" 48, ..." +---- +-- +==== diff --git a/modules/eventing/pages/eventing-handler-advancedTouchOp.adoc b/modules/eventing/pages/eventing-handler-advancedTouchOp.adoc new file mode 100644 index 00000000..83fb5742 --- /dev/null +++ b/modules/eventing/pages/eventing-handler-advancedTouchOp.adoc @@ -0,0 +1,116 @@ += Function: Advanced TOUCH operation +:description: pass:q[Perform the Advanced TOUCH operation where Eventing interacts with the Data service.] +:page-edition: Enterprise Edition +:tabs: + +*Goal*: {description} + +* This function *advancedTouchOp* merely demonstrates the Advanced TOUCH operation. +* Requires Eventing Storage (or metadata collection) and a "source" collection. +* Needs a Binding of type "bucket alias" (as documented in the Scriptlet). +* Will operate on any mutation where meta.id or the KEY starts with "ten_seconds:". +* Does not require sending the document back to the Data Service to update the TTL. +* For more information refer to xref:eventing-advanced-keyspace-accessors.adoc#advanced-touch-op[Advanced TOUCH operation] in the detailed documentation. + +[{tabs}] +==== +advancedTouchOp:: ++ +-- +[source,javascript] +---- +// To run configure the settings for this Function, advancedTouchOp, as follows: +// +// Version 7.6+ +// "Function Scope" +// *.* (or try bulk.data if non-privileged) +// "Listen to Location" +// bulk.data.source +// "Eventing Storage" +// rr100.eventing.metadata +// Binding(s) +// 1. "binding type", "alias name...", "bucket.scope.collection", "Access" +// "bucket alias", "src_col", "bulk.data.source", "read and write" + +function OnUpdate(doc, meta) { + if (! meta.id.startsWith("ten_seconds:") ) return; + + log('input meta', meta); + log('input doc', doc); + + var expiry = new Date(); + expiry.setSeconds(expiry.getSeconds() + 10); + + var req = {"id": meta.id, "expiry_date": expiry}; + var result = couchbase.touch(src_col, req); + if (result.success) { + log('success adv. touch: result', result); + } else { + log('failure adv. touch: id', req.id, 'result', result); + } +} +---- +-- + +Input Data/Mutation:: ++ +-- +[source,json] +---- +INPUT: KEY ten_seconds:001 + +{ + "id": "ten_seconds:001", + "type": "Will auto delete in 10 seconds so keep keep retreiving docs and refreshing" +} + +---- +-- + +Output Data/Logged:: ++ +-- +[source,json] +---- + +2024-03-15T11:57:51.103-07:00 [INFO] "input doc" +{ + "id": "ten_seconds:001", + "type": "Will auto delete in 10 seconds so keep keep retreiving docs and refreshing" +} + +2024-03-15T11:57:51.103-07:00 [INFO] "input meta" +{ + "cas": "1710529071079817216", + "id": "ten_seconds:001", + "expiration": 0, + "flags": 33554438, + "vb": 679, + "seq": 102, + "datatype": "json", + "keyspace": + { + "bucket_name": "travel-sample", + "scope_name": "tenant_agent_00", + "collection_name": "bookings" + }, + "cid": 18 +} + +2024-03-15T11:57:51.108-07:00 [INFO] "success adv. touch: result" +{ + "meta": + { + "id": "ten_seconds:001", + "cas": "1710529071107276800" + }, + "success": true +} + +2024-03-15T11:58:03.302-07:00 [INFO] "Doc deleted/expired" "ten_seconds:001" +{ + "expired": true +} +---- +-- +====