@@ -6,11 +6,11 @@ description: >-
66---
77# Computed Values
88
9+ <small >Introduced in: v3.10.0</small >
10+
911{{ page.description }}
1012{: class ="lead"}
1113
12- <small >Introduced in: v3.10.0</small >
13-
1414If you want to add default values to new documents, maintain auxiliary
1515attributes for search queries, or similar, you can set these attributes manually
1616in every operation that inserts or modifies documents. However, it is more
@@ -28,15 +28,24 @@ every document, or to automatically combine multiple attributes into one,
2828possibly with filtering and a conversion to lowercase characters, to then index
2929the attribute and use it to perform case-insensitive searches.
3030
31+ ## Using Computed Values
32+
33+ Computed values are defined per collection using the ` computedValues ` property,
34+ either when creating the collection or by modifying the collection later on.
35+ If you add or modify computed value definitions at a later point, then they only
36+ affect subsequent write operations. Existing documents remain in their state.
37+
38+ Computed value definitions are included in dumps, and the attributes they added,
39+ too, but no expressions are executed when restoring dumps. The collections and
40+ documents are restored as they are in the dump and no attributes are recalculated.
41+
3142## JavaScript API
3243
33- Computed values are defined per collection, either when creating the collection
34- or by modifying the collection later on. The collection property is called
35- ` computedFields ` and accepts an array of objects.
44+ The ` computedValues ` collection property accepts an array of objects.
3645
37- ` db._create(<collection-name>, { computedFields : [ { … }, … ] }) `
46+ ` db._create(<collection-name>, { computedValues : [ { … }, … ] }) `
3847
39- ` db.<collection-name>.properties({ computedFields : [ { … }, … ] }) `
48+ ` db.<collection-name>.properties({ computedValues : [ { … }, … ] }) `
4049
4150Each object represents a computed value and can have the following attributes:
4251
@@ -50,21 +59,29 @@ Each object represents a computed value and can have the following attributes:
5059 See [ Computed Value Expressions] ( #computed-value-expressions ) for details.
5160
5261- ` overwrite ` (boolean, _ required_ ):
53- Whether the computed value shall take precedence over a user-provided or
54- existing attribute.
62+ Whether the target attribute shall be set if the expression evaluates to ` null ` .
63+ You can set the option to ` false ` to not set (or unset) the target attribute if
64+ the expression returns ` null ` . The default is ` true ` .
5565
5666- ` computeOn ` (array, _ optional_ ):
5767 An array of strings to define on which write operations the value shall be
5868 computed. The possible values are ` "insert" ` , ` "update" ` , and ` "replace" ` .
5969 The default is ` ["insert", "update", "replace"] ` .
6070
6171- ` keepNull ` (boolean, _ optional_ ):
62- Whether the result of the expression shall be stored if it evaluates to ` null ` .
63- This can be used to skip the value computation if any pre-conditions are not met.
72+ Whether the target attribute shall be set if the expression evaluates to ` null ` .
73+ You can set the option to ` false ` to not set (or unset) the target attribute if
74+ the expression returns ` null ` . The default is ` true ` .
75+
76+ - ` failOnWarning ` (boolean, _ optional_ ):
77+ Whether to let the write operation fail if the expression produces a warning.
78+ The default is ` false ` .
6479
65- If you add, remove, or modify computed value definitions at a later point, they
66- are only applied to subsequent write operations and older documents remain in
67- their state.
80+ ## HTTP API
81+
82+ See the ` computedValues ` collection property in the HTTP API documentation for
83+ [ Creating Collections] ( http/collection-creating.html ) and
84+ [ Modifying Collections] ( http/collection-modifying.html ) .
6885
6986## Computed Value Expressions
7087
@@ -76,14 +93,14 @@ You can access the document data via the `@doc` bind variable. It contains the
7693data as it will be stored, including the ` _key ` , ` _id ` , and ` _rev `
7794system attributes. On inserts, you get the user-provided values (plus the
7895system attributes), and on modifications, you get the updated or replaced
79- document to work with.
80-
81- Computed value expressions have the following requirements:
96+ document to work with, including the user-provided values.
8297
83- - The expression must start with a ` RETURN ` statement.
98+ Computed value expressions have the following properties:
8499
85- - No ` FOR ` loops, ` LET ` statements, and subqueries are allowed in the expression.
86- ` FOR ` loops can be substituted using the [ array expansion operator ` [*] ` ] ( aql/advanced-array-operators.html#inline-expressions ) ,
100+ - The expression must start with a ` RETURN ` operation and cannot contain any
101+ other operations. No ` FOR ` loops, ` LET ` statements, and subqueries are allowed
102+ in the expression. ` FOR ` loops can be substituted using the
103+ [ array expansion operator ` [*] ` ] ( aql/advanced-array-operators.html#inline-expressions ) ,
87104 for example, with an inline expressions like the following:
88105
89106 ` RETURN @doc.values[* FILTER CURRENT > 42 RETURN CURRENT * 2] `
@@ -93,9 +110,15 @@ Computed value expressions have the following requirements:
93110 be used in the expression (e.g. ` DOCUMENT() ` , ` PREGEL_RESULT() ` ,
94111 ` COLLECTION_COUNT() ` ).
95112
96- - You cannot base a computed value on another. If you reference an attribute
97- that results from another computed value, the value is implicitly ` null ` ,
98- like any access of non-existent attributes.
113+ - You cannot access the result of another computed value that is generated on
114+ the same ` computeOn ` event.
115+
116+ For example, two computed values that are generated on ` insert ` can not see
117+ the result of the other. Referencing the attributes results in an implicit
118+ ` null ` value. Computed values that are generated on ` update ` or ` replace ` can
119+ see the results of the previous ` insert ` computations, however. They cannot
120+ see the new values of other ` update ` and ` replace ` computations, regardless of
121+ the order of the computed value definitions in the ` computedValues ` property.
99122
100123- You can use AQL functions in the expression but only those that can be
101124 executed on DB-Servers, regardless of your deployment type. The following
@@ -117,10 +140,142 @@ Computed value expressions have the following requirements:
117140 - ` FULLTEXT() `
118141 - [ User-defined functions (UDFs)] ( aql/extending.html )
119142
120- <!-- TODO
121- When using arangodump to restore data into a collection with computed attributes defined, the restore does not recalculate the computed values, but will restore the documents as they are contained inside the dump.
143+ Expressions that do not meet the requirements or that are syntactically invalid
144+ are rejected immediately, when setting or modifying the computed value definitions
145+ of a collection.
146+
147+ ## Examples
148+
149+ Add an attribute with the creation timestamp to new documents:
150+
151+ {% arangoshexample examplevar="examplevar" script="script" result="result" %}
152+ @startDocuBlockInline computedValuesCreatedAt
153+ @EXAMPLE_ARANGOSH_OUTPUT{computedValuesCreatedAt}
154+ | var coll = db._create("users", {
155+ | computedValues: [
156+ | {
157+ | name: "createdAt",
158+ | expression: "RETURN DATE_NOW()",
159+ | overwrite: true,
160+ | computeOn: ["insert"]
161+ | }
162+ | ]
163+ });
164+ var doc = db.users.save({ name: "Paula Plant" });
165+ db.users.toArray();
166+ ~ db._drop("users");
167+ @END_EXAMPLE_ARANGOSH_OUTPUT
168+ @endDocuBlock computedValuesCreatedAt
169+ {% endarangoshexample %}
170+ {% include arangoshexample.html id=examplevar script=script result=result %}
171+
172+ Add an attribute with the date and time of the last modification, only taking
173+ update and replace operations into (not inserts), and allowing to manually
174+ set this value instead of using the computed value:
175+
176+ {% arangoshexample examplevar="examplevar" script="script" result="result" %}
177+ @startDocuBlockInline computedValuesModifiedAt
178+ @EXAMPLE_ARANGOSH_OUTPUT{computedValuesModifiedAt}
179+ | var coll = db._create("users", {
180+ | computedValues: [
181+ | {
182+ | name: "modifiedAt",
183+ | expression: "RETURN ZIP(['date', 'time'], SPLIT(DATE_ISO8601(DATE_NOW()), 'T'))",
184+ | overwrite: false,
185+ | computeOn: ["update", "replace"]
186+ | }
187+ | ]
188+ });
189+ var doc = db.users.save({ _key: "123", name: "Paula Plant" });
190+ doc = db.users.update("123", { email: "gardener@arangodb.com" });
191+ db.users.toArray();
192+ doc = db.users.update("123", { email: "greenhouse@arangodb.com", modifiedAt: { date: "2019-01-01", time: "20:30:00.000Z" } });
193+ db.users.toArray();
194+ ~ db._drop("users");
195+ @END_EXAMPLE_ARANGOSH_OUTPUT
196+ @endDocuBlock computedValuesModifiedAt
197+ {% endarangoshexample %}
198+ {% include arangoshexample.html id=examplevar script=script result=result %}
199+
200+ Compute an attribute from two arrays, filtering one of the lists, and calculating
201+ new values to implement a case-insensitive search using a persistent array index:
202+
203+ {% arangoshexample examplevar="examplevar" script="script" result="result" %}
204+ @startDocuBlockInline computedValuesCombine
205+ @EXAMPLE_ARANGOSH_OUTPUT{computedValuesCombine}
206+ | var coll = db._create("users", {
207+ | computedValues: [
208+ | {
209+ | name: "searchTags",
210+ | expression: "RETURN APPEND(@doc.is[* FILTER CURRENT.public == true RETURN LOWER(CURRENT.name)], @doc.loves[* RETURN LOWER(CURRENT)])",
211+ | overwrite: true
212+ | }
213+ | ]
214+ });
215+ var doc = db.users.save({ name: "Paula Plant", is: [ { name: "Gardener", public: true }, { name: "female" } ], loves: ["AVOCADOS", "Databases"] });
216+ var idx = db.users.ensureIndex({ type: "persistent", fields: ["searchTags[*]"] });
217+ db._query(`FOR u IN users FILTER "avocados" IN u.searchTags RETURN u`).toArray();
218+ ~ db._drop("users");
219+ @END_EXAMPLE_ARANGOSH_OUTPUT
220+ @endDocuBlock computedValuesCombine
221+ {% endarangoshexample %}
222+ {% include arangoshexample.html id=examplevar script=script result=result %}
223+
224+ Set ` keepNull ` to ` false ` and let an expression return ` null ` to not set or
225+ unset the target attribute. If you set ` overwrite ` to ` false ` at the same time,
226+ then the target attribute is not actively unset:
227+
228+ {% arangoshexample examplevar="examplevar" script="script" result="result" %}
229+ @startDocuBlockInline computedValuesKeepNull
230+ @EXAMPLE_ARANGOSH_OUTPUT{computedValuesKeepNull}
231+ | var coll = db._create("users", {
232+ | computedValues: [
233+ | {
234+ | name: "fullName",
235+ | expression: "RETURN @doc.firstName != null AND @doc.lastName != null ? CONCAT_SEPARATOR(' ', @doc.firstName, @doc.lastName) : null",
236+ | overwrite: false,
237+ | keepNull: false
238+ | }
239+ | ]
240+ });
241+ | var docs = db.users.save([
242+ | { firstName: "Paula", lastName: "Plant" },
243+ | { firstName: "James" },
244+ | { lastName: "Barrett", fullName: "Andy J. Barrett" }
245+ ]);
246+ db.users.toArray();
247+ ~ db._drop("users");
248+ @END_EXAMPLE_ARANGOSH_OUTPUT
249+ @endDocuBlock computedValuesKeepNull
250+ {% endarangoshexample %}
251+ {% include arangoshexample.html id=examplevar script=script result=result %}
122252
123- Values of computed attributes are properly replicated to followers. Followers will receive the documents as they are stored on the leader, including any computed attributes. Followers will not rerun the computed attributes computations themselves.
253+ Add a computed value as a sub-attribute to documents. This is not possible
254+ directly because the target attribute needs to be a top-level attribute, but the
255+ AQL expression can merge a nested object with the top-level attribute to achieve
256+ this. The expression checks whether the attributes it wants to calculate a new
257+ value from exist and are strings. If the preconditions are not met, then it
258+ returns the original ` name ` attribute:
124259
125- Non-deterministic computation expressions, such as RETURN RAND() will still work correctly, so that leaders and followers will have the same value for the computed attribute. This is achieved by leaders replicating the document data including the computed attributes values, and the followers not carrying out the computation again.
126- -->
260+ {% arangoshexample examplevar="examplevar" script="script" result="result" %}
261+ @startDocuBlockInline computedValuesSubattribute
262+ @EXAMPLE_ARANGOSH_OUTPUT{computedValuesSubattribute}
263+ | var coll = db._create("users", {
264+ | computedValues: [
265+ | {
266+ | name: "name",
267+ | expression: "RETURN IS_STRING(@doc.name.first) AND IS_STRING(@doc.name.last) ? MERGE(@doc.name, { 'full': CONCAT_SEPARATOR(' ', @doc.name.first, @doc.name.last) }) : @doc.name",
268+ | overwrite: true // must be true to replace the top-level "name" attribute
269+ | }
270+ | ]
271+ });
272+ | var docs = db.users.save([
273+ | { name: { first: "James" } },
274+ | { name: { first: "Paula", last: "Plant" } }
275+ ]);
276+ db.users.toArray();
277+ ~ db._drop("users");
278+ @END_EXAMPLE_ARANGOSH_OUTPUT
279+ @endDocuBlock computedValuesSubattribute
280+ {% endarangoshexample %}
281+ {% include arangoshexample.html id=examplevar script=script result=result %}
0 commit comments