1.CRUD
Insert documents
INSERT document INTO collectionName
INSERT {
"name": "Ned",
"surname": "Stark",
"alive": true,
"age": 41,
"traits": ["A","H","C","N","P"]
} INTO Characters
LET data = [
{ "name": "Robert", "surname": "Baratheon", "alive": false, "traits": ["A","H","C"] },
{ "name": "Jaime", "surname": "Lannister", "alive": true, "age": 36, "traits": ["A","F","B"] },
{ "name": "Catelyn", "surname": "Stark", "alive": false, "age": 40, "traits": ["D","H","C"] },
{ "name": "Cersei", "surname": "Lannister", "alive": true, "age": 36, "traits": ["H","E","F"] },
{ "name": "Daenerys", "surname": "Targaryen", "alive": true, "age": 16, "traits": ["D","H","C"] },
{ "name": "Jorah", "surname": "Mormont", "alive": false, "traits": ["A","B","C","F"] },
{ "name": "Petyr", "surname": "Baelish", "alive": false, "traits": ["E","G","F"] },
{ "name": "Viserys", "surname": "Targaryen", "alive": false, "traits": ["O","L","N"] },
{ "name": "Jon", "surname": "Snow", "alive": true, "age": 16, "traits": ["A","B","C","F"] },
{ "name": "Sansa", "surname": "Stark", "alive": true, "age": 13, "traits": ["D","I","J"] },
{ "name": "Arya", "surname": "Stark", "alive": true, "age": 11, "traits": ["C","K","L"] },
{ "name": "Robb", "surname": "Stark", "alive": false, "traits": ["A","B","C","K"] },
{ "name": "Theon", "surname": "Greyjoy", "alive": true, "age": 16, "traits": ["E","R","K"] },
{ "name": "Bran", "surname": "Stark", "alive": true, "age": 10, "traits": ["L","J"] },
{ "name": "Joffrey", "surname": "Baratheon", "alive": false, "age": 19, "traits": ["I","L","O"] },
{ "name": "Sandor", "surname": "Clegane", "alive": true, "traits": ["A","P","K","F"] },
{ "name": "Tyrion", "surname": "Lannister", "alive": true, "age": 32, "traits": ["F","K","M","N"] },
{ "name": "Khal", "surname": "Drogo", "alive": false, "traits": ["A","C","O","P"] },
{ "name": "Tywin", "surname": "Lannister", "alive": false, "traits": ["O","M","H","F"] },
{ "name": "Davos", "surname": "Seaworth", "alive": true, "age": 49, "traits": ["C","K","P","F"] },
{ "name": "Samwell", "surname": "Tarly", "alive": true, "age": 17, "traits": ["C","L","I"] },
{ "name": "Stannis", "surname": "Baratheon", "alive": false, "traits": ["H","O","P","M"] },
{ "name": "Melisandre", "alive": true, "traits": ["G","E","H"] },
{ "name": "Margaery", "surname": "Tyrell", "alive": false, "traits": ["M","D","B"] },
{ "name": "Jeor", "surname": "Mormont", "alive": false, "traits": ["C","H","M","P"] },
{ "name": "Bronn", "alive": true, "traits": ["K","E","C"] },
{ "name": "Varys", "alive": true, "traits": ["M","F","N","E"] },
{ "name": "Shae", "alive": false, "traits": ["M","D","G"] },
{ "name": "Talisa", "surname": "Maegyr", "alive": false, "traits": ["D","C","B"] },
{ "name": "Gendry", "alive": false, "traits": ["K","C","A"] },
{ "name": "Ygritte", "alive": false, "traits": ["A","P","K"] },
{ "name": "Tormund", "surname": "Giantsbane", "alive": true, "traits": ["C","P","A","I"] },
{ "name": "Gilly", "alive": true, "traits": ["L","J"] },
{ "name": "Brienne", "surname": "Tarth", "alive": true, "age": 32, "traits": ["P","C","A","K"] },
{ "name": "Ramsay", "surname": "Bolton", "alive": true, "traits": ["E","O","G","A"] },
{ "name": "Ellaria", "surname": "Sand", "alive": true, "traits": ["P","O","A","E"] },
{ "name": "Daario", "surname": "Naharis", "alive": true, "traits": ["K","P","A"] },
{ "name": "Missandei", "alive": true, "traits": ["D","L","C","M"] },
{ "name": "Tommen", "surname": "Baratheon", "alive": true, "traits": ["I","L","B"] },
{ "name": "Jaqen", "surname": "H'ghar", "alive": true, "traits": ["H","F","K"] },
{ "name": "Roose", "surname": "Bolton", "alive": true, "traits": ["H","E","F","A"] },
{ "name": "The High Sparrow", "alive": true, "traits": ["H","M","F","O"] }
]
FOR d IN data
INSERT d INTO Characters
=========================================================
Read documents
=========================================================
FOR c IN Characters
RETURN c
=========================================================
RETURN DOCUMENT("Characters", "2861650")
// --- or ---
RETURN DOCUMENT("Characters/2861650")
[
{
"_key": "2861650",
"_id": "Characters/2861650",
"_rev": "_V1bzsXa---",
"name": "Ned",
"surname": "Stark",
"alive": true,
"age": 41,
"traits": ["A","H","C","N","P"]
}
]
=========================================================
=========================================================
Update documents
=========================================================
UPDATE "2861650" WITH { alive: false } IN Characters
REPLACE "2861650" WITH {
name: "Ned",
surname: "Stark",
alive: false,
age: 41,
traits: ["A","H","C","N","P"]
} IN Characters
=========================================================
add a new attribute to all documents for instance:
FOR c IN Characters
UPDATE c WITH { season: 1 } IN Characters
=========================================================
=========================================================
Delete documents
=========================================================
REMOVE "2861650" IN Characters
=========================================================
FOR c IN Characters
REMOVE c IN Characters
=========================================================
2.Match
=========================================================
Equality condition
=========================================================
FOR c IN Characters
FILTER c.name == "Ned"
RETURN c
=========================================================
FOR c IN Characters
FILTER c.surname == "Stark"
RETURN c
=========================================================
=========================================================
Range conditions
=========================================================
FOR c IN Characters
FILTER c.age >= 13
RETURN c.name
[
"Joffrey",
"Tyrion",
"Samwell",
"Ned",
"Catelyn",
"Cersei",
"Jon",
"Sansa",
"Brienne",
"Theon",
"Davos",
"Jaime",
"Daenerys"
]
=========================================================
FOR c IN Characters
FILTER c.age < 13
RETURN { name: c.name, age: c.age }
[
{ "name": "Tommen", "age": null },
{ "name": "Arya", "age": 11 },
{ "name": "Roose", "age": null },
...
]
=========================================================
=========================================================
Multiple conditions
=========================================================
FOR c IN Characters
FILTER c.age < 13
FILTER c.age != null
RETURN { name: c.name, age: c.age }
[
{ "name": "Arya", "age": 11 },
{ "name": "Bran", "age": 10 }
]
=========================================================
FOR c IN Characters
FILTER c.age < 13 AND c.age != null
RETURN { name: c.name, age: c.age }
-------------------------------------------------------------
And the second condition could as well be c.age > null.
=========================================================
=========================================================
Alternative conditions
=========================================================
FOR c IN Characters
FILTER c.name == "Jon" OR c.name == "Joffrey"
RETURN { name: c.name, surname: c.surname }
[
{ "name": "Joffrey", "surname": "Baratheon" },
{ "name": "Jon", "surname": "Snow" }
]
=========================================================
3.SORT
=========================================================
Cap the result count
=========================================================
FOR c IN Characters
LIMIT 5
RETURN c.name
[
"Joffrey",
"Tommen",
"Tyrion",
"Roose",
"Tywin"
]
=========================================================
-----------------------------------------------
allows you to skip a certain amount of record and return the next n documents
-----------------------------------------------
FOR c IN Characters
LIMIT 2, 5
RETURN c.name
[
"Tyrion",
"Roose",
"Tywin",
"Samwell",
"Melisandre"
]
=========================================================
=========================================================
Sort by name
=========================================================
FOR c IN Characters
SORT c.name
LIMIT 10
RETURN c.name
[
"Arya",
"Bran",
"Brienne",
"Bronn",
"Catelyn",
"Cersei",
"Daario",
"Daenerys",
"Davos",
"Ellaria"
]
=========================================================
FOR c IN Characters
SORT c.name DESC
LIMIT 10
RETURN c.name
[
"Ygritte",
"Viserys",
"Varys",
"Tywin",
"Tyrion",
"Tormund",
"Tommen",
"Theon",
"The High Sparrow",
"Talisa"
]
=========================================================
=========================================================
Sort by multiple attributes
=========================================================
-------------------------------------------------------------
Note that a filter is applied before sorting, to only let documents through,
that actually feature a surname value (many don’t have it and would cause null values in the result)
--------------------------------------------------------------
FOR c IN Characters
FILTER c.surname
SORT c.surname, c.name
LIMIT 10
RETURN {
surname: c.surname,
name: c.name
}
[
{ "surname": "Baelish", "name": "Petyr" },
{ "surname": "Baratheon", "name": "Joffrey" },
{ "surname": "Baratheon", "name": "Robert" },
{ "surname": "Baratheon", "name": "Stannis" },
{ "surname": "Baratheon", "name": "Tommen" },
{ "surname": "Bolton", "name": "Ramsay" },
{ "surname": "Bolton", "name": "Roose" },
{ "surname": "Clegane", "name": "Sandor" },
{ "surname": "Drogo", "name": "Khal" },
{ "surname": "Giantsbane", "name": "Tormund" }
]
=========================================================
=========================================================
Sort by age
=========================================================
FOR c IN Characters
FILTER c.age
SORT c.age
LIMIT 10
RETURN {
name: c.name,
age: c.age
}
[
{ "name": "Bran", "age": 10 },
{ "name": "Arya", "age": 11 },
{ "name": "Sansa", "age": 13 },
{ "name": "Jon", "age": 16 },
{ "name": "Theon", "age": 16 },
{ "name": "Daenerys", "age": 16 },
{ "name": "Samwell", "age": 17 },
{ "name": "Joffrey", "age": 19 },
{ "name": "Tyrion", "age": 32 },
{ "name": "Brienne", "age": 32 }
]
=========================================================
4.JOIN
=============================================================
Importing traits
=============================================================
let data = [
{ "_key": "A", "en": "strong", "de": "stark" },
{ "_key": "B", "en": "polite", "de": "freundlich" },
{ "_key": "C", "en": "loyal", "de": "loyal" },
{ "_key": "D", "en": "beautiful", "de": "schön" },
{ "_key": "E", "en": "sneaky", "de": "hinterlistig" },
{ "_key": "F", "en": "experienced", "de": "erfahren" },
{ "_key": "G", "en": "corrupt", "de": "korrupt" },
{ "_key": "H", "en": "powerful", "de": "einflussreich" },
{ "_key": "I", "en": "naive", "de": "naiv" },
{ "_key": "J", "en": "unmarried", "de": "unverheiratet" },
{ "_key": "K", "en": "skillful", "de": "geschickt" },
{ "_key": "L", "en": "young", "de": "jung" },
{ "_key": "M", "en": "smart", "de": "klug" },
{ "_key": "N", "en": "rational", "de": "rational" },
{ "_key": "O", "en": "ruthless", "de": "skrupellos" },
{ "_key": "P", "en": "brave", "de": "mutig" },
{ "_key": "Q", "en": "mighty", "de": "mächtig" },
{ "_key": "R", "en": "weak", "de": "schwach" }
]
FOR d IN data
INSERT d INTO Traits
=============================================================
Resolving traits
=============================================================
FOR c IN Characters
RETURN c.traits
[
{ "traits": ["A","H","C","N","P"] },
{ "traits": ["D","H","C"] },
...
]
=============================================================
FOR c IN Characters
RETURN DOCUMENT("Traits", c.traits)
[
[
{
"_key": "A",
"_id": "Traits/A",
"_rev": "_V5oRUS2---",
"en": "strong",
"de": "stark"
},
{
"_key": "H",
"_id": "Traits/H",
"_rev": "_V5oRUS6--E",
"en": "powerful",
"de": "einflussreich"
},
{
"_key": "C",
"_id": "Traits/C",
"_rev": "_V5oRUS6--_",
"en": "loyal",
"de": "loyal"
},
{
"_key": "N",
"_id": "Traits/N",
"_rev": "_V5oRUT---D",
"en": "rational",
"de": "rational"
},
{
"_key": "P",
"_id": "Traits/P",
"_rev": "_V5oRUTC---",
"en": "brave",
"de": "mutig"
}
],
[
{
"_key": "D",
"_id": "Traits/D",
"_rev": "_V5oRUS6--A",
"en": "beautiful",
"de": "schön"
},
{
"_key": "H",
"_id": "Traits/H",
"_rev": "_V5oRUS6--E",
"en": "powerful",
"de": "einflussreich"
},
{
"_key": "C",
"_id": "Traits/C",
"_rev": "_V5oRUS6--_",
"en": "loyal",
"de": "loyal"
}
],
...
]
=============================================================
=============================================================
-------------------------------------------------------------
The DOCUMENT() function can be used to look up a single or multiple documents via document identifiers
In our example, we pass the collection name from which we want to fetch documents as first argument ("Traits") and an array of document keys (_key attribute) as second argument.
In return we get an array of the full trait documents for each character.
-------------------------------------------------------------
FOR c IN Characters
RETURN DOCUMENT("Traits", c.traits)[*].en
[
[
"strong",
"powerful",
"loyal",
"rational",
"brave"
],
[
"beautiful",
"powerful",
"loyal"
],
...
]
=============================================================
=============================================================
Merging characters and traits
=============================================================
---------------------------------------
merge both the character document and the data from the trait documents
The MERGE() functions merges objects together.
Because we used an object { traits: ... } which has the same attribute name traits as the original character attribute,
the latter got overwritten by the merge operation.
---------------------------------------
FOR c IN Characters
RETURN MERGE(c, { traits: DOCUMENT("Traits", c.traits)[*].en } )
[
{
"_id": "Characters/2861650",
"_key": "2861650",
"_rev": "_V1bzsXa---",
"age": 41,
"alive": false,
"name": "Ned",
"surname": "Stark",
"traits": [
"strong",
"powerful",
"loyal",
"rational",
"brave"
]
},
{
"_id": "Characters/2861653",
"_key": "2861653",
"_rev": "_V1bzsXa--B",
"age": 40,
"alive": false,
"name": "Catelyn",
"surname": "Stark",
"traits": [
"beautiful",
"powerful",
"loyal"
]
},
...
]
=============================================================
=============================================================
Join another way
=============================================================
FOR c IN Characters
RETURN MERGE(c, {
traits: (
FOR key IN c.traits
FOR t IN Traits
FILTER t._key == key
RETURN t.en
)
})
=============================================================
5.graph travel
=========================================================
Traversal
=========================================================
---------------------------------------------------------
In ArangoDB, two documents (a parent and a child character document) can be linked by an edge document.
Edge documents are stored in edge collections and have two additional attributes: _from and _to.
They reference any two documents by their document IDs (_id).
---------------------------------------------------------
=========================================================
ChildOf relations
=========================================================
Robb -> Ned
Sansa -> Ned
Arya -> Ned
Bran -> Ned
Jon -> Ned
Robb -> Catelyn
Sansa -> Catelyn
Arya -> Catelyn
Bran -> Catelyn
Jaime -> Tywin
Cersei -> Tywin
Tyrion -> Tywin
Joffrey -> Jaime
Joffrey -> Cersei
=========================================================
=========================================================
Creating the edges
=========================================================
----------------------------------------------------------
To create the required edge documents to store these relations in the database,
we can run a query that combines joining and filtering to match up the right character documents,
then use their _id attribute to insert an edge into an edge collection ChildOf.
----------------------------------------------------------
LET data = [
{
"parent": { "name": "Ned", "surname": "Stark" },
"child": { "name": "Robb", "surname": "Stark" }
}, {
"parent": { "name": "Ned", "surname": "Stark" },
"child": { "name": "Sansa", "surname": "Stark" }
}, {
"parent": { "name": "Ned", "surname": "Stark" },
"child": { "name": "Arya", "surname": "Stark" }
}, {
"parent": { "name": "Ned", "surname": "Stark" },
"child": { "name": "Bran", "surname": "Stark" }
}, {
"parent": { "name": "Catelyn", "surname": "Stark" },
"child": { "name": "Robb", "surname": "Stark" }
}, {
"parent": { "name": "Catelyn", "surname": "Stark" },
"child": { "name": "Sansa", "surname": "Stark" }
}, {
"parent": { "name": "Catelyn", "surname": "Stark" },
"child": { "name": "Arya", "surname": "Stark" }
}, {
"parent": { "name": "Catelyn", "surname": "Stark" },
"child": { "name": "Bran", "surname": "Stark" }
}, {
"parent": { "name": "Ned", "surname": "Stark" },
"child": { "name": "Jon", "surname": "Snow" }
}, {
"parent": { "name": "Tywin", "surname": "Lannister" },
"child": { "name": "Jaime", "surname": "Lannister" }
}, {
"parent": { "name": "Tywin", "surname": "Lannister" },
"child": { "name": "Cersei", "surname": "Lannister" }
}, {
"parent": { "name": "Tywin", "surname": "Lannister" },
"child": { "name": "Tyrion", "surname": "Lannister" }
}, {
"parent": { "name": "Cersei", "surname": "Lannister" },
"child": { "name": "Joffrey", "surname": "Baratheon" }
}, {
"parent": { "name": "Jaime", "surname": "Lannister" },
"child": { "name": "Joffrey", "surname": "Baratheon" }
}
]
FOR rel in data
LET parentId = FIRST(
FOR c IN Characters
FILTER c.name == rel.parent.name
FILTER c.surname == rel.parent.surname
LIMIT 1
RETURN c._id
)
LET childId = FIRST(
FOR c IN Characters
FILTER c.name == rel.child.name
FILTER c.surname == rel.child.surname
LIMIT 1
RETURN c._id
)
FILTER parentId != null AND childId != null
INSERT { _from: childId, _to: parentId } INTO ChildOf
RETURN NEW
--------------------------------------------------------------
The character documents don’t have user-defined keys.
If they had, it would allow us to create the edges more easily like:
INSERT { _from: "Characters/robb", _to: "Characters/ned" } INTO ChildOf
---------------------------------------------------------------
=========================================================
Traverse to the parents
=========================================================
--------------------------------
we have a graph we can query to find out who the parents are of another character – or in graph terms,
we want to start at a vertex and follow the edges to other vertices in an AQL graph traversal:
------------------------------
FOR v IN 1..1 OUTBOUND "Characters/2901776" ChildOf
RETURN v.name
FOR c IN Characters
FILTER c.name == "Bran"
RETURN c._id
[ "Characters/<YourDocumentkey>" ]
=========================================================
FOR c IN Characters
FILTER c.name == "Bran"
FOR v IN 1..1 OUTBOUND c ChildOf
RETURN v.name
[
"Ned",
"Catelyn"
]
=========================================================
=========================================================
Traverse to the children
=========================================================
FOR c IN Characters
FILTER c.name == "Ned"
FOR v IN 1..1 INBOUND c ChildOf
RETURN v.name
[
"Robb",
"Sansa",
"Jon",
"Arya",
"Bran"
]
=========================================================
=========================================================
Traverse to the grandchildren
=========================================================
FOR c IN Characters
FILTER c.name == "Tywin"
FOR v IN 2..2 INBOUND c ChildOf
RETURN v.name
[
"Joffrey",
"Joffrey"
]
Tywin <- Jaime <- Joffrey
Tywin <- Cersei <- Joffrey
=========================================================
=========================================================
Traverse with variable depth
=========================================================
-------------------------------------------------------------------
To return the parents and grandparents of Joffrey,
we can walk edges in OUTBOUND direction and adjust the traversal depth to go at least 1 step, and 2 at most:
-------------------------------------------------------------------
FOR c IN Characters
FILTER c.name == "Joffrey"
FOR v IN 1..2 OUTBOUND c ChildOf
RETURN DISTINCT v.name
[
"Cersei",
"Tywin",
"Jaime"
]
=========================================================
6.OPERATORS
=========================================================
Comparison operators
=========================================================
The following comparison operators are supported:
== equality
!= inequality
< less than
<= less or equal
> greater than
>= greater or equal
IN test if a value is contained in an array
NOT IN test if a value is not contained in an array
LIKE tests if a string value matches a pattern
=~ tests if a string value matches a regular expression
!~ tests if a string value does not match a regular expression
=========================================================
----------------------------------------------------------
比较操作符接受第一和第二操作数的任何数据类型。
但是,IN和NOT IN只在它们的右操作数是数组时返回有意义的结果,
而LIKE只在两个操作数都是字符串值时执行。
如果比较操作数具有不同的或不可感知的类型,则比较操作符不会执行任何隐式类型转换。
----------------------------------------------------------
0 == null // false
1 > 0 // true
true != null // true
45 <= "yikes!" // true
65 != "65" // true
65 == 65 // true
1.23 > 1.32 // false
1.5 IN [ 2, 3, 1.5 ] // true
"foo" IN null // false
42 NOT IN [ 17, 40, 50 ] // true
"abc" == "abc" // true
"abc" == "ABC" // false
"foo" LIKE "f%" // true
"foo" =~ "^f[o].$" // true
"foo" !~ "[a-z]+bar$" // true
=========================================================
----------------------------------------------------------
LIKE操作符检查其左操作数是否匹配其右操作数中指定的模式。
模式可以由正则字符和通配符组成。
所支持的通配符是_,用于匹配单个任意字符,%用于匹配任意数量的任意字符。文字%和_需要用反斜杠转义。
反斜杠本身需要转义,这实际上意味着两个反向solidus字符需要放在百分比符号或下划线的前面。
在arangosh中,需要额外的转义,使它在要转义的字符之前总共有四个反斜杠。
----------------------------------------------------------
"abc" LIKE "a%" // true
"abc" LIKE "_bc" // true
"a_b_foo" LIKE "a\\_b\\_foo" // true
=========================================================
Array comparison operators
=========================================================
[ 1, 2, 3 ] ALL IN [ 2, 3, 4 ] // false
[ 1, 2, 3 ] ALL IN [ 1, 2, 3 ] // true
[ 1, 2, 3 ] NONE IN [ 3 ] // false
[ 1, 2, 3 ] NONE IN [ 23, 42 ] // true
[ 1, 2, 3 ] ANY IN [ 4, 5, 6 ] // false
[ 1, 2, 3 ] ANY IN [ 1, 42 ] // true
[ 1, 2, 3 ] ANY == 2 // true
[ 1, 2, 3 ] ANY == 4 // false
[ 1, 2, 3 ] ANY > 0 // true
[ 1, 2, 3 ] ANY <= 1 // true
[ 1, 2, 3 ] NONE < 99 // false
[ 1, 2, 3 ] NONE > 10 // true
[ 1, 2, 3 ] ALL > 2 // false
[ 1, 2, 3 ] ALL > 0 // true
[ 1, 2, 3 ] ALL >= 3 // false
["foo", "bar"] ALL != "moo" // true
["foo", "bar"] NONE == "bar" // false
["foo", "bar"] ANY == "foo" // true
=========================================================
Logical operators
=========================================================
Logical operators
The following logical operators are supported in AQL:
&& logical and operator
|| logical or operator
! logical not/negation operator
AQL also supports the following alternative forms for the logical operators:
AND logical and operator
OR logical or operator
NOT logical not/negation operator
=========================================================
Arithmetic operators
=========================================================
AQL supports the following arithmetic operators:
+ addition
- subtraction
* multiplication
/ division
% modulus
Unary plus and unary minus are supported as well:
LET x = -5
LET y = 1
RETURN [-x, +y]
// [5, 1]
=========================================================
算术运算符接受任何类型的操作数。将非数值值传递给算术运算符将使用TO_NUMBER()函数应用的类型转换规则将操作数转换为数字:
null将被转换为0
false将被转换为0,true将被转换为1
一个有效的数值保持不变,但是NaN和Infinity将被转换为0
如果字符串值包含数字的有效字符串表示形式,则将其转换为数字。字符串开头或结尾的任何空格都将被忽略。包含其他内容的字符串被转换为数字0
将空数组转换为0,将具有一个成员的数组转换为其唯一成员的数字表示形式。具有更多成员的数组被转换为数字0。
对象/文档被转换为数字0。
1 + "a" // 1
1 + "99" // 100
1 + null // 1
null + 1 // 1
3 + [ ] // 3
24 + [ 2 ] // 26
24 + [ 2, 4 ] // 0
25 - null // 25
17 - true // 16
23 * { } // 0
5 * [ 7 ] // 35
24 / "12" // 2
1 / 0 // 0
=========================================================
=========================================================
Ternary operator
=========================================================
三元运算符期望布尔条件作为其第一个操作数,如果条件的值为true,则返回第二个操作数的结果,否则返回第三个操作数的结果。
Examples
u.age > 15 || u.active == true ? u.userId : null
=========================================================
Range operator
=========================================================
The .. operator will produce an array of the integer values in the defined range, with both bounding values included.
Examples
2010..2013
will produce the following result:
[ 2010, 2011, 2012, 2013 ]
=========================================================
Operator precedence
=========================================================
The operator precedence in AQL is similar as in other familiar languages (lowest precedence first):
? : ternary operator
|| logical or
&& logical and
**, != equality and inequality
IN in operator
<, <=, >=, > less than, less equal, greater equal, greater than
+, - addition, subtraction
*, /, % multiplication, division, modulus
!, +, - logical negation, unary plus, unary minus
[*] expansion
() function call
. member access
[] indexed value access
=========================================================
7.DATA QUERY
=========================================================
Data Access Queries
=========================================================
RETURN "Hello ArangoDB!"
--可以调用函数DOCUMENT()通过它的文档句柄检索单个文档,例如:
RETURN DOCUMENT("users/phil")
=========================================================
FOR doc IN users
RETURN doc
----------------------------------------------------------
FOR doc IN users
RETURN { user: doc, newAttribute: true }
----------------------------------------------------------
FOR doc IN users
FILTER doc._key == "phil"
RETURN doc
----------------------------------------------------------
FOR doc IN users
FILTER doc.status == "active"
SORT doc.name
LIMIT 10
=========================================================
=========================================================
Data Modification Queries
=========================================================
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
=========================================================
Modifying a single document
=========================================================
INSERT {
firstName: "Anna",
name: "Pavlova",
profession: "artist"
} IN users
INSERT {
_key: "GilbertoGil",
firstName: "Gilberto",
name: "Gil",
city: "Fortalezza"
} IN users
INSERT {
_key: "PhilCarpenter",
firstName: "Phil",
name: "Carpenter",
middleName: "G.",
status: "inactive"
} IN users
INSERT {
_key: "NatachaDeclerck",
firstName: "Natacha",
name: "Declerck",
location: "Antwerp"
} IN users
UPDATE "PhilCarpenter" WITH {
status: "active",
location: "Beijing"
} IN users
--Replace是替换文档所有属性的update的替代方法。
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 :
REMOVE "GilbertoGil" IN users
REMOVE { _key: "GilbertoGil" } IN users
=========================================================
=========================================================
Modifying multiple documents
=========================================================
FOR u IN users
FILTER u.status == "not active"
UPDATE u WITH { status: "inactive" } IN users
FOR u IN users
INSERT u IN backup
FOR u IN users
FILTER u.status == "deleted"
REMOVE u IN backup
--The following example will remove all documents from both users and backup:
LET r1 = (FOR u IN users REMOVE u IN users)
LET r2 = (FOR u IN backup REMOVE u IN backup)
RETURN true
=========================================================
=========================================================
Returning documents
=========================================================
数据修改查询可以选择返回文档。
为了在返回语句中引用插入、删除或修改的文档,数据修改语句引入了旧的和/或新的伪值:
FOR i IN 1..100
INSERT { value: i } IN test
RETURN NEW
FOR u IN users
FILTER u.status == "deleted"
REMOVE u IN users
RETURN OLD
FOR u IN users
FILTER u.status == "not active"
UPDATE u WITH { status: "inactive" } IN users
RETURN NEW
FOR i IN 1..100
INSERT { value: i } IN test
RETURN NEW._key
--Using OLD and NEW in the same query
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
--例如,下面的查询执行upsert操作,是更新了现有文档,还是插入了新文档。
--它通过在UPSERT之后,检查旧变量并使用LET语句存储操作类型的临时字符串来实现:
UPSERT { name: "test" }
INSERT { name: "test" }
UPDATE { } IN users
LET opType = IS_NULL(OLD) ? "insert" : "update"
RETURN { _key: NEW._key, type: opType }
=========================================================
在集群中,AQL数据修改查询目前不以事务方式执行。
此外,update、replace、upsert和remove AQL查询目前要求为所有应该修改或删除的文档指定_key属性,即使为集合选择了除_key之外的共享密钥属性。
这个限制可以在ArangoDB的未来版本中克服
=========================================================
=========================================================
9.HIGH-LEVEL OPERATORS
=========================================================
FOR
=========================================================
FOR variableName IN expression
FOR vertexVariableName, edgeVariableName, pathVariableName IN traversalExpression
FOR u IN users
RETURN u
FOR year IN [ 2011, 2012, 2013 ]
RETURN { "year" : year, "isLeapYear" : year % 4 == 0 && (year % 100 != 0 || year % 400 == 0) }
FOR u IN users
FOR l IN locations
RETURN { "user" : u, "location" : l }
=========================================================
RETURN
=========================================================
RETURN expression
FOR variableName IN expression
RETURN variableName
FOR u IN users
RETURN u
FOR u IN users
RETURN u.name
FOR u IN users
RETURN { name: u.name, age: u.age }
FOR u IN users
RETURN { [ u._id ]: u.age }
[
{
"users/9883": 32
},
{
"users/9915": 27
},
{
"users/10074": 69
}
]
--对于一个将用户id映射到年龄的单一对象,需要将单个结果与另一个返回合并并返回
RETURN MERGE(
FOR u IN users
RETURN { [ u._id ]: u.age }
)
[
{
"users/10074": 69,
"users/9883": 32,
"users/9915": 27
}
]
=========================================================
=========================================================
RETURN DISTINCT
=========================================================
FOR variableName IN expression
RETURN DISTINCT expression
FOR what IN 1..2
RETURN DISTINCT (
FOR i IN [ 1, 2, 3, 4, 1, 3 ]
RETURN i
)
[
[ 1, 2, 3, 4, 1, 3 ]
]
=========================================================
=========================================================
FILTER
=========================================================
FOR u IN users
FILTER u.active == true && u.age < 39
RETURN u
FOR u IN users
FILTER u.active == true
FILTER u.age < 39
RETURN u
FOR u IN users
FILTER u.active == true
RETURN u
FOR u IN users
FILTER u.active == true
LIMIT 5
RETURN u
FOR u IN users
FILTER u.active == true
LIMIT 5
FILTER u.gender == "f"
RETURN u
FOR u IN users
FILTER u.active == true
SORT u.age ASC
LIMIT 5
FILTER u.gender == "f"
RETURN u
=========================================================
=========================================================
SORT
=========================================================
FOR u IN users
SORT u.lastName, u.firstName, u.id DESC
RETURN u
=========================================================
=========================================================
LIMIT
=========================================================
LIMIT count
LIMIT offset, count
FOR u IN users
LIMIT 5
RETURN u
FOR u IN users
SORT u.firstName, u.lastName, u.id DESC
LIMIT 2, 5
RETURN u
=========================================================
=========================================================
LET
=========================================================
LET variableName = expression
LET a = [1, 2, 3] // initial assignment
a = PUSH(a, 4) // syntax error, unexpected identifier
LET a = PUSH(a, 4) // parsing error, variable 'a' is assigned multiple times
LET b = PUSH(a, 4) // allowed, result: [1, 2, 3, 4]
FOR u IN users
LET numRecommendations = LENGTH(u.recommendations)
RETURN {
"user" : u,
"numRecommendations" : numRecommendations,
"isPowerUser" : numRecommendations >= 10
}
FOR u IN users
LET friends = (
FOR f IN friends
FILTER u.id == f.userId
RETURN f
)
LET memberships = (
FOR m IN memberships
FILTER u.id == m.userId
RETURN m
)
RETURN {
"user" : u,
"friends" : friends,
"numFriends" : LENGTH(friends),
"memberShips" : memberships
}
=========================================================
=========================================================
COLLECT
The COLLECT keyword can be used to group an array by one or multiple group criteria.
=========================================================
COLLECT的第一种语法形式仅根据表达式中指定的已定义的组标准对结果进行分组。
为了进一步处理COLLECT产生的结果,引入了一个新的变量(由variableName指定)。这个变量包含组值。
下面是一个示例查询,它在u中查找不同的值。并在不同的城市提供:
=========================================================
--The general syntaxes for COLLECT are:
COLLECT variableName = expression options
COLLECT variableName = expression INTO groupsVariable options
COLLECT variableName = expression INTO groupsVariable = projectionExpression options
COLLECT variableName = expression INTO groupsVariable KEEP keepVariable options
COLLECT variableName = expression WITH COUNT INTO countVariable options
COLLECT variableName = expression AGGREGATE variableName = aggregateExpression options
COLLECT AGGREGATE variableName = aggregateExpression options
COLLECT WITH COUNT INTO countVariable options
FOR u IN users
COLLECT city = u.city
RETURN {
"city" : city
}
FOR u IN users
COLLECT city = u.city INTO groups
RETURN {
"city" : city,
"usersInCity" : groups
}
FOR u IN users
COLLECT country = u.country, city = u.city INTO groups
RETURN {
"country" : country,
"city" : city,
"usersInCity" : groups
}
FOR u IN users
COLLECT country = u.country, city = u.city INTO groups = u.name
RETURN {
"country" : country,
"city" : city,
"userNames" : groups
}
FOR u IN users
COLLECT country = u.country, city = u.city INTO groups = {
"name" : u.name,
"isActive" : u.status == "active"
}
RETURN {
"country" : country,
"city" : city,
"usersInCity" : groups
}
--KEEP仅与INTO组合时有效。在KEEP子句中只能使用有效的变量名。KEEP支持指定多个变量名。
FOR u IN users
LET name = u.name
LET someCalculation = u.value1 + u.value2
COLLECT city = u.city INTO groups KEEP name
RETURN {
"city" : city,
"userNames" : groups[*].name
}
--COLLECT还提供了一个特殊的WITH COUNT子句,可用来有效地确定组成员的数量。
FOR u IN users
COLLECT WITH COUNT INTO length
RETURN length
--WITH COUNT子句只能与INTO子句一起使用。
FOR u IN users
COLLECT age = u.age WITH COUNT INTO length
RETURN {
"age" : age,
"count" : length
}
=========================================================
Aggregation
=========================================================
--可以使用COLLECT语句对每个组执行数据聚合。
--为了只确定组长度,可以使用前面描述的COLLECT的WITH COUNT INTO变体。
--对于其他聚合,可以对收集的结果运行聚合函数
FOR u IN users
COLLECT ageGroup = FLOOR(u.age / 5) * 5 INTO g
RETURN {
"ageGroup" : ageGroup,
"minAge" : MIN(g[*].u.age),
"maxAge" : MAX(g[*].u.age)
}
--上面的方法要求在对所有组的collect操作期间存储所有组的值,这可能是低效的。
--COLLECT的特殊聚合变体允许在COLLECT操作期间增量地构建聚合值,因此通常效率更高。
--使用聚合变量,上面的查询变成:
FOR u IN users
COLLECT ageGroup = FLOOR(u.age / 5) * 5
AGGREGATE minAge = MIN(u.age), maxAge = MAX(u.age)
RETURN {
ageGroup,
minAge,
maxAge
}
--聚合关键字只能在COLLECT关键字之后使用。如果使用,它必须直接遵循分组键的声明。如果没有使用分组键,则必须直接跟随COLLECT关键字:
FOR u IN users
COLLECT AGGREGATE minAge = MIN(u.age), maxAge = MAX(u.age)
RETURN {
minAge,
maxAge
}
FOR u IN users
COLLECT AGGREGATE minAge = MIN(u.age), maxAge = MAX(u.age), sumAge = SUM(u.age)
RETURN {
minAge,
maxAge,
sumAge
}
--在顶层,聚合表达式必须是对受支持的聚合函数之一)的调用。
--(LENGTH、MIN、MAX、SUM、AVERAGE、STDDEV_POPULATION、STDDEV_SAMPLE、VARIANCE_POPULATION、VARIANCE_SAMPLE、UNIQUE、SORTED_UNIQUE或COUNT_DISTINCT
--也允许使用以下别名:COUNT(用于长度)、AVG(用于平均值)、STDDEV(用于STDDEV_POPULATION)、VARIANCE(用于VARIANCE_POPULATION)、COUNT_UNIQUE(用于COUNT_DISTINCT)。
--聚合表达式不能引用COLLECT本身引入的变量。
=========================================================
Setting COLLECT options
=========================================================
可以在COLLECT语句中使用选项来通知优化器有关首选的COLLECT方法。
当为COLLECT语句指定以下附录时,优化器将始终使用已排序的COLLECT变体,甚至不会使用hash变体创建计划:
OPTIONS { method: "sorted" }
也可以指定hash作为首选方法。在这种情况下,优化器只有在COLLECT语句符合条件时才会使用散列方法创建计划(不是所有的COLLECT语句都可以使用散列方法)。如果COLLECT语句符合条件,则只有一个计划使用散列方法。如果不符合条件,优化器将使用排序的方法。
=========================================================
COLLECT vs. RETURN DISTINCT
=========================================================
为了使结果集惟一,可以使用COLLECT或RETURN DISTINCT。在幕后,这两种变体都将通过创建AggregateNode来工作。对于这两种变体,优化器都可以尝试COLLECT的排序变体和散列变体。因此,区别主要在于语法,与等效的COLLECT相比,RETURN明显节省了一些输入:
FOR u IN users
RETURN DISTINCT u.age
FOR u IN users
COLLECT age = u.age
RETURN age
然而,COLLECT比RETURN DISTINCT灵活得多。此外,对于不同的返回,结果的顺序是未定义的,而对于COLLECT,结果将排序。
===========================================================
REMOVE
===========================================================
REMOVE keyExpression IN collection options
--集合必须包含要从中删除文档的集合的名称。keyExpression必须是包含文档标识的表达式。它可以是字符串(然后必须包含文档键),也可以是文档(必须包含_key属性)。
FOR u IN users
REMOVE { _key: u._key } IN users
FOR u IN users
REMOVE u._key IN users
FOR u IN users
REMOVE u IN users
--删除操作可以删除任意文件,且该文件不需要与前一个FOR语句生成的文件相同:
FOR i IN 1..1000
REMOVE { _key: CONCAT('test', i) } IN users
FOR u IN users
FILTER u.active == false
REMOVE { _key: u._key } IN backup
--也可以删除单个文档,使用文档键字符串或具有_key属性的文档:
REMOVE 'john' IN users
LET doc = DOCUMENT('users/john')
REMOVE doc IN users
--如果其中一个待删除文档不存在,下面的查询将会失败:
FOR i IN 1..1000
REMOVE { _key: CONCAT('test', i) } IN users
--通过指定ignoreErrors查询选项,可以抑制这些错误,这样查询就完成了:
FOR i IN 1..1000
REMOVE { _key: CONCAT('test', i) } IN users OPTIONS { ignoreErrors: true }
--为了确保查询返回时数据已被写入磁盘,有waitForSync查询选项:
FOR i IN 1..1000
REMOVE { _key: CONCAT('test', i) } IN users OPTIONS { waitForSync: true }
FOR i IN 1..1000
REMOVE { _key: CONCAT('test', i), _rev: "1287623" } IN users OPTIONS { ignoreRevs: false }
===========================================================
Returning the removed documents
===========================================================
FOR u IN users
REMOVE u IN users
LET removed = OLD
RETURN removed._key
======================================================================================================================
======================================================================================================================
UPDATE
======================================================================================================================
======================================================================================================================
UPDATE document IN collection options
UPDATE keyExpression WITH document IN collection options
FOR u IN users
UPDATE { _key: u._key, name: CONCAT(u.firstName, " ", u.lastName) } IN users
FOR u IN users
UPDATE { name: CONCAT(u.firstName, " ", u.lastName) } IN users
--集合必须包含要更新文档的集合的名称。文档必须是包含要更新的属性和值的文档。
--在使用第一个语法时,document还必须包含_key属性,以标识要更新的文档。
FOR u IN users
UPDATE { _key: u._key, name: CONCAT(u.firstName, " ", u.lastName) } IN users
--有_id属性但没有_key属性的对象以及像“users/john”这样的字符串一样的文档ID不能工作。
--但是,您可以使用DOCUMENT(id)通过它的id和PARSE_IDENTIFIER(id)来获取文档。获取作为字符串的文档密钥。
FOR u IN users
UPDATE u._key WITH { name: CONCAT(u.firstName, " ", u.lastName) } IN users
FOR u IN users
UPDATE { _key: u._key } WITH { name: CONCAT(u.firstName, " ", u.lastName) } IN users
FOR u IN users
UPDATE u WITH { name: CONCAT(u.firstName, " ", u.lastName) } IN users
--更新操作可更新不需要与前一个FOR语句生成的文档相同的任意文档:
FOR i IN 1..1000
UPDATE CONCAT('test', i) WITH { foobar: true } IN users
FOR u IN users
FILTER u.active == false
UPDATE u WITH { status: 'inactive' } IN backup
--Using the current value of a document attribute
--WITH子句中不支持伪变量OLD(更新后可用)。要访问当前属性值,通常可以通过FOR循环的变量引用文档,FOR循环用于遍历集合
FOR doc IN users
UPDATE doc WITH {
fullName: CONCAT(doc.firstName, " ", doc.lastName)
} IN users
--如果没有循环,因为只更新一个文档,那么可能没有像上面这样的变量(doc),让您引用正在更新的文档:
UPDATE "john" WITH { ... } IN users
LET key = PARSE_IDENTIFIER("users/john").key
UPDATE key WITH { ... } IN users
--要在这种情况下访问当前值,必须首先检索文档并将其存储在一个变量中:
LET doc = DOCUMENT("users/john")
UPDATE doc WITH {
fullName: CONCAT(doc.firstName, " ", doc.lastName)
} IN users
--现有属性可以根据其当前值进行修改,例如:
UPDATE doc WITH {
karma: doc.karma + 1
} IN users
--If the attribute hobbies doesn’t exist yet,
--it is conveniently initialized as [ "swimming" ] and otherwise extended.
UPDATE doc WITH {
hobbies: PUSH(doc.hobbies, "swimming")
} IN users
====================================================
Setting query options
====================================================
FOR i IN 1..1000
UPDATE {
_key: CONCAT('test', i)
} WITH {
foobar: true
} IN users OPTIONS { ignoreErrors: true }
更新操作只更新文档中指定的属性,而不修改其他属性。内部属性(如_id、_key、_rev、_from和_to)不能更新,并且在文档中指定时将被忽略。更新文档将使用服务器生成的值修改文档的修订号。
当使用空值更新属性时,ArangoDB不会从文档中删除该属性,而是为其存储空值。若要在更新操作中删除属性,请将其设置为null并提供keepNull选项:
FOR u IN users
UPDATE u WITH {
foobar: true,
notNeeded: null
} IN users OPTIONS { keepNull: false }
====================================================
Returning the modified documents
====================================================
UPDATE document IN collection options RETURN OLD
UPDATE document IN collection options RETURN NEW
UPDATE keyExpression WITH document IN collection options RETURN OLD
UPDATE keyExpression WITH document IN collection options RETURN NEW
FOR u IN users
UPDATE u WITH { value: "test" }
IN users
LET previous = OLD
RETURN previous._key
FOR u IN users
UPDATE u WITH { value: "test" }
IN users
LET updated = NEW
RETURN UNSET(updated, "_key", "_id", "_rev")
FOR u IN users
UPDATE u WITH { value: "test" }
IN users
RETURN { before: OLD, after: NEW }
======================================================================================================================
======================================================================================================================
REPLACE
======================================================================================================================
======================================================================================================================
REPLACE document IN collection options
REPLACE keyExpression WITH document IN collection options
--集合必须包含要替换文档的集合的名称。文档是替换文档。当使用第一个语法时,document还必须包含_key属性来标识要替换的文档。
FOR u IN users
REPLACE { _key: u._key, name: CONCAT(u.firstName, u.lastName), status: u.status } IN users
--The following query is invalid because it does not contain a _key attribute and thus it is not possible to determine the documents to be replaced:
FOR u IN users
REPLACE { name: CONCAT(u.firstName, u.lastName, status: u.status) } IN users
--当使用第二种语法时,keyExpression提供文档标识。它可以是字符串(然后必须包含文档键),也可以是文档(必须包含_key属性)。
FOR u IN users
REPLACE { _key: u._key, name: CONCAT(u.firstName, u.lastName) } IN users
FOR u IN users
REPLACE u._key WITH { name: CONCAT(u.firstName, u.lastName) } IN users
FOR u IN users
REPLACE { _key: u._key } WITH { name: CONCAT(u.firstName, u.lastName) } IN users
FOR u IN users
REPLACE u WITH { name: CONCAT(u.firstName, u.lastName) } IN users
======================================================================================================================
======================================================================================================================
INSERT
======================================================================================================================
======================================================================================================================
INSERT document INTO collection [ OPTIONS options ]
FOR i IN 1..100
INSERT { value: i } INTO numbers
INSERT { value: 1 } INTO numbers
FOR u IN users
FOR p IN products
FILTER u._key == p.recommendedBy
INSERT { _from: u._id, _to: p._id } INTO recommendations
--Setting query options
--忽略错误
FOR i IN 1..1000
INSERT {
_key: CONCAT('test', i),
name: "test",
foobar: true
} INTO users OPTIONS { ignoreErrors: true }
-- waitForSync true, 确保已经插入才返回
FOR i IN 1..1000
INSERT {
_key: CONCAT('test', i),
name: "test",
foobar: true
} INTO users OPTIONS { waitForSync: true }
--插入时,若已存在相同的key,则重写
FOR i IN 1..1000
INSERT {
_key: CONCAT('test', i),
name: "test",
foobar: true
} INTO users OPTIONS { overwrite: true }
--Returning the inserted documents
INSERT document INTO collection RETURN NEW
FOR i IN 1..100
INSERT { value: i }
INTO users
LET inserted = NEW
RETURN inserted._key
======================================================================================================================
======================================================================================================================
UPSERT
======================================================================================================================
======================================================================================================================
UPSERT searchExpression INSERT insertExpression UPDATE updateExpression IN collection options
UPSERT searchExpression INSERT insertExpression REPLACE updateExpression IN collection options
UPSERT { name: 'superuser' }
INSERT { name: 'superuser', logins: 1, dateCreated: DATE_NOW() }
UPDATE { logins: OLD.logins + 1 } IN users
-------------------------------------------------------------------------------------------------------------
FOR i IN 1..1000
UPSERT { _key: CONCAT('test', i)}
INSERT {foobar: false}
UPDATE {_rev: "1287623", foobar: true }
IN users OPTIONS { ignoreRevs: false }
======================================================================================================================
======================================================================================================================
WITH
======================================================================================================================
======================================================================================================================
AQL查询可以选择从with语句和查询使用的集合列表开始。除了查询使用的其他集合和由AQL查询解析器检测到的集合之外,WITH中指定的所有集合在查询开始时都将被读锁定。
对于动态访问集合的查询(例如通过遍历或通过document()之类的动态文档访问函数),在WITH中指定进一步的集合可能很有用。
这样的集合可能在查询编译时对AQL查询解析器不可见,因此在查询开始时不会被自动读锁定。
在这种情况下,无论何时使用这些集合,AQL执行引擎都会惰性地锁定它们,这可能导致与其他查询发生死锁
。如果检测到这样的死锁,查询将自动中止并回滚更改。在这种情况下,客户机应用程序可以尝试再次发送查询。
但是,如果客户机应用程序为所有使用WITH的查询指定使用的集合列表,则不会发生死锁,也不会因为死锁情况而中止查询。