From 9d0225e806783dce6447826b0ee587f0b9d558dc Mon Sep 17 00:00:00 2001 From: Paula Date: Fri, 17 Nov 2023 17:55:42 +1100 Subject: [PATCH 1/6] alternative UPSERT syntax with filter conditions --- .../version-3.12/whats-new-in-3-12.md | 47 ++++++++++++++++++- 1 file changed, 46 insertions(+), 1 deletion(-) diff --git a/site/content/3.12/release-notes/version-3.12/whats-new-in-3-12.md b/site/content/3.12/release-notes/version-3.12/whats-new-in-3-12.md index 93a9a7fdad..7756131d4b 100644 --- a/site/content/3.12/release-notes/version-3.12/whats-new-in-3-12.md +++ b/site/content/3.12/release-notes/version-3.12/whats-new-in-3-12.md @@ -97,7 +97,52 @@ Swagger 2.x compatibility. ## AQL - +### Filter conditions in `UPSERT` operations (experimental) + +Version 3.12 introduces an alternative syntax for `UPSERT` operations that +allows you to use dynamic attribute names to look up documents. Previously, +the expression used to look up a document had to be an object literal. + +{{< tabs groupid="UPSERT syntax" >}} + +{{< tab name="Exact-value matching" >}} +```aql +UPSERT +INSERT ... +UPDATE|REPLACE ... +IN +``` +{{< /tab >}} + +{{< tab name="Using FILTER conditions" >}} +```aql +UPSERT FILTER +INSERT ... +UPDATE|REPLACE ... +IN +``` +{{< /tab >}} + +{{< /tabs >}} + +The arbitrary filter condition for the lookup can make use of the pseudo-variable +`CURRENT`, and it can access the document and apply more filters on it than just +equality matches. Example: + +```aql +UPSERT FILTER CURRENT.category == 'test && CURRENT.dateTime >= ... && CURRENT.dateTime < ... && CURRENT.status != 'inactive' +INSERT ... +UPDATE|REPLACE ... +IN +``` + +If no document is found in the collection which fits the `FILTER` condition, +then the `INSERT` operation is executed. +If exactly one document is found in the collection which fits the `FILTER` +condition, then the `UPDATE` operation is executed on that document. +If many documents are found in the collection which fit the `FILTER` +condition, then the first found document that satisfies the condition is +updated/replaced. ## Indexing From a88aff72a83de6cf7d7ab0543e71b1d3451c688d Mon Sep 17 00:00:00 2001 From: Paula Date: Thu, 23 Nov 2023 23:26:01 +1100 Subject: [PATCH 2/6] finalize description and examples --- .../3.12/aql/high-level-operations/upsert.md | 50 ++++++++++++++++++- .../version-3.12/whats-new-in-3-12.md | 46 ++++++++--------- 2 files changed, 69 insertions(+), 27 deletions(-) diff --git a/site/content/3.12/aql/high-level-operations/upsert.md b/site/content/3.12/aql/high-level-operations/upsert.md index f8faa16c33..75b691cc7f 100644 --- a/site/content/3.12/aql/high-level-operations/upsert.md +++ b/site/content/3.12/aql/high-level-operations/upsert.md @@ -13,9 +13,12 @@ Only a single `UPSERT` statement per collection is allowed per AQL query, and it cannot be followed by read or write operations that access the same collection, by traversal operations, or AQL functions that can read documents. -## Syntax +## Exact-value matching syntax -The syntax for upsert and repsert operations is: +This syntax can only look up documents by exact-value matching and does not +support arbitrary filter conditions. + +The exact-value matching syntax for upsert and repsert operations is:
UPSERT searchExpression
 INSERT insertExpression
@@ -63,6 +66,49 @@ UPDATE { logins: OLD.logins + 1 } IN users
 Note that in the `UPDATE` case it is possible to refer to the previous version of the
 document using the `OLD` pseudo-value.
 
