1
0
Fork 0
arangodb/Documentation/Books/AQL/DataQueries.mdpp

316 lines
9.5 KiB
Plaintext

Data Queries
============
Data Access Queries
-------------------
Retrieving data from the database with AQL does always include a **RETURN**
operation. It can be used to return a static value, such as a string:
```js
RETURN "Hello ArangoDB!"
```
The query result is always an array of elements, even if a single element was
returned and contains a single element in that case: `["Hello ArangoDB!"]`
The function `DOCUMENT()` can be called to retrieve a single document via
its document handle, for instance:
```js
RETURN DOCUMENT("users/phil")
```
*RETURN* is usually accompanied by a **FOR** loop to iterate over the
documents of a collection. The following query executes the loop body for all
documents of a collection called *users*. Each document is returned unchanged
in this example:
```js
FOR doc IN users
RETURN doc
```
Instead of returning the raw `doc`, one can easily create a projection:
```js
FOR doc IN users
RETURN { user: doc, newAttribute: true }
```
For every user document, an object with two attributes is returned. The value
of the attribute *user* is set to the content of the user document, and
*newAttribute* is a static attribute with the boolean value *true*.
Operations like **FILTER**, **SORT** and **LIMIT** can be added to the loop body
to narrow and order the result. Instead of above shown call to `DOCUMENT()`,
one can also retrieve the document that describes user *phil* like so:
```js
FOR doc IN users
FILTER doc._key == "phil"
RETURN doc
```
The document key is used in this example, but any other attribute could equally
be used for filtering. Since the document key is guaranteed to be unique, no
more than a single document will match this filter. For other attributes this
may not be the case. To return a subset of active users (determined by an
attribute called *status*), sorted by name in ascending order, you can do:
```js
FOR doc IN users
FILTER doc.status == "active"
SORT doc.name
LIMIT 10
```
Note that operations do not have to occur in a fixed order and that their order
can influence the result significantly. Limiting the number of documents
before a filter is usually not what you want, because it easily misses a lot
of documents that would fulfill the filter criterion, but are ignored because
of a premature *LIMIT* clause. Because of the aforementioned reasons, *LIMIT*
is usually put at the very end, after *FILTER*, *SORT* and other operations.
See the [High Level Operations](Operations/README.md) chapter for more details.
Data Modification Queries
-------------------------
AQL supports the following data-modification operations:
- **INSERT**: insert new documents into a collection
- **UPDATE**: partially update existing documents in a collection
- **REPLACE**: completely replace existing documents in a collection
- **REMOVE**: remove existing documents from a collection
- **UPSERT**: conditionally insert or update documents in a collection
Below you find some simple example queries that use these operations.
The operations are detailed in the chapter [High Level Operations](Operations/README.md).
### Modifying a single document
Let's start with the basics: `INSERT`, `UPDATE` and `REMOVE` operations on single documents.
Here is an example that insert a document in an existing collection *users*:
```js
INSERT {
firstName: "Anna",
name: "Pavlova",
profession: "artist"
} IN users
```
You may provide a key for the new document; if not provided, ArangoDB will create one for you.
```js
INSERT {
_key: "GilbertoGil",
firstName: "Gilberto",
name: "Gil",
city: "Fortalezza"
} IN users
```
As ArangoDB is schema-free, attributes of the documents may vary:
```js
INSERT {
_key: "PhilCarpenter",
firstName: "Phil",
name: "Carpenter",
middleName: "G.",
status: "inactive"
} IN users
```
```js
INSERT {
_key: "NatachaDeclerck",
firstName: "Natacha",
name: "Declerck",
location: "Antwerp"
} IN users
```
Update is quite simple. The following AQL statement will add or change the attributes status and location
```js
UPDATE "PhilCarpenter" WITH {
status: "active",
location: "Beijing"
} IN users
```
Replace is an alternative to update where all attributes of the document are replaced.
```js
REPLACE {
_key: "NatachaDeclerck",
firstName: "Natacha",
name: "Leclerc",
status: "active",
level: "premium"
} IN users
```
Removing a document if you know its key is simple as well :
```js
REMOVE "GilbertoGil" IN users
```
or
```js
REMOVE { _key: "GilbertoGil" } IN users
```
### Modifying multiple documents
Data-modification operations are normally combined with *FOR* loops to
iterate over a given list of documents. They can optionally be combined with
*FILTER* statements and the like.
Let's start with an example that modifies existing documents in a collection
*users* that match some condition:
```js
FOR u IN users
FILTER u.status == "not active"
UPDATE u WITH { status: "inactive" } IN users
```
Now, let's copy the contents of the collection *users* into the collection
*backup*:
```js
FOR u IN users
INSERT u IN backup
```
As a final example, let's find some documents in collection *users* and
remove them from collection *backup*. The link between the documents in both
collections is established via the documents' keys:
```js
FOR u IN users
FILTER u.status == "deleted"
REMOVE u IN backup
```
### Returning documents
Data-modification queries can optionally return documents. In order to reference
the inserted, removed or modified documents in a `RETURN` statement, data-modification
statements introduce the `OLD` and/or `NEW` pseudo-values:
```js
FOR i IN 1..100
INSERT { value: i } IN test
RETURN NEW
```
```js
FOR u IN users
FILTER u.status == "deleted"
REMOVE u IN users
RETURN OLD
```
```js
FOR u IN users
FILTER u.status == "not active"
UPDATE u WITH { status: "inactive" } IN users
RETURN NEW
```
`NEW` refers to the inserted or modified document revision, and `OLD` refers
to the document revision before update or removal. `INSERT` statements can
only refer to the `NEW` pseudo-value, and `REMOVE` operations only to `OLD`.
`UPDATE`, `REPLACE` and `UPSERT` can refer to either.
In all cases the full documents will be returned with all their attributes,
including the potentially auto-generated attributes such as `_id`, `_key`, or `_rev`
and the attributes not specified in the update expression of a partial update.
#### Projections
It is possible to return a projection of the documents in `OLD` or `NEW` instead of
returning the entire documents. This can be used to reduce the amount of data returned
by queries.
For example, the following query will return only the keys of the inserted documents:
```js
FOR i IN 1..100
INSERT { value: i } IN test
RETURN NEW._key
```
#### Using OLD and NEW in the same query
For `UPDATE`, `REPLACE` and `UPSERT` statements, both `OLD` and `NEW` can be used
to return the previous revision of a document together with the updated revision:
```js
FOR u IN users
FILTER u.status == "not active"
UPDATE u WITH { status: "inactive" } IN users
RETURN { old: OLD, new: NEW }
```
#### Calculations with OLD or NEW
It is also possible to run additional calculations with `LET` statements between the
data-modification part and the final `RETURN` of an AQL query. For example, the following
query performs an upsert operation and returns whether an existing document was
updated, or a new document was inserted. It does so by checking the `OLD` variable
after the `UPSERT` and using a `LET` statement to store a temporary string for
the operation type:
```js
UPSERT { name: "test" }
INSERT { name: "test" }
UPDATE { } IN users
LET opType = IS_NULL(OLD) ? "insert" : "update"
RETURN { _key: NEW._key, type: opType }
```
### Restrictions
The name of the modified collection (*users* and *backup* in the above cases)
must be known to the AQL executor at query-compile time and cannot change at
runtime. Using a bind parameter to specify the
[collection name](../Manual/Appendix/Glossary.html#collection-name) is allowed.
Data-modification queries are restricted to modifying data in a single
collection per query. That means a data-modification query cannot modify
data in multiple collections with a single query. It is still possible (and
was shown above) to read from one or many collections and modify data in another
within the same query.
Only a single data-modification operation can be used per AQL query. Data-modification
queries cannot be used inside subqueries. Data-modification operations can optionally
be followed by `LET` operations and a single `RETURN` operation to return data. If
expressions are used within these operations, they cannot contain subqueries or
access data in collections using AQL functions.
Finally, data-modification operations can optionally be followed by `LET` and `RETURN`,
but not by other statements such as `SORT`, `COLLECT` etc.
### Transactional Execution
On a single server, data-modification operations are executed transactionally.
If a data-modification operation fails, any changes made by it will be rolled
back automatically as if they never happened.
In a cluster, AQL data-modification queries are currently not executed transactionally.
Additionally, *update*, *replace*, *upsert* and *remove* AQL queries currently
require the *_key* attribute to be specified for all documents that should be
modified or removed, even if a shared key attribute other than *_key* was chosen
for the collection. This restriction may be overcome in a future release of ArangoDB.