Language Constructs

The language constructs are fundamental units of a language. This topic discusses the JavaScript constructs that have been removed and new constructs that have been added in order to support the requirements of Couchbase Functions.

Using JavaScript, you can write your custom Functions. Couchbase Functions inherit support for most ECMAScript constructs by using Google v8 as the execution container. However, to support the ability to shard and scale Function-execution automatically, some capabilities have been removed. Additionally, to optimize language-utilization of the server environment, some new constructs have been added.

Removed Language Features

The following JavaScript features have been removed:

  • Global State

  • Asynchrony

  • Browser and other Extensions

Global State:

Functions do not allow global variables. All state must be saved and retrieved from persistence providers. In Couchbase Server 5.5, the Data Service provider is used as a persistence provider. Therefore, all global states are contained in the Data Service bucket(s) made available to the Function through bindings. This restriction is mandatory for the Function-logic to remain agnostic of the rebalance operation.

var count = 0;                         // Not allowed - global variable.
function OnUpdate(doc, meta) {
  count++;
}

Asynchrony:

Asynchrony, particularly asynchronous callback, to be Functional needs to retain access to parent scope. This forms a node-specific, long-running state that prevents the capture of entire long-running state in persistence providers. Therefore, Function handlers are restricted to running as short-running, straight-line code, without sleep and wakeups. Limited asynchrony is added back through time observers. Time observers are designed not to make the state node-specific

function OnUpdate(doc, meta) {
  setTimeout(function(){}, 300);     // Not allowed - asynchronous flow.
}

Browser and other Extensions:

.A Function executes on Couchbase Server similar to the code that is visible in the browser.

As a Developer, you need to understand that the code displayed in the below example runs in the browser. The ‘window’ term in the code window.XMLHttpRequest(), is not a server-side construct but is in the context of a browser.

function OnUpdate(doc, meta) {
  var rpc = window.XMLHttpRequest();  // Not allowed - browser extension.
}

Added Language Features

The following constructs have been added:

  • Bucket Accessors

  • Top level N1QL keywords such as SELECT, UPDATE, and INSERT

Bucket Accessors:

Couchbase buckets, when bound to a Function, appear as a global JavaScript map. The map operations such as get, set and delete are mapped to the Data Service providers get, set, and delete operations respectively. Other advanced Data Service operations are available as member Functions on the map object.

function OnUpdate(doc, meta) {
  // Assuming 'dest' is a bucket alias binding
  var val = dest[meta.id];         // this is a bucket GET operation.
  dest[meta.id] = {"status":3};    // this is a bucket SET operation.
  delete dest[meta.id];            // this is a bucket DEL operation.
}

N1QL:

Top level N1QL keywords, such as SELECT, UPDATE, and INSERT, are available as keywords in Functions. Operations that return values are accessible through a special iterator on which the for (var <row> of <iterator>) looping-construct has been defined. This restricted looping-construct allows support of query-result streaming, and automatic query-cancellation when the iterator goes out of scope. Any variable that is reachable from the scope of the N1QL query can be referred to using $<variable> syntax in the N1QL statement, where parameters get substituted according to the rules of the named-parameter substitution in the N1QL grammar specification.

The N1QL queries in events are a BETA feature and may have some rough edges and bugs, and may change significantly before the final GA release. This pre-release version of the feature is intended for development purposes only; no Enterprise Support is provided for Beta features.

The iterator is an input iterator (elements are read-only). The keyword this cannot be used in the body of the iterator. The variables created inside the iterator are local to the iterator.

function OnUpdate(doc, meta) {
  var strong = 70;
  var stmt =
    SELECT *                  // N1QL queries are embedded directly.
    FROM `beer-samples`       // Token escaping is standard N1QL style.
    WHERE abv > $strong;      // Local variable reference using $ syntax.
  for (var beer of stmt) {  // Stream results using 'for' iterator.
    break;                   // Cancel streaming query by breaking out.
  }
}

The Function handler code supports N1QL queries. Top level N1QL keywords, such as SELECT, UPDATE, and INSERT, are available as keywords in Functions.

During deployment, if a handler code includes an N1QL query, then the system generates a warning message. Warning Message: "Handler <function_name> uses Beta features. Do not use in production environments."However, the warning message does not prevent the Function deployment.

You must use $<variable>, as per N1QL specification, to use a JavaScript variable in the query statement. The object expressions for substitution are not supported and therefore you cannot use the meta.id expression in the query statement.