+## Filter matching syntax (experimental)
+
+The alternative syntax for `UPSERT` operations allows you to use dynamic
+attribute names to look up documents. This means that the expression you use to
+look up a document does not have to be an object literal.
+
+
UPSERT FILTERfilter-condition
+INSERT insertExpression
+UPDATE updateExpression
+IN collection
+ +
UPSERT FILTERfilter-condition
+INSERT insertExpression
+REPLACE updateExpression
+IN collection
+ +The arbitrary filter condition for the lookup can make use of the pseudo-variable +`CURRENT` and it can access the document and apply more filters on it than just +equality matches. Example: + +```aql +UPSERT FILTER CURRENT.name == 'superuser' +INSERT { name: 'superuser', logins: 1, dateCreated: DATE_NOW() } +UPDATE { logins: OLD.logins + 1 } IN users +``` + +The `FILTER` statement can also use operators such as `&&` and `||` to make +more complex filter conditions. + +```aql +UPSERT FILTER CURRENT.name == 'superuser' && CURRENT.active == true && (LOWER(CURRENT.member) == true || CURRENT.age == 33) +INSERT { name: 'superuser', logins: 1, dateCreated: DATE_NOW() } +UPDATE { logins: OLD.logins + 1 } IN users +``` + +If no document is found in the collection which fits the `FILTER` condition, +then the `INSERT` operation is executed. +If exactly one document is found in the collection which fits the `FILTER` +condition, then the `UPDATE` operation is executed on that document. +If many documents are found in the collection which fit the `FILTER` +condition, then the first found document that satisfies the condition is +updated/replaced. + ## Query options ### `ignoreErrors` diff --git a/site/content/3.12/release-notes/version-3.12/whats-new-in-3-12.md b/site/content/3.12/release-notes/version-3.12/whats-new-in-3-12.md index c75d3d76c8..e5405b306b 100644 --- a/site/content/3.12/release-notes/version-3.12/whats-new-in-3-12.md +++ b/site/content/3.12/release-notes/version-3.12/whats-new-in-3-12.md @@ -130,52 +130,48 @@ Swagger 2.x compatibility. ## AQL -### Filter conditions in `UPSERT` operations (experimental) +### Filter matching syntax in `UPSERT` operations (experimental) -Version 3.12 introduces an alternative syntax for `UPSERT` operations that -allows you to use dynamic attribute names to look up documents. Previously, +Version 3.12 introduces an alternative syntax for +[`UPSERT` operations](../../aql/high-level-operations/upsert.md) that allows +you to use dynamic attribute names to look up documents. Previously, the expression used to look up a document had to be an object literal. +You can now use a `FILTER` statement for the `UPSERT` operation. The arbitrary +filter condition for the lookup can make use of the pseudo-variable `CURRENT` +and it can access the document and apply more filters on it than just +equality matches. + {{< tabs groupid="UPSERT syntax" >}} {{< tab name="Exact-value matching" >}} ```aql -UPSERT -INSERT ... -UPDATE|REPLACE ... -IN +UPSERT { name: 'superuser' } +INSERT { name: 'superuser', logins: 1, dateCreated: DATE_NOW() } +UPDATE { logins: OLD.logins + 1 } IN users ``` {{< /tab >}} {{< tab name="Using FILTER conditions" >}} ```aql -UPSERT FILTER -INSERT ... -UPDATE|REPLACE ... -IN +UPSERT FILTER CURRENT.name == 'superuser' +INSERT { name: 'superuser', logins: 1, dateCreated: DATE_NOW() } +UPDATE { logins: OLD.logins + 1 } IN users ``` {{< /tab >}} {{< /tabs >}} -The arbitrary filter condition for the lookup can make use of the pseudo-variable -`CURRENT`, and it can access the document and apply more filters on it than just -equality matches. Example: +The `FILTER` statement can also use operators such as `&&` and `||` to make +more complex filter conditions. ```aql -UPSERT FILTER CURRENT.category == 'test && CURRENT.dateTime >= ... && CURRENT.dateTime < ... && CURRENT.status != 'inactive' -INSERT ... -UPDATE|REPLACE ... -IN +UPSERT FILTER CURRENT.name == 'superuser' && CURRENT.active == true && (LOWER(CURRENT.member) == true || CURRENT.age == 33) +INSERT { name: 'superuser', logins: 1, dateCreated: DATE_NOW() } +UPDATE { logins: OLD.logins + 1 } IN users ``` -If no document is found in the collection which fits the `FILTER` condition, -then the `INSERT` operation is executed. -If exactly one document is found in the collection which fits the `FILTER` -condition, then the `UPDATE` operation is executed on that document. -If many documents are found in the collection which fit the `FILTER` -condition, then the first found document that satisfies the condition is -updated/replaced. +Read more about [`UPSERT` operations](../../aql/high-level-operations/upsert.md) in AQL. ## Indexing From 076176e1517414f08a4fa84d911a870f6e008620 Mon Sep 17 00:00:00 2001 From: Paula Mihu <97217318+nerpaula@users.noreply.github.com> Date: Fri, 24 Nov 2023 00:25:10 +1100 Subject: [PATCH 3/6] Apply suggestions from code review Co-authored-by: Jan --- site/content/3.12/aql/high-level-operations/upsert.md | 8 ++++---- .../3.12/release-notes/version-3.12/whats-new-in-3-12.md | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/site/content/3.12/aql/high-level-operations/upsert.md b/site/content/3.12/aql/high-level-operations/upsert.md index 75b691cc7f..4c794e0d16 100644 --- a/site/content/3.12/aql/high-level-operations/upsert.md +++ b/site/content/3.12/aql/high-level-operations/upsert.md @@ -66,7 +66,7 @@ UPDATE { logins: OLD.logins + 1 } IN users Note that in the `UPDATE` case it is possible to refer to the previous version of the document using the `OLD` pseudo-value. -## Filter matching syntax (experimental) +## Filter matching syntax The alternative syntax for `UPSERT` operations allows you to use dynamic attribute names to look up documents. This means that the expression you use to @@ -82,8 +82,8 @@ INSERT insertExpression REPLACE updateExpression IN collection
-The arbitrary filter condition for the lookup can make use of the pseudo-variable -`CURRENT` and it can access the document and apply more filters on it than just +The filter condition for the lookup can make use of the pseudo-variable +`CURRENT` to access the lookup document and apply more filters on it than just equality matches. Example: ```aql @@ -92,7 +92,7 @@ INSERT { name: 'superuser', logins: 1, dateCreated: DATE_NOW() } UPDATE { logins: OLD.logins + 1 } IN users ``` -The `FILTER` statement can also use operators such as `&&` and `||` to make +While there can only be one `FILTER` statement in this version of `UPSERT`, the `FILTER` statement can also use operators such as `&&` and `||` to make more complex filter conditions. ```aql diff --git a/site/content/3.12/release-notes/version-3.12/whats-new-in-3-12.md b/site/content/3.12/release-notes/version-3.12/whats-new-in-3-12.md index e5405b306b..9f3b812a6e 100644 --- a/site/content/3.12/release-notes/version-3.12/whats-new-in-3-12.md +++ b/site/content/3.12/release-notes/version-3.12/whats-new-in-3-12.md @@ -130,16 +130,16 @@ Swagger 2.x compatibility. ## AQL -### Filter matching syntax in `UPSERT` operations (experimental) +### Filter matching syntax in `UPSERT` operations Version 3.12 introduces an alternative syntax for [`UPSERT` operations](../../aql/high-level-operations/upsert.md) that allows -you to use dynamic attribute names to look up documents. Previously, +you to use more flexible filter conditions to look up documents. Previously, the expression used to look up a document had to be an object literal. -You can now use a `FILTER` statement for the `UPSERT` operation. The arbitrary +You can now use a `FILTER` statement for the `UPSERT` operation. The filter condition for the lookup can make use of the pseudo-variable `CURRENT` -and it can access the document and apply more filters on it than just +to access the lookup document and apply more filters on it than just equality matches. {{< tabs groupid="UPSERT syntax" >}} From d6fd187e73c1dfd2f2de5b5aae756f28d0de9546 Mon Sep 17 00:00:00 2001 From: Paula Date: Fri, 24 Nov 2023 00:27:10 +1100 Subject: [PATCH 4/6] change naming > more flexible filter conditions --- site/content/3.12/aql/high-level-operations/upsert.md | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/site/content/3.12/aql/high-level-operations/upsert.md b/site/content/3.12/aql/high-level-operations/upsert.md index 4c794e0d16..b627890695 100644 --- a/site/content/3.12/aql/high-level-operations/upsert.md +++ b/site/content/3.12/aql/high-level-operations/upsert.md @@ -68,9 +68,9 @@ document using the `OLD` pseudo-value. ## Filter matching syntax -The alternative syntax for `UPSERT` operations allows you to use dynamic -attribute names to look up documents. This means that the expression you use to -look up a document does not have to be an object literal. +The alternative syntax for `UPSERT` operations allows you to use more flexible +filter conditions to look up documents. This means that the expression you use +to look up a document does not have to be an object literal.
UPSERT FILTERfilter-condition
 INSERT insertExpression
@@ -92,7 +92,8 @@ INSERT { name: 'superuser', logins: 1, dateCreated: DATE_NOW() }
 UPDATE { logins: OLD.logins + 1 } IN users
 ```
 
