Data Structures

You can use complex data structures such as maps and lists in Couchbase. These data structures may be manipulated with basic operations without retrieving and storing the entire document.

The data structure API is not available in the C SDK, but the sub-document API (which the data structure feature uses) can be used to attain the same results with the same performance profile. The best way to inter-operate with data structures as provided by other Couchbase SDKs is to use either full-document operations or sub-document operations. The section on Sub-Document Operations shows how C applications can manipulate these data structures.

Data Structure to Sub-document Operations

The following table may be used to help you perform data structure operations by using sub-document operations. Note that some data structure operations may consist of one or more sub-document operations.

LCB_SDCMD_xxx constants are assumed to be used in a context similar to the following (refer to the Sub-Document Operations page for more details).

lcb_SDSPEC spec = { 0 };
lcb_CMDSUBDOC cmd = { 0 };
cmd.specs = &spec;
cmd.nspecs = 1;

LCB_CMD_SET_KEY(&cmd, docid, strlen(docid));
LCB_SDSPEC_SET_PATH(&spec, path, strlen(path)); // Can be an empty string where applicable
LCB_SDSPEC_SET_VALUE(&spec, value, strlen(value));
spec.sdcmd = LCB_SDCMD_DICT_UPSERT;
Table 1. Data Structure to Sub-document Mapping
Data Structure Operation Sub-document Equivalent

MapGet(docid, mapkey)

spec.sdcmd = LCB_SDCMD_GET;
LCB_SDSPEC_SET_PATH(&spec, mapkey, mapkey_len);

MapRemove(docid, mapkey)

spec.sdcmd = LCB_SDCMD_DICT_UPSERT;
LCB_SDSPEC_SET_PATH(&spec, mapkey, mapkey_len);

MapSize(docid)

Your application should fetch the entire document, parse it with a JSON parser, and derive the size.

MapAdd(docid, mapkey, value)

spec.sdcmd = LCB_SDCMD_DICT_ADD;
LCB_SDSPEC_SET_PATH(&spec, mapkey, mapkey_len);
LCB_SDSPEC_SET_VALUE(&spec, value, value_len);

ListGet(docid, index)

spec.sdcmd = LCB_SDCMD_GET;
std::string path = "[" + std::to_string(index) + "]";
LCB_SDSPEC_SET_PATH(&spec, path.c_str(), path.size());

ListAppend(docid, value)

spec.sdcmd = LCB_SDCMD_ARRAY_ADD_LAST;
LCB_SDCMD_SET_PATH(&spec, "", 0);
LCB_SDCMD_SET_VALUE(&spec, value, value_len);

ListPrepend(docid, value)

spec.sdcmd = LCB_SDCMD_ARRAY_ADD_FIRST;
LCB_SDCMD_SET_PATH(&spec, "", 0);
LCB_SDCMD_SET_VALUE(&spec, value, value_len);

ListSize(docid)

See notes on MapSize.

SetContains(docid, value)

Not available as a native operation. Your application should fetch the document and parse it (as a JSON array) and then determine if the item exists.

SetAdd(docid, value)

This requires that the set contains only JSON primitives (i.e. numeric, string, boolean, and null values).
spec.sdcmd = LCB_SDCMD_ARRAY_ADD_UNIQUE;
LCB_SDCMD_SET_PATH(&spec, "", 0);
LCB_SDCMD_SET_VALUE(&spec, value, value_len);

SetSize(docid)

See notes on MapSize

SetRemove(docid, value)

Not available as a native operation. Your application should perform the following:

  1. Fetch the document (make note of the CAS value).

  2. Parse the value as JSON.

  3. Get the index of the item.

  4. Perform another sub-document operation using the LCB_SDCMD_REMOVE command with the index (i.e. "[" + std::to_string(index) + "]") as the path. Ensure to pass the CAS to the remove operation, i.e.

    sdcmd.cas = cas_from_step_1;
  5. If the removal results in an LCB_KEY_EEXISTS then it means another client has modified the set since step #1, and you must go back to step #1. Otherwise, the removal is complete.

QueuePush(docid, value)

This is an alias to ListPrepend

QueuePop(docid)

Not available as a native operation because the returned value may be lost in transit. To perform this operation in your application:

  1. Fetch the last item of the list:

    spec.sdcmd = LCB_SDCMD_GET;
    LCB_SDSPEC_SET_PATH(&spec, "[-1]", 4);

    Take note of the CAS when the operation completes.

  2. If the operation results in an LCB_SUBDOC_PATH_ENOENT error then the queue is empty.

  3. Remove the item from the queue:

    spec.sdcmd = LCB_SDCMD_GET;
    LCB_SDSPEC_SET_PATH(&spec, "[-1]", 4);

    Ensure to pass the CAS as well:

    sdcmd.cas = cas_from_step_1;
  4. If the operation fails with LCB_SUBDOC_PATH_EEXISTS then someone else has modified the queue (possibly by trying to process the last item) as well. Go back to step 1.

If you are using the queue as a job queue, ensure that you do not process the item until it is fully removed from the queue, as it is possible another client/application thread will retrieve the same queue item and try to process it as well.

QueueSize(docid)

See notes on MapSize