Instead of meta.id expression, you can use var id = meta.id in an N1QL query.

  • Invalid N1QL query

    DELETE FROM `transactions` WHERE username = $meta.id;
  • Valid N1QL query

    var id = meta.id;
    DELETE FROM `transactions` WHERE username = $id;

When you use a N1QL query inside a Function handler, remember to use an escaped identifier for bucket names with special characters ([.var]`bucket-name`). Escaped identifiers are surrounded by backticks and support all identifiers in JSON

For example:

  • If the bucket name is beer-sample, then use the N1QL query such as:

    SELECT * FROM `beer-sample` WHERE type...
  • If bucket name is beersample, then use the N1QL query such as:

    SELECT * FROM beersample WHERE type ...

    The Function handler code supports N1QL queries. Top level N1QL keywords, such as SELECT, UPDATE, and INSERT, are available as keywords in Functions.

During deployment, if a handler code includes an N1QL query, then the system generates a warning message. Warning Message: "Handler <function_name> uses Beta features. Do not use in production environments."However, the warning message does not prevent the Function deployment.

You must use $<variable>, as per N1QL specification, to use a JavaScript variable in the query statement. The object expressions for substitution are not supported and therefore you cannot use the meta.id expression in the query statement.

Instead of meta.id expression, you can use var id = meta.id in an N1QL query.

  • Invalid N1QL query

    DELETE FROM `transactions` WHERE username = $meta.id;
  • Valid N1QL query

    var id = meta.id;
    DELETE FROM `transactions` WHERE username = $id;

When you use a N1QL query inside a Function handler, remember to use an escaped identifier for bucket names with special characters ([.var]`bucket-name`). Escaped identifiers are surrounded by backticks and support all identifiers in JSON

For example:

  • If the bucket name is beer-sample, then use the N1QL query such as:

    SELECT * FROM `beer-sample` WHERE type...
  • If bucket name is beersample, then use the N1QL query such as:

    SELECT * FROM beersample WHERE type ...

Handler Signatures

The Eventing Service supports two event-handlers:

  • OnUpdate Handler

  • OnDelete Handler

OnUpdate Handler: This handler gets called when a document is created or modified. The handler listens to data changes from the associated source vBucket. A sample OnUpdate handler is displayed below:

function OnUpdate(doc, meta) {
  if (doc.type === 'order' && doc.value > 5000) {
    //‘phonverify’ is a bucket alias that is specified as a binding.
    phoneverify[meta.id] = doc.customer;
  }
}

In this handler following limitations exist:

  • If a document is modified several times in a short duration, the calls at times get coalesced into a single event, due to deduplication.

  • It is not possible to distinguish a Create from an Update operation.

OnDelete Handler:

This handler gets called when a document is deleted. The handler listens to data changes from the associated source vBucket. This handler also gets invoked during the expiry of a document.

A sample OnDelete handler is displayed below:

function OnDelete(meta) {
  var stmt = SELECT id from orders WHERE shipaddr = $meta.id;
    for (var id of stmt) {
      log("Address invalidated for pending order: " + id);
    }
  }

In this handler the following limitations exist:

  • It is not possible to distinguish a delete as a result of expiration from a user-triggered delete operation.

  • It is not possible to get the value of the document that was just deleted or the one that just got expired.

Reserved Words

Reserved words are words that cannot be used as a variable name, function name, or as a property in the Function handler code. The following table lists the reserved words that you must refrain from using as they are used by Couchbase’s query language, N1QL.

N1QL Keywords

ALTER

EXECUTE

MERGE

UPDATE

BUILD

EXPLAIN

PREPARE

UPSERT

CREATE

GRANT

RENAME

DELETE

INFER

REVOKE

DROP

INSERT

SELECT

What Happens If You Use a Reserved Word?

Let’s say you try to create a new Function handler code using a reserved word for variable names, for function names, and as a property bindings value. All three cases generate a deployment error.

Reserved words as a variable name:

function get_numip_first_3_octets(ip)
{
  var grant = 0;
  if (ip)
  {
	var parts = ip.split('.');
  }
}

Reserved words as a function name:

function grant(ip)
{
  var return_val = 0;
  if (ip)
  {
    var parts = ip.split('.');
  }
}

During the Function deployment step, when the system validates the handler code, it displays an error message such as the following: Sample Error Message - Deployment failed: Syntax error (<line and column numbers>) - grant is a reserved name in N1QLJs

Reserved words as a property bindings value

reserved words