-While there can only be one `FILTER` statement in this version of `UPSERT`, the `FILTER` statement can also use operators such as `&&` and `||` to make
+While there can only be one `FILTER` statement in this version of `UPSERT`, the
+`FILTER` statement can also use operators such as `&&` and `||` to make
 more complex filter conditions.
 
 ```aql

From b97eb0948d969e1732e5ae6abd24907e125376d2 Mon Sep 17 00:00:00 2001
From: Simran Spiller 
Date: Wed, 29 Nov 2023 14:53:54 +0100
Subject: [PATCH 5/6] Review, improve example, mention index utilization,
 inverted index cannot be used

---
 .../3.10/aql/high-level-operations/upsert.md  | 65 +++++++------
 .../3.11/aql/high-level-operations/upsert.md  | 65 +++++++------
 .../3.12/aql/high-level-operations/upsert.md  | 94 +++++++++++--------
 .../version-3.12/whats-new-in-3-12.md         | 31 +++---
 4 files changed, 147 insertions(+), 108 deletions(-)

diff --git a/site/content/3.10/aql/high-level-operations/upsert.md b/site/content/3.10/aql/high-level-operations/upsert.md
index f8faa16c33..b0b6486173 100644
--- a/site/content/3.10/aql/high-level-operations/upsert.md
+++ b/site/content/3.10/aql/high-level-operations/upsert.md
@@ -3,10 +3,16 @@ title: '`UPSERT` operation in AQL'
 menuTitle: UPSERT
 weight: 70
 description: >-
-  The `UPSERT` operations either modifies an existing document, or creates a new
+  An `UPSERT` operation either modifies an existing document, or creates a new
   document if it does not exist
 archetype: default
 ---
+`UPSERT` looks up a single document that matches the provided example or filter
+conditions. If there is no match, an insert operation is executed to create a
+document. If a document is found, you can either update or replace the document.
+These subtypes are called **upsert** (update or insert) and **repsert**
+(replace or insert).
+
 Each `UPSERT` operation is restricted to a single collection, and the 
 [collection name](../../concepts/data-structure/collections.md#collection-names) must not be dynamic.
 Only a single `UPSERT` statement per collection is allowed per AQL query, and 
@@ -15,13 +21,15 @@ traversal operations, or AQL functions that can read documents.
 
 ## Syntax
 
-The syntax for upsert and repsert operations is:
+The syntax for an upsert operation:
 
 
UPSERT searchExpression
 INSERT insertExpression
 UPDATE updateExpression
 IN collection
+The syntax for a repsert operation: +
UPSERT searchExpression
 INSERT insertExpression
 REPLACE updateExpression
@@ -29,30 +37,31 @@ IN collection
Both variants can optionally end with an `OPTIONS { … }` clause. -When using the `UPDATE` variant of the upsert operation, the found document -will be partially updated, meaning only the attributes specified in -*updateExpression* will be updated or added. When using the `REPLACE` variant -of upsert (repsert), existing documents will be replaced with the contexts of +When using the `UPDATE` variant of the `UPSERT` operation, the found document +is partially updated, meaning only the attributes specified in +*updateExpression* are updated or added. When using the `REPLACE` variant +of `UPSERT` (repsert), the found document is replaced with the content of *updateExpression*. -Updating a document will modify the document's revision number with a server-generated value. -The system attributes `_id`, `_key` and `_rev` cannot be updated, `_from` and `_to` can. +Updating a document modifies the document's revision number with a server-generated value. +The system attributes `_id`, `_key`, and `_rev` cannot be updated, but `_from` and `_to` +can be modified. -The *searchExpression* contains the document to be looked for. It must be an object -literal without dynamic attribute names. In case no such document can be found in -*collection*, a new document will be inserted into the collection as specified in the -*insertExpression*. +The *searchExpression* contains the document to be looked for. It must be an +**object literal** (`UPSERT { : , ... } ...`) without dynamic +attribute names. In case no such document can be found in *collection*, a new +document is inserted into the collection as specified in the *insertExpression*. -In case at least one document in *collection* matches the *searchExpression*, it will -be updated using the *updateExpression*. When more than one document in the collection -matches the *searchExpression*, it is undefined which of the matching documents will -be updated. It is therefore often sensible to make sure by other means (such as unique +In case at least one document in *collection* matches the *searchExpression*, it is +updated using the *updateExpression*. When more than one document in the collection +matches the *searchExpression*, it is undefined which of the matching documents is +updated. It is therefore often sensible to make sure by other means (such as unique indexes, application logic etc.) that at most one document matches *searchExpression*. -The following query will look in the *users* collection for a document with a specific -*name* attribute value. If the document exists, its *logins* attribute will be increased -by one. If it does not exist, a new document will be inserted, consisting of the -attributes *name*, *logins*, and *dateCreated*: +The following query looks for a document in the `users` collection with a specific +`name` attribute value. If the document exists, its *logins* attribute is increased +by one. If it does not exist, a new document is inserted, consisting of the +attributes `name`, `logins`, and `dateCreated`: ```aql UPSERT { name: 'superuser' } @@ -96,7 +105,7 @@ inside of arrays (e.g. `{ attr: [ { nested: null } ] }`). ### `mergeObjects` -The option `mergeObjects` controls whether object contents will be +The option `mergeObjects` controls whether object contents are merged if an object attribute is present in both the `UPDATE` query and in the to-be-updated document. @@ -125,9 +134,9 @@ FOR i IN 1..1000 ``` {{< info >}} -You need to add the `_rev` value in the *updateExpression*. It will not be used +You need to add the `_rev` value in the *updateExpression*. It is not used within the *searchExpression*. Even worse, if you use an outdated `_rev` in the -*searchExpression*, `UPSERT` will trigger the `INSERT` path instead of the +*searchExpression*, `UPSERT` triggers the `INSERT` path instead of the `UPDATE` path, because it has not found a document exactly matching the *searchExpression*. {{< /info >}} @@ -153,7 +162,7 @@ FOR i IN 1..1000 ### `indexHint` -The `indexHint` option will be used as a hint for the document lookup +The `indexHint` option is used as a hint for the document lookup performed as part of the `UPSERT` operation, and can help in cases such as `UPSERT` not picking the best index automatically. @@ -167,6 +176,8 @@ UPSERT { a: 1234 } The index hint is passed through to an internal `FOR` loop that is used for the lookup. Also see [`indexHint` Option of the `FOR` Operation](for.md#indexhint). +Inverted indexes cannot be used for `UPSERT` lookups. + ### `forceIndexHint` Makes the index or indexes specified in `indexHint` mandatory if enabled. The @@ -185,11 +196,11 @@ UPSERT { a: 1234 } `UPSERT` statements can optionally return data. To do so, they need to be followed by a `RETURN` statement (intermediate `LET` statements are allowed, too). These statements can optionally perform calculations and refer to the pseudo-values `OLD` and `NEW`. -In case the upsert performed an insert operation, `OLD` will have a value of `null`. -In case the upsert performed an update or replace operation, `OLD` will contain the +In case the upsert performed an insert operation, `OLD` has a value of `null`. +In case the upsert performed an update or replace operation, `OLD` contains the previous version of the document, before update/replace. -`NEW` will always be populated. It will contain the inserted document in case the +`NEW` is always populated. It contains the inserted document in case the upsert performed an insert, or the updated/replaced document in case it performed an update/replace. diff --git a/site/content/3.11/aql/high-level-operations/upsert.md b/site/content/3.11/aql/high-level-operations/upsert.md index f8faa16c33..b0b6486173 100644 --- a/site/content/3.11/aql/high-level-operations/upsert.md +++ b/site/content/3.11/aql/high-level-operations/upsert.md @@ -3,10 +3,16 @@ title: '`UPSERT` operation in AQL' menuTitle: UPSERT weight: 70 description: >- - The `UPSERT` operations either modifies an existing document, or creates a new + An `UPSERT` operation either modifies an existing document, or creates a new document if it does not exist archetype: default --- +`UPSERT` looks up a single document that matches the provided example or filter +conditions. If there is no match, an insert operation is executed to create a +document. If a document is found, you can either update or replace the document. +These subtypes are called **upsert** (update or insert) and **repsert** +(replace or insert). + Each `UPSERT` operation is restricted to a single collection, and the [collection name](../../concepts/data-structure/collections.md#collection-names) must not be dynamic. Only a single `UPSERT` statement per collection is allowed per AQL query, and @@ -15,13 +21,15 @@ traversal operations, or AQL functions that can read documents. ## Syntax -The syntax for upsert and repsert operations is: +The syntax for an upsert operation:
UPSERT searchExpression
 INSERT insertExpression
 UPDATE updateExpression
 IN collection
