Document Functions
C8QL provides below listed functions to operate on objects and document values.
ATTRIBUTES()
ATTRIBUTES(document, removeInternal, sort) → strArray
Return the top-level attribute keys of the document
as an array. Optionally omit system attributes and sort the array.
document
(object): an arbitrary document / object- removeInternal (bool, optional): whether all system attributes (_key, _id etc., every attribute key that starts with an underscore) shall be omitted in the result. The default is false.
- sort (bool, optional): optionally sort the resulting array alphabetically. The default is false and will return the attribute names in any order.
- returns strArray (array): the attribute keys of the input
document
as an array of strings
ATTRIBUTES( { "foo": "bar", "_key": "123", "_custom": "yes" } )
// [ "foo", "_key", "_custom" ]
ATTRIBUTES( { "foo": "bar", "_key": "123", "_custom": "yes" }, true )
// [ "foo" ]
ATTRIBUTES( { "foo": "bar", "_key": "123", "_custom": "yes" }, false, true )
// [ "_custom", "_key", "foo" ]
Complex example to count how often every attribute key occurs in the documents of collection
(expensive on large collections):
LET attributesPerDocument = (
FOR doc IN collection RETURN ATTRIBUTES(doc, true)
)
FOR attributeArray IN attributesPerDocument
FOR attribute IN attributeArray
COLLECT attr = attribute WITH COUNT INTO count
SORT count DESC
RETURN {attr, count}
COUNT()
This is an alias for LENGTH().
HAS()
HAS(document, attributeName) → isPresent
Test whether an attribute is present in the provided document.
document
(object): an arbitrary document / object- attributeName (string): the attribute key to test for
- returns isPresent (bool): true if
document
has an attribute named attributeName, and false otherwise. An attribute with a falsy value (0, false, empty string""
) or null is also considered as present and returns true.
HAS( { name: "Jane" }, "name" ) // true
HAS( { name: "Jane" }, "age" ) // false
HAS( { name: null }, "name" ) // true
The function checks if the specified attribute exists. This is different from similar ways to test for the existance of an attribute, in case the attribute has a falsy value or is not present (implicitly null on object access):
!!{ name: "" }.name // false
HAS( { name: "" }, "name") // true
{ name: null }.name == null // true
{ }.name == null // true
HAS( { name: null }, "name" ) // true
HAS( { }, "name" ) // false
HAS()
can not utilize indexes. If it's not necessary to distinguish between explicit and implicit null values in your query, you may use an equality comparison to test for null and create a non-sparse index on the attribute you want to test against:
FILTER !HAS(doc, "name") // can not use indexes
FILTER IS_NULL(doc, "name") // can not use indexes
FILTER doc.name == null // can utilize non-sparse indexes
IS_SAME_COLLECTION()
IS_SAME_COLLECTION(collectionName, documentHandle) → bool
collection id as the collection specified in collection
. document
can either be a document handle string, or a document with an _id attribute. The function does not validate whether the collection actually contains the specified document, but only compares the name of the specified collection with the collection name part of the specified document. If document
is neither an object with an id
attribute nor a string value, the function will return null and raise a warning.
- collectionName (string): the name of a collection as string
- documentHandle (string|object): a document identifier string (e.g. _users/1234) or a regular document from a collection. Passing either a non-string or a non-document or a document without an _id attribute will result in an error.
- returns bool (bool): return true if the collection of documentHandle is the same as collectionName, otherwise false
// true
IS_SAME_COLLECTION( "_users", "_users/my-user" )
IS_SAME_COLLECTION( "_users", { _id: "_users/my-user" } )
// false
IS_SAME_COLLECTION( "_users", "foobar/baz")
IS_SAME_COLLECTION( "_users", { _id: "something/else" } )
KEEP()
KEEP(document, attributeName1, attributeName2, ... attributeNameN) → doc
Keep only the attributes attributeName to attributeNameN of document
. All other attributes will be removed from the result.
To do the opposite, see UNSET().
- document (object): a document / object
- attributeNames (string, repeatable): an arbitrary number of attribute names as multiple arguments
- returns doc (object): a document with only the specified attributes on the top-level
KEEP(doc, "firstname", "name", "likes")
KEEP(document, attributeNameArray) → doc
- document (object): a document / object
- attributeNameArray (array): an array of attribute names as strings
- returns doc (object): a document with only the specified attributes on the top-level
KEEP(doc, [ "firstname", "name", "likes" ])
LENGTH()
LENGTH(doc) → attrCount
Determine the number of attribute keys of an object / document.
- doc (object): a document / object
- returns attrCount (number): the number of attribute keys in doc, regardless of their values
LENGTH() can also determine the number of elements in an array, the amount of documents in a collection and the character length of a string.
MATCHES()
MATCHES(document, examples, returnIndex) → match
Compare the given document
against each example document provided. The comparisons will be started with the first example. All attributes of the example will be compared against the attributes of document
. If all attributes match, the comparison stops and the result is returned. If there is a mismatch, the function will continue the comparison with the next example until there are no more examples left.
The examples can be an array of 1..n example documents or a single document, with any number of attributes each.
An attribute value of null
will match documents with an explicit attribute value of null
as well as documents with this attribute missing (implicitly null
). Only HAS() can differentiate between an attribute being absent and having a stored null
value.
An empty object {}
will match all documents. Be careful not to ask for all documents accidentally.
MATCHES()
can not utilize indexes. You may use plain FILTER
conditions instead to potentially benefit from existing indexes:
```js
FOR doc IN coll
FILTER (cond1 AND cond2 AND cond3) OR (cond4 AND cond5) ...
```
- document (object): document to determine whether it matches any example
- examples (object|array): a single document, or an array of documents to compare against. Specifying an empty array is not allowed.
- returnIndex (bool): by setting this flag to true, the index of the example that matched will be returned (starting at offset 0), or -1 if there was no match. The default is false and makes the function return a boolean.
- returns match (bool|number): if
document
matches one of the examples, true is returned, otherwise false. A number is returned instead if returnIndex is used.
LET doc = {
name: "jane",
age: 27,
active: true
}
RETURN MATCHES(doc, { age: 27, active: true } )
This will return true, because all attributes of the example are present in the document.
RETURN MATCHES(
{ "test": 1 },
[
{ "test": 1, "foo": "bar" },
{ "foo": 1 },
{ "test": 1 }
], true)
This will return 2, because the third example matches, and because the returnIndex flag is set to true.
MERGE()
MERGE(document1, document2, ... documentN) → mergedDocument
Merge the documents document1 to documentN into a single document. If document attribute keys are ambiguous, the merged result will contain the values of the documents contained later in the argument list.
- documents (object, repeatable): an arbitrary number of documents as multiple arguments (at least 2)
- returns mergedDocument (object): a combined document
Merging will only be done for top-level attributes. If you wish to merge sub-attributes, use MERGE_RECURSIVE() instead.
Two documents with distinct attribute names can easily be merged into one:
MERGE(
{ "user1": { "name": "Jane" } },
{ "user2": { "name": "Tom" } }
)
// { "user1": { "name": "Jane" }, "user2": { "name": "Tom" } }
When merging documents with identical attribute names, the attribute values of the latter documents will be used in the end result:
MERGE(
{ "users": { "name": "Jane" } },
{ "users": { "name": "Tom" } }
)
// { "users": { "name": "Tom" } }
MERGE(docArray) → mergedDocument
MERGE works with a single array parameter, too. This variant allows combining the attributes of multiple objects in an array into a single object.
- docArray (array): an array of documents, as sole argument
- returns mergedDocument (object): a combined document
MERGE(
[
{ foo: "bar" },
{ quux: "quetzalcoatl", ruled: true },
{ bar: "baz", foo: "done" }
]
)
This will now return:
{
"foo": "done",
"quux": "quetzalcoatl",
"ruled": true,
"bar": "baz"
}
MERGE_RECURSIVE()
MERGE_RECURSIVE(document1, document2, ... documentN) → mergedDocument
Recursively merge the documents document1 to documentN into a single document. If document attribute keys are ambiguous, the merged result will contain the values of the documents contained later in the argument list.
- documents (object, repeatable): an arbitrary number of documents as multiple arguments (at least 2)
- returns mergedDocument (object): a combined document
For example, two documents with distinct attribute names can easily be merged into one:
MERGE_RECURSIVE(
{ "user-1": { "name": "Jane", "livesIn": { "city": "LA" } } },
{ "user-1": { "age": 42, "livesIn": { "state": "CA" } } }
)
// { "user-1": { "name": "Jane", "livesIn": { "city": "LA", "state": "CA" }, "age": 42 } }
MERGE_RECURSIVE()
does not support the single array parameter variant that MERGE offers.
PARSE_IDENTIFIER()
PARSE_IDENTIFIER(documentHandle) → parts
Parse a document handle and return its individual parts as separate attributes.
This function can be used to easily determine the collection name and key of a given document.
- documentHandle (string|object): a document identifier string (e.g. _users/1234) or a regular document from a collection. Passing either a non-string or a non-document or a document without an _id attribute will result in an error.
- returns parts (object): an object with the attributes
collection
and key
PARSE_IDENTIFIER("_users/my-user")
// { "collection": "_users", "key": "my-user" }
PARSE_IDENTIFIER( { "_id": "mycollection/mykey", "value": "some value" } )
// { "collection": "mycollection", "key": "mykey" }
TRANSLATE()
TRANSLATE(value, lookupDocument, defaultValue) → mappedValue
Look up the specified value in the lookupDocument. If value is a key in lookupDocument, then value will be replaced with the lookup value found. If value is not present in lookupDocument, then defaultValue will be returned if specified. If no defaultValue is specified, value will be returned unchanged.
- value (string): the value to encode according to the mapping
- lookupDocument (object): a key/value mapping as document
- defaultValue (any, optional): a fallback value in case value is not found
- returns mappedValue (any): the encoded value, or the unaltered value or defaultValue (if supplied) in case it couldn't be mapped
TRANSLATE("FR", { US: "United States", UK: "United Kingdom", FR: "France" } )
// "France"
TRANSLATE(42, { foo: "bar", bar: "baz" } )
// 42
TRANSLATE(42, { foo: "bar", bar: "baz" }, "not found!")
// "not found!"
UNSET()
UNSET(document, attributeName1, attributeName2, ... attributeNameN) → doc
Remove the attributes attributeName1 to attributeNameN from document
. All other attributes will be preserved.
To do the opposite, see KEEP().
- document (object): a document / object
- attributeNames (string, repeatable): an arbitrary number of attribute names as multiple arguments (at least 1)
- returns doc (object):
document
without the specified attributes on the top-level
UNSET( doc, "_id", "_key", "foo", "bar" )
UNSET(document, attributeNameArray) → doc
document
(object): a document / object- attributeNameArray (array): an array of attribute names as strings
- returns doc (object):
document
without the specified attributes on the top-level
UNSET( doc, [ "_id", "_key", "foo", "bar" ] )
UNSET_RECURSIVE()
UNSET_RECURSIVE(document, attributeName1, attributeName2, ... attributeNameN) → doc
Recursively remove the attributes attributeName1 to attributeNameN from document
and its sub-documents. All other attributes will be preserved.
document
(object): a document / object- attributeNames (string, repeatable): an arbitrary number of attribute names as multiple arguments (at least 1)
- returns doc (object):
document
without the specified attributes on all levels (top-level as well as nested objects)
UNSET_RECURSIVE( doc, "_id", "_key", "foo", "bar" )
UNSET_RECURSIVE(document, attributeNameArray) → doc
document
(object): a document / object- attributeNameArray (array): an array of attribute names as strings
- returns doc (object):
document
without the specified attributes on all levels (top-level as well as nested objects)
UNSET_RECURSIVE( doc, [ "_id", "_key", "foo", "bar" ] )
VALUES()
VALUES(document, removeInternal) → anyArray
Return the attribute values of the document
as an array. Optionally omit system attributes.
document
(object): a document / object- removeInternal (bool, optional): if set to true, then all internal attributes (such as _id, _key etc.) are removed from the result
- returns anyArray (array): the values of
document
returned in any order
VALUES( { "_key": "users/jane", "name": "Jane", "age": 35 } )
// [ "Jane", 35, "users/jane" ]
VALUES( { "_key": "users/jane", "name": "Jane", "age": 35 }, true )
// [ "Jane", 35 ]
ZIP()
ZIP(keys, values) → doc
Return a document object assembled from the separate parameters keys and values.
keys and values must be arrays and have the same length.
- keys (array): an array of strings, to be used as attribute names in the result
- values (array): an array with elements of arbitrary types, to be used as attribute values
- returns doc (object): a document with the keys and values assembled
ZIP( [ "name", "active", "hobbies" ], [ "some user", true, [ "swimming", "riding" ] ] )
// { "name": "some user", "active": true, "hobbies": [ "swimming", "riding" ] }