+The syntax for a repsert operation: +
UPSERT searchExpression
 INSERT insertExpression
 REPLACE updateExpression
@@ -29,30 +37,31 @@ IN collection
Both variants can optionally end with an `OPTIONS { … }` clause. -When using the `UPDATE` variant of the upsert operation, the found document -will be partially updated, meaning only the attributes specified in -*updateExpression* will be updated or added. When using the `REPLACE` variant -of upsert (repsert), existing documents will be replaced with the contexts of +When using the `UPDATE` variant of the `UPSERT` operation, the found document +is partially updated, meaning only the attributes specified in +*updateExpression* are updated or added. When using the `REPLACE` variant +of `UPSERT` (repsert), the found document is replaced with the content of *updateExpression*. -Updating a document will modify the document's revision number with a server-generated value. -The system attributes `_id`, `_key` and `_rev` cannot be updated, `_from` and `_to` can. +Updating a document modifies the document's revision number with a server-generated value. +The system attributes `_id`, `_key`, and `_rev` cannot be updated, but `_from` and `_to` +can be modified. -The *searchExpression* contains the document to be looked for. It must be an object -literal without dynamic attribute names. In case no such document can be found in -*collection*, a new document will be inserted into the collection as specified in the -*insertExpression*. +The *searchExpression* contains the document to be looked for. It must be an +**object literal** (`UPSERT { : , ... } ...`) without dynamic +attribute names. In case no such document can be found in *collection*, a new +document is inserted into the collection as specified in the *insertExpression*. -In case at least one document in *collection* matches the *searchExpression*, it will -be updated using the *updateExpression*. When more than one document in the collection -matches the *searchExpression*, it is undefined which of the matching documents will -be updated. It is therefore often sensible to make sure by other means (such as unique +In case at least one document in *collection* matches the *searchExpression*, it is +updated using the *updateExpression*. When more than one document in the collection +matches the *searchExpression*, it is undefined which of the matching documents is +updated. It is therefore often sensible to make sure by other means (such as unique indexes, application logic etc.) that at most one document matches *searchExpression*. -The following query will look in the *users* collection for a document with a specific -*name* attribute value. If the document exists, its *logins* attribute will be increased -by one. If it does not exist, a new document will be inserted, consisting of the -attributes *name*, *logins*, and *dateCreated*: +The following query looks for a document in the `users` collection with a specific +`name` attribute value. If the document exists, its *logins* attribute is increased +by one. If it does not exist, a new document is inserted, consisting of the +attributes `name`, `logins`, and `dateCreated`: ```aql UPSERT { name: 'superuser' } @@ -96,7 +105,7 @@ inside of arrays (e.g. `{ attr: [ { nested: null } ] }`). ### `mergeObjects` -The option `mergeObjects` controls whether object contents will be +The option `mergeObjects` controls whether object contents are merged if an object attribute is present in both the `UPDATE` query and in the to-be-updated document. @@ -125,9 +134,9 @@ FOR i IN 1..1000 ``` {{< info >}} -You need to add the `_rev` value in the *updateExpression*. It will not be used +You need to add the `_rev` value in the *updateExpression*. It is not used within the *searchExpression*. Even worse, if you use an outdated `_rev` in the -*searchExpression*, `UPSERT` will trigger the `INSERT` path instead of the +*searchExpression*, `UPSERT` triggers the `INSERT` path instead of the `UPDATE` path, because it has not found a document exactly matching the *searchExpression*. {{< /info >}} @@ -153,7 +162,7 @@ FOR i IN 1..1000 ### `indexHint` -The `indexHint` option will be used as a hint for the document lookup +The `indexHint` option is used as a hint for the document lookup performed as part of the `UPSERT` operation, and can help in cases such as `UPSERT` not picking the best index automatically. @@ -167,6 +176,8 @@ UPSERT { a: 1234 } The index hint is passed through to an internal `FOR` loop that is used for the lookup. Also see [`indexHint` Option of the `FOR` Operation](for.md#indexhint). +Inverted indexes cannot be used for `UPSERT` lookups. + ### `forceIndexHint` Makes the index or indexes specified in `indexHint` mandatory if enabled. The @@ -185,11 +196,11 @@ UPSERT { a: 1234 } `UPSERT` statements can optionally return data. To do so, they need to be followed by a `RETURN` statement (intermediate `LET` statements are allowed, too). These statements can optionally perform calculations and refer to the pseudo-values `OLD` and `NEW`. -In case the upsert performed an insert operation, `OLD` will have a value of `null`. -In case the upsert performed an update or replace operation, `OLD` will contain the +In case the upsert performed an insert operation, `OLD` has a value of `null`. +In case the upsert performed an update or replace operation, `OLD` contains the previous version of the document, before update/replace. -`NEW` will always be populated. It will contain the inserted document in case the +`NEW` is always populated. It contains the inserted document in case the upsert performed an insert, or the updated/replaced document in case it performed an update/replace. diff --git a/site/content/3.12/aql/high-level-operations/upsert.md b/site/content/3.12/aql/high-level-operations/upsert.md index b627890695..5e0ec1931f 100644 --- a/site/content/3.12/aql/high-level-operations/upsert.md +++ b/site/content/3.12/aql/high-level-operations/upsert.md @@ -3,10 +3,16 @@ title: '`UPSERT` operation in AQL' menuTitle: UPSERT weight: 70 description: >- - The `UPSERT` operations either modifies an existing document, or creates a new + An `UPSERT` operation either modifies an existing document, or creates a new document if it does not exist archetype: default --- +`UPSERT` looks up a single document that matches the provided example or filter +conditions. If there is no match, an insert operation is executed to create a +document. If a document is found, you can either update or replace the document. +These subtypes are called **upsert** (update or insert) and **repsert** +(replace or insert). + Each `UPSERT` operation is restricted to a single collection, and the [collection name](../../concepts/data-structure/collections.md#collection-names) must not be dynamic. Only a single `UPSERT` statement per collection is allowed per AQL query, and @@ -15,16 +21,18 @@ traversal operations, or AQL functions that can read documents. ## Exact-value matching syntax -This syntax can only look up documents by exact-value matching and does not -support arbitrary filter conditions. +This syntax can only look up documents by exact-value matching using an +object literal and does not support arbitrary filter conditions. -The exact-value matching syntax for upsert and repsert operations is: +The syntax for an upsert operation:
UPSERT searchExpression
 INSERT insertExpression
 UPDATE updateExpression
 IN collection
+The syntax for a repsert operation: +
UPSERT searchExpression
 INSERT insertExpression
 REPLACE updateExpression
@@ -32,30 +40,31 @@ IN collection
Both variants can optionally end with an `OPTIONS { … }` clause. -When using the `UPDATE` variant of the upsert operation, the found document -will be partially updated, meaning only the attributes specified in -*updateExpression* will be updated or added. When using the `REPLACE` variant -of upsert (repsert), existing documents will be replaced with the contexts of +When using the `UPDATE` variant of the `UPSERT` operation, the found document +is partially updated, meaning only the attributes specified in +*updateExpression* are updated or added. When using the `REPLACE` variant +of `UPSERT` (repsert), the found document is replaced with the content of *updateExpression*. -Updating a document will modify the document's revision number with a server-generated value. -The system attributes `_id`, `_key` and `_rev` cannot be updated, `_from` and `_to` can. +Updating a document modifies the document's revision number with a server-generated value. +The system attributes `_id`, `_key`, and `_rev` cannot be updated, but `_from` and `_to` +can be modified. -The *searchExpression* contains the document to be looked for. It must be an object -literal without dynamic attribute names. In case no such document can be found in -*collection*, a new document will be inserted into the collection as specified in the -*insertExpression*. +The *searchExpression* contains the document to be looked for. It must be an +**object literal** (`UPSERT { : , ... } ...`) without dynamic +attribute names. In case no such document can be found in *collection*, a new +document is inserted into the collection as specified in the *insertExpression*. -In case at least one document in *collection* matches the *searchExpression*, it will -be updated using the *updateExpression*. When more than one document in the collection -matches the *searchExpression*, it is undefined which of the matching documents will -be updated. It is therefore often sensible to make sure by other means (such as unique +In case at least one document in *collection* matches the *searchExpression*, it is +updated using the *updateExpression*. When more than one document in the collection +matches the *searchExpression*, it is undefined which of the matching documents is +updated. It is therefore often sensible to make sure by other means (such as unique indexes, application logic etc.) that at most one document matches *searchExpression*. -The following query will look in the *users* collection for a document with a specific -*name* attribute value. If the document exists, its *logins* attribute will be increased -by one. If it does not exist, a new document will be inserted, consisting of the -attributes *name*, *logins*, and *dateCreated*: +The following query looks for a document in the `users` collection with a specific +`name` attribute value. If the document exists, its *logins* attribute is increased +by one. If it does not exist, a new document is inserted, consisting of the +attributes `name`, `logins`, and `dateCreated`: ```aql UPSERT { name: 'superuser' } @@ -69,22 +78,26 @@ document using the `OLD` pseudo-value. ## Filter matching syntax The alternative syntax for `UPSERT` operations allows you to use more flexible -filter conditions to look up documents. This means that the expression you use -to look up a document does not have to be an object literal. +filter conditions beyond equality matches to look up documents. + +The syntax for an upsert operation:
UPSERT FILTERfilter-condition
 INSERT insertExpression
 UPDATE updateExpression
 IN collection
+The syntax for a repsert operation: +
UPSERT FILTERfilter-condition
 INSERT insertExpression
 REPLACE updateExpression
 IN collection
-The filter condition for the lookup can make use of the pseudo-variable -`CURRENT` to access the lookup document and apply more filters on it than just -equality matches. Example: +Both variants can optionally end with an `OPTIONS { … }` clause. + +The filter condition for the lookup can make use of the `CURRENT` pseudo-variable +to access the lookup document. Example: ```aql UPSERT FILTER CURRENT.name == 'superuser' @@ -92,13 +105,14 @@ INSERT { name: 'superuser', logins: 1, dateCreated: DATE_NOW() } UPDATE { logins: OLD.logins + 1 } IN users ``` -While there can only be one `FILTER` statement in this version of `UPSERT`, the -`FILTER` statement can also use operators such as `&&` and `||` to make -more complex filter conditions. +The `FILTER` expression can contain operators such as `AND` and `OR` to create +complex filter conditions. You can apply all kinds of filters on the +`CURRENT` pseudo-variable, and indexes may get utilized to accelerate the lookup +if they are eligible. ```aql -UPSERT FILTER CURRENT.name == 'superuser' && CURRENT.active == true && (LOWER(CURRENT.member) == true || CURRENT.age == 33) -INSERT { name: 'superuser', logins: 1, dateCreated: DATE_NOW() } +UPSERT FILTER CURRENT.age < 30 AND (STARTS_WITH(CURRENT.name, "Jo") OR CURRENT.gender IN ["f", "x"]) +INSERT { name: 'Jordan', age: 29, logins: 1, dateCreated: DATE_NOW() } UPDATE { logins: OLD.logins + 1 } IN users ``` @@ -143,7 +157,7 @@ inside of arrays (e.g. `{ attr: [ { nested: null } ] }`). ### `mergeObjects` -The option `mergeObjects` controls whether object contents will be +The option `mergeObjects` controls whether object contents are merged if an object attribute is present in both the `UPDATE` query and in the to-be-updated document. @@ -172,9 +186,9 @@ FOR i IN 1..1000 ``` {{< info >}} -You need to add the `_rev` value in the *updateExpression*. It will not be used +You need to add the `_rev` value in the *updateExpression*. It is not used within the *searchExpression*. Even worse, if you use an outdated `_rev` in the -*searchExpression*, `UPSERT` will trigger the `INSERT` path instead of the +*searchExpression*, `UPSERT` triggers the `INSERT` path instead of the `UPDATE` path, because it has not found a document exactly matching the *searchExpression*. {{< /info >}} @@ -200,7 +214,7 @@ FOR i IN 1..1000 ### `indexHint` -The `indexHint` option will be used as a hint for the document lookup +The `indexHint` option is used as a hint for the document lookup performed as part of the `UPSERT` operation, and can help in cases such as `UPSERT` not picking the best index automatically. @@ -214,6 +228,8 @@ UPSERT { a: 1234 } The index hint is passed through to an internal `FOR` loop that is used for the lookup. Also see [`indexHint` Option of the `FOR` Operation](for.md#indexhint). +Inverted indexes cannot be used for `UPSERT` lookups. + ### `forceIndexHint` Makes the index or indexes specified in `indexHint` mandatory if enabled. The @@ -232,11 +248,11 @@ UPSERT { a: 1234 } `UPSERT` statements can optionally return data. To do so, they need to be followed by a `RETURN` statement (intermediate `LET` statements are allowed, too). These statements can optionally perform calculations and refer to the pseudo-values `OLD` and `NEW`. -In case the upsert performed an insert operation, `OLD` will have a value of `null`. -In case the upsert performed an update or replace operation, `OLD` will contain the +In case the upsert performed an insert operation, `OLD` has a value of `null`. +In case the upsert performed an update or replace operation, `OLD` contains the previous version of the document, before update/replace. -`NEW` will always be populated. It will contain the inserted document in case the +`NEW` is always populated. It contains the inserted document in case the upsert performed an insert, or the updated/replaced document in case it performed an update/replace. diff --git a/site/content/3.12/release-notes/version-3.12/whats-new-in-3-12.md b/site/content/3.12/release-notes/version-3.12/whats-new-in-3-12.md index 9f3b812a6e..5b4cba4880 100644 --- a/site/content/3.12/release-notes/version-3.12/whats-new-in-3-12.md +++ b/site/content/3.12/release-notes/version-3.12/whats-new-in-3-12.md @@ -130,31 +130,31 @@ Swagger 2.x compatibility. ## AQL -### Filter matching syntax in `UPSERT` operations +### Filter matching syntax for `UPSERT` operations Version 3.12 introduces an alternative syntax for [`UPSERT` operations](../../aql/high-level-operations/upsert.md) that allows you to use more flexible filter conditions to look up documents. Previously, -the expression used to look up a document had to be an object literal. +the expression used to look up a document had to be an object literal, and this +is now called the "exact-value matching" syntax. -You can now use a `FILTER` statement for the `UPSERT` operation. The -filter condition for the lookup can make use of the pseudo-variable `CURRENT` -to access the lookup document and apply more filters on it than just -equality matches. +The new "filter matching" syntax lets you use a `FILTER` statement for the +`UPSERT` operation. The filter condition for the lookup can make use of the +`CURRENT` pseudo-variable to access the lookup document. -{{< tabs groupid="UPSERT syntax" >}} +{{< tabs groupid="upsert-syntax" >}} -{{< tab name="Exact-value matching" >}} +{{< tab name="Filter matching" >}} ```aql -UPSERT { name: 'superuser' } +UPSERT FILTER CURRENT.name == 'superuser' INSERT { name: 'superuser', logins: 1, dateCreated: DATE_NOW() } UPDATE { logins: OLD.logins + 1 } IN users ``` {{< /tab >}} -{{< tab name="Using FILTER conditions" >}} +{{< tab name="Exact-value matching" >}} ```aql -UPSERT FILTER CURRENT.name == 'superuser' +UPSERT { name: 'superuser' } INSERT { name: 'superuser', logins: 1, dateCreated: DATE_NOW() } UPDATE { logins: OLD.logins + 1 } IN users ``` @@ -162,12 +162,13 @@ UPDATE { logins: OLD.logins + 1 } IN users {{< /tabs >}} -The `FILTER` statement can also use operators such as `&&` and `||` to make -more complex filter conditions. +The `FILTER` expression can contain operators such as `AND` and `OR` to create +complex filter conditions, and you can apply more filters on the `CURRENT` +pseudo-variable than just equality matches. ```aql -UPSERT FILTER CURRENT.name == 'superuser' && CURRENT.active == true && (LOWER(CURRENT.member) == true || CURRENT.age == 33) -INSERT { name: 'superuser', logins: 1, dateCreated: DATE_NOW() } +UPSERT FILTER CURRENT.age < 30 AND (STARTS_WITH(CURRENT.name, "Jo") OR CURRENT.gender IN ["f", "x"]) +INSERT { name: 'Jordan', age: 29, logins: 1, dateCreated: DATE_NOW() } UPDATE { logins: OLD.logins + 1 } IN users ``` From 1231541f39180c174c2ba1fccc9bf919e04c3fd6 Mon Sep 17 00:00:00 2001 From: Simran Spiller Date: Wed, 29 Nov 2023 15:05:34 +0100 Subject: [PATCH 6/6] Remove incorrect statement --- site/content/3.10/aql/high-level-operations/upsert.md | 4 ++-- site/content/3.11/aql/high-level-operations/upsert.md | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/site/content/3.10/aql/high-level-operations/upsert.md b/site/content/3.10/aql/high-level-operations/upsert.md index e93d2dc7fc..caf694d0be 100644 --- a/site/content/3.10/aql/high-level-operations/upsert.md +++ b/site/content/3.10/aql/high-level-operations/upsert.md @@ -7,8 +7,8 @@ description: >- document if it does not exist archetype: default --- -`UPSERT` looks up a single document that matches the provided example or filter -conditions. If there is no match, an insert operation is executed to create a +`UPSERT` looks up a single document that matches the provided example. +If there is no match, an insert operation is executed to create a document. If a document is found, you can either update or replace the document. These subtypes are called **upsert** (update or insert) and **repsert** (replace or insert). diff --git a/site/content/3.11/aql/high-level-operations/upsert.md b/site/content/3.11/aql/high-level-operations/upsert.md index e93d2dc7fc..caf694d0be 100644 --- a/site/content/3.11/aql/high-level-operations/upsert.md +++ b/site/content/3.11/aql/high-level-operations/upsert.md @@ -7,8 +7,8 @@ description: >- document if it does not exist archetype: default --- -`UPSERT` looks up a single document that matches the provided example or filter -conditions. If there is no match, an insert operation is executed to create a +`UPSERT` looks up a single document that matches the provided example. +If there is no match, an insert operation is executed to create a document. If a document is found, you can either update or replace the document. These subtypes are called **upsert** (update or insert) and **repsert** (replace or insert).