ArangoDB高级操作

ArangoDB高级操作

原文:ArangoDB High-level operations

以下介绍以下高级操作:

  • FOR:迭代数组的所有元素。
  • RETURN:生成查询结果。
  • FILTER:将结果限制为与任意逻辑条件匹配的元素。
  • SORT:强制排序一系列已经生成的中间结果。
  • LIMIT:将结果中的元素数量减少到最多指定的数字,可选择跳过元素(分页)。
  • LET:为变量分配任意值。
  • COLLECT:按一个或多个组标准对数组进行分组。也可以统计和聚合。
  • REMOVE:从集合中删除文档。
  • UPDATE:部分更新集合中的文档。
  • REPLACE:完全替换集合中的文档。
  • INSERT:将新文档插入到集合中。
  • UPSERT:更新/替换现有文档,或在不存在的情况下创建它。
  • WITH:指定查询中使用的集合(仅在查询时开始)。


=====================================================================================

FOR

FOR关键字可以是迭代数组的所有元素。一般语法是:

FOR variableName IN expression

图形遍历还有一个特殊的变体:

FOR vertexVariableName, edgeVariableName, pathVariableName IN traversalExpression

对于这种特殊情况,请参见图形遍历章节对于所有其他情况,请阅读:

表达式返回的每个数组元素都被访问了一次。需要表达式在所有情况下返回一个数组。空数组也是允许的。当前的数组元素可用于在variableName指定的变量中进一步处理

FOR u IN users  RETURN u

这将遍历数组用户的所有元素(注意:在这种情况下,该数组由名为“users”的集合的所有文档组成),并使变量u中的当前数组元素可用在本示例中未修改,但仅使用RETURN关键字推送到结果中

注意:如下所示,对基于集合的数组进行迭代时,文档的顺序是未定义的,除非使用SORT 语句定义了明确的排序顺序

通过引入的可变FOR可用直到范围FOR被放置在被关闭。

另一个使用静态声明的值数组来迭代的例子:

FOR year IN [ 2011, 2012, 2013 ]  RETURN { "year" : year, "isLeapYear" : year % 4 == 0 && (year % 100 != 0 || year % 400 == 0) }

也允许嵌套多个FOR语句。FOR语句嵌套时, 将创建由各个FOR语句返回的数组元素的交叉乘积

FOR u IN users  FOR l IN locations    RETURN { "user" : u, "location" : l }

在这个例子中,有两个数组迭代:数组用户的外部迭代 加上数组位置的内部迭代内部数组遍历外部数组中的元素多次。对于每次迭代,用户位置的当前值可用于变量ul中的进一步处理



=====================================================================================

RETURN

RETURN语句可以用于生成查询的结果。必须在数据选择查询中的每个块的末尾指定一个RETURN语句,否则查询结果将是未定义的。在数据修改查询的主级使用 RETURN是可选的。

RETURN的一般语法是:

RETURN expression

表达通过返回RETURN产生在块中的每个迭代 RETURN语句被放置在,这意味着一个的结果RETURN语句是总是一个数组如果没有与查询匹配的文档和一个返回值作为数组返回一个元素,则包含一个空数组。

要从当前迭代的数组中返回所有元素,而不需要修改,可以使用以下简单的形式:

FOR variableName IN expression  RETURN variableName

由于RETURN允许指定表达式,因此可以执行任意计算来计算结果元素。RETURN放置的范围内有效的任何变量都可用于计算。

要遍历称为用户的一个集合的所有文档并返回完整的文档,您可以写:

FOR u IN users  RETURN u

在for循环的每次迭代中,将用户集合的文档分配给变量u,并在此示例中未修改返回。要只返回每个文档的一个属性,您可以使用不同的返回表达式:

FOR u IN users  RETURN u.name

或者返回多个属性,一个对象可以这样构造:

FOR u IN users  RETURN { name: u.name, age: u.age }

注意:RETURN将关闭当前作用域并消除其中的所有局部变量。在使用子查询时记住这一点很重要

还支持动态属性名称

FOR u IN users  RETURN { [ u._id ]: u.age }

在此示例中,每个用户的文档_id用作表达式来计算属性键:

[  {    "users/9883": 32  },  {    "users/9915": 27  },  {    "users/10074": 69  }]

结果每个用户包含一个对象,每个对象具有单个键/值对。这通常是不需要的。对于单个对象,将用户ID映射到年龄,需要将各个结果合并并返回给另一个对象RETURN

RETURN MERGE(  FOR u IN users    RETURN { [ u._id ]: u.age })
[  {    "users/10074": 69,    "users/9883": 32,    "users/9915": 27  }]

请记住,如果键表达式多次计算相同的值,则只有一个具有重复名称的键/值对可以在MERGE()中存活 为了避免这种情况,您可以不使用动态属性名称,而是使用静态名称,并将所有文档属性作为属性值返回:

FOR u IN users  RETURN { name: u.name, age: u.age }
[  {    "name": "John Smith",    "age": 32  },  {    "name": "James Hendrix",    "age": 69  },  {    "name": "Katie Foster",    "age": 27  }]

返回列表

由于ArangoDB 2.7,RETURN可以后跟DISTINCT关键字。DISTINCT关键字将确保返回值的唯一RETURN语句:

FOR variableName IN expression  RETURN DISTINCT expression

如果DISTINCT应用于本身是数组或子查询的表达式,则DISTINCT不会使每个数组或子查询结果中的值唯一,而是确保结果仅包含不同的数组或子查询结果。要使数组或子查询的结果唯一,只需对数组或子查询应用DISTINCT

例如,以下查询将对其子查询结果应用DISTINCT,但不在子查询中:

FOR what IN 1..2  RETURN DISTINCT (    FOR i IN [ 1, 2, 3, 4, 1, 3 ]       RETURN i  )

这里我们将有一个两个迭代FOR循环,每个循环都执行一个子查询。这里DISTINCT应用于两个子查询结果。两个子查询返回相同的结果值(即[1,2,3,4,1,3]),所以在DISTINCT之后,只有一个值[1,2,3,4,1,3]剩下:

[  [ 1, 2, 3, 4, 1, 3 ]]

如果目标是将DISTINCT应用到子查询中,则需要将其移动到:

FOR what IN 1..2  LET sub = (    FOR i IN [ 1, 2, 3, 4, 1, 3 ]       RETURN DISTINCT i  )   RETURN sub

在上述情况下,DISTINCT将使子查询结果唯一,以便每个子查询将返回唯一的值数组([1,2,3,4])。由于子查询执行两次,顶层没有DISTINCT,该数组将返回两次:

[  [ 1, 2, 3, 4 ],  [ 1, 2, 3, 4 ]]

注意:结果的顺序未定义为RETURN DISTINCT

注意:如果查询的顶层没有FOR 循环,则不允许返回DISTINCT


=====================================================================================


FILTER

FILTER语句可以被用来限制的结果相匹配的任意的逻辑条件的元素。

一般语法

FILTER condition

条件必须是一个评估为falsetrue的条件如果条件结果为假,则跳过当前元素,因此不会进一步处理,而不是结果的一部分。如果条件为真,则不跳过当前元素,并可进一步处理。有关可以在条件下使用的比较运算符,逻辑运算符等的列表,请参阅运算符

FOR u IN users  FILTER u.active == true && u.age < 39  RETURN u

允许在查询中指定多个FILTER语句,即使在同一个块中。如果使用多个FILTER语句,它们的结果将与逻辑AND组合,这意味着包含一个元素的所有过滤条件必须为true。

FOR u IN users  FILTER u.active == true  FILTER u.age < 39  RETURN u

在上面的例子中,所有数组元素的用户 具有的属性 活性值为,并且具有属性年龄与值小于39(包括的)将被包含在结果中。用户的所有其他元素将被跳过,不包括在RETURN生成的结果中 有关不存在或空属性的影响的描述,可参考“ 从集合访问数据 ”一章

操作顺序

请注意,FILTER语句的位置可能会影响查询的结果。测试数据中有16个活跃用户, 例如:

FOR u IN users  FILTER u.active == true  RETURN u

我们最多可以将结果集限制在5个用户:

FOR u IN users  FILTER u.active == true  LIMIT 5  RETURN u

这可能会返回Jim,Diego,Anthony,Michael和Chloe的用户文档。返回的是未定义的,因为没有SORT语句来确保特定的顺序。如果我们添加第二个FILTER语句只返回女性...

FOR u IN users  FILTER u.active == true  LIMIT 5  FILTER u.gender == "f"  RETURN u

...它可能只是返回Chloe文档,因为LIMIT被应用在第二个FILTER之前不超过5个文件到达第二个FILTER区块,并不是所有这些都符合性别标准,尽管收集中有超过5名活跃的女性用户。通过添加SORT可以获得更确定的结果

FOR u IN users  FILTER u.active == true  SORT u.age ASC  LIMIT 5  FILTER u.gender == "f"  RETURN u

这将返回用户Mariah和Mary。如果按照DESC顺序按年龄排序,则返回Sophia,Emma和Madison文档。一个过滤器一后 LIMIT不是很常见不过了,你可能想这样的查询,而不是:

FOR u IN users  FILTER u.active == true AND u.gender == "f"  SORT u.age ASC  LIMIT 5  RETURN u

放置FILTER的重要性允许这个单个关键字可以承担两个SQL关键字WHERE以及HAVING的角色因此,AQL的FILTERCOLLECT聚合一起使用与任何其他中间结果,文档属性等相同。

=====================================================================================


SORT

所述SORT语句将强制在当前块中已经产生的中间结果的数组进行排序。SORT允许指定一个或多个排序标准和方向。一般语法是:

SORT expression direction

指定方向是可选的。排序的默认(隐式)方向是升序。要明确指定排序方向,可以使用关键字ASC(升序)和DESC可以使用逗号分隔多个排序标准。

注意:在迭代基于集合的数组时,除非使用SORT定义明确的排序顺序,否则始终未定义文档的顺序

FOR u IN users  SORT u.lastName, u.firstName, u.id DESC  RETURN u

请注意,常量SORT表达式可用于指示不需要特定排序顺序。在优化过程中,AQL优化程序将优化常量SORT表达式,但是如果优化程序不需要考虑任何特定的排序顺序,则可以显式指定它们可以进一步优化。


=====================================================================================




LIMIT

LIMIT语句允许切片使用偏移和计数结果数组。它将结果中的元素数量减少到最多指定的数字。遵循LIMIT的两种一般形式

LIMIT countLIMIT offset, count

第一种形式允许仅指定数值,而第二种形式允许指定偏移量计数值第一种形式与使用偏移值为0的第二种形式相同

FOR u IN users  LIMIT 5  RETURN u

以上查询返回用户集合的前五个文档也可以写出LIMIT 0, 5相同的结果。实际返回的文件是相当随意的,因为没有指定明确的排序顺序。因此,通常需要一个SORT操作。

偏移值指定从结果许多元素将被跳过。它必须为0或更大。值指定有多少元素应该至多包含在结果中。

FOR u IN users  SORT u.firstName, u.lastName, u.id DESC  LIMIT 2, 5  RETURN u

在上面的示例中,用户的文档被排序,前两个结果被跳过,并返回下一个五个用户文档。

请注意,变量和表达式不能用于偏移计数它们的值在查询编译时必须知道,这意味着您只能使用数字文字和绑定参数。

如果使用LIMIT与查询中的其他操作有关。 特别是FILTER之前的LIMIT操作可以显着地改变结果,因为操作按照它们在查询中写入的顺序执行。有关详细示例,请参阅FILTER




=====================================================================================



LET

LET语句可用于分配给一个变量的任意值。然后在LET语句放置的范围中引入该变量

一般语法是:

LET variableName = expression

变量在AQL中是不可变的,这意味着它们不能被重新分配:

LET a = [1, 2, 3]  // initial assignmenta = PUSH(a, 4)     // syntax error, unexpected identifierLET a = PUSH(a, 4) // parsing error, variable 'a' is assigned multiple timesLET b = PUSH(a, 4) // allowed, result: [1, 2, 3, 4]

LET语句主要用于声明复杂计算,并避免在查询的多个部分重复计算相同的值。

FOR u IN users  LET numRecommendations = LENGTH(u.recommendations)  RETURN {     "user" : u,     "numRecommendations" : numRecommendations,     "isPowerUser" : numRecommendations >= 10   }

在上述示例中,使用LET语句计算推荐数量,从而避免在RETURN语句中计算两次值

LET的另一个用例是在子查询中声明一个复杂的计算,使整个查询更易读。

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

所述COLLECT关键字可以由一个或多个基团的标准被用于组的阵列。

COLLECT语句将消除当前范围内的所有局部变量。COLLECT之后,只有COLLECT本身引入的变量才可用。

COLLECT的一般语法是:

COLLECT variableName = expression optionsCOLLECT variableName = expression INTO groupsVariable optionsCOLLECT variableName = expression INTO groupsVariable = projectionExpression optionsCOLLECT variableName = expression INTO groupsVariable KEEP keepVariable optionsCOLLECT variableName = expression WITH COUNT INTO countVariable optionsCOLLECT variableName = expression AGGREGATE variableName = aggregateExpression optionsCOLLECT AGGREGATE variableName = aggregateExpression optionsCOLLECT WITH COUNT INTO countVariable options

options 在所有变体中都是可选的。

分组语法

COLLECT的第一个语法形式只能通过表达式中指定的定义组标准对结果进行分组为了进一步处理COLLECT生成的结果引入了一个新的变量(由variableName指定)。此变量包含组值。

以下是一个示例查询,可以在u.city中找到不同的值,并使其在可变城市中可用

FOR u IN users  COLLECT city = u.city  RETURN {     "city" : city   }

第二种形式与第一种形式相同,但另外还引入了一个变量(由groupsVariable指定),该变量包含落入组中的所有元素。这样做的工作如下:groupsVariable变量是一个数组,包含与组中所有元素一样多的数组。该数组的每个成员都是一个JSON对象,其中在AQL查询中定义的每个变量的值都绑定到相应的属性。请注意,它考虑在COLLECT语句之前定义的所有变量,但不考虑顶级(在任何FOR之外中的所有变量,除非COLLECT语句本身在顶级,在这种情况下所有变量都被采用。

FOR u IN users  COLLECT city = u.city INTO groups  RETURN {     "city" : city,     "usersInCity" : groups   }

在上面的例子中,数组用户将按属性城市分组 结果是一个新的文档数组,每个不同的u.city有一个元素每个城市的原始阵列(这里:用户的元素在变量组中可用这是因为INTO条款。

COLLECT还允许指定多个组标准。个别组标准可以用逗号分隔:

FOR u IN users  COLLECT country = u.country, city = u.city INTO groups  RETURN {     "country" : country,     "city" : city,     "usersInCity" : groups   }

在上述示例中,阵列用户按国家/地区按城市分组,并且对于国家和城市的每个不同组合,将返回用户。

丢弃过时的变量

COLLECT的第三种形式允许 使用任意的projectionExpression重写groupsVariable的内容

FOR u IN users  COLLECT country = u.country, city = u.city INTO groups = u.name  RETURN {     "country" : country,     "city" : city,     "userNames" : groups   }

在上面的例子中,只有projectionExpressionu.name因此,只有这个属性被复制到每个文档groupsVariable中。这可能比将所有变量从范围复制到groupsVariable更有效,就像没有projectionExpression一样

INTO中的表达式也可用于任意计算:

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   }

COLLECT还提供了一个可选的KEEP子句,可用于控制哪些变量将被复制到创建的变量中INTO如果没有指定KEEP子句,则范围中的所有变量将作为子属性复制到groupsVariable中这是安全的,但如果范围中有很多变量或变量包含大量数据,则可能会对性能产生负面影响。

以下示例将复制到groupsVariable中的变量限制 为仅命名范围内的变量usomeCalculation也不会被复制到groupsVariable,因为它们没有列在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   }

KEEP仅适用于INTOKEEP子句中只能使用有效的变量名KEEP支持多个变量名的规范。

组长度计算

COLLECT还提供了一个特殊的WITH COUNT子句,可以用来有效地确定组成员的数量。

最简单的形式只是返回将其转入COLLECT的项目数

FOR u IN users  COLLECT WITH COUNT INTO length  RETURN length

以上相当于,但效率比:

RETURN LENGTH(  FOR u IN users    RETURN length)

与COUNT子句也可以用来有效地计算每个组中的项的数目:

FOR u IN users  COLLECT age = u.age WITH COUNT INTO length  RETURN {     "age" : age,     "count" : length   }

注意:WITH COUNT子句只能与INTO子句一起使用

聚合

COLLECT可以使用一个语句来执行每组数据的聚合。为了仅确定组长度,可以如前所述使用WITH COUNT INTO变体COLLECT

对于其他聚合,可以对COLLECT 结果运行聚合函数

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)  }

然而,上述要求在所有组的收集操作期间存储所有组值,这可能是低效的。

允许在收集操作期间逐渐建立聚合值的特殊AGGREGATE变体,COLLECT因此通常更有效。

使用AGGREGATE变体,上述查询变为:

FOR u IN users  COLLECT ageGroup = FLOOR(u.age / 5) * 5   AGGREGATE minAge = MIN(u.age), maxAge = MAX(u.age)  RETURN {    ageGroup,     minAge,     maxAge   }

AGGREGATE关键字只能在COLLECT关键字后使用如果使用,它必须直接按照分组键的声明。如果没有使用分组键,则它必须COLLECT直接按照关键字:

FOR u IN users  COLLECT AGGREGATE minAge = MIN(u.age), maxAge = MAX(u.age)  RETURN {    minAge,     maxAge   }

每个AGGREGATE 作业的右侧只允许具体表达式

  • 在顶层,聚合表达式必须是所支持的聚合功能中的一个的呼叫LENGTHMINMAXSUMAVERAGESTDDEV_POPULATIONSTDDEV_SAMPLEVARIANCE_POPULATION,或VARIANCE_SAMPLE

  • 聚合表达式不得引用COLLECT自身引入的变量

收集变体

由于ArangoDB 2.6,优化器可以选择两种COLLECT变体排序变体和散列变体。散列变种只成为一个候选人COLLECT不使用的语句INTO子句。

优化器将始终生成采用排序方法的计划排序的方法要求其输入按照COLLECT子句中指定的组标准进行排序为了确保结果的正确性,AQL优化程序将自动将一个SORT 语句插入到COLLECT语句前面的查询中如果组标准中存在排序的索引,则优化器可能能够稍后优化该SORT语句。

如果COLLECT有资格使用哈希变体,优化程序将在计划阶段开始时为其创建额外的计划。在此计划中,COLLECT前面不会添加额外的SORT语句这是因为COLLECT哈希变体不需要排序输入。相反,将在COLLECT之后添加一个SORT语句来对其输出进行排序。SORT声明可能会在稍后的阶段再次优化。如果COLLECT的排序顺序与用户无关,

FOR u IN users  COLLECT age = u.age  SORT null  /* note: will be optimized away */  RETURN age

优化器使用哪种COLLECT变体取决于优化器的成本估算。使用不同COLLECT变体创建的计划将通过常规优化流程发货。最终,优化者将按常规选择最低估计总成本的计划。

通常,在组标准上存在排序索引的情况下COLLECT排序变体应该是优选的。在这种情况下,优化器可以消除COLLECT之前SORT语句,以便不会留下SORT

如果组标准中没有可用的排序索引,排序 变体所需的前期排序可能很昂贵。在这种情况下,优化器可能更喜欢COLLECT哈希变体,它不需要对其输入进行排序。

COLLECT实际使用的变体可以通过查询查询的执行计划,特别是AggregateNode及其aggregationOptions属性来计算出来

设置COLLECT选项

可以在COLLECT语句中使用选项来通知优化器有关首选COLLECT 方法。当指定COLLECT语句的以下附录时,优化器将始终使用COLLECT排序变体,甚至不使用哈希变体创建计划

OPTIONS { method: "sorted" }

请注意,将hash指定为方法不会使优化器使用哈希变体。这是因为哈希变体不符合所有查询的资格。相反,如果OPTIONS中没有指定选项或任何其他排序方法,优化器将使用其常规成本估算。

COLLECT与RETURN DISTINCT

为了使结果集成为唯一,可以使用COLLECTRETURN DISTINCT幕后,两个变体都可以通过创建一个AggregateNode来实现对于这两种变体,优化器可以尝试COLLECT的排序和散列变体因此,差异主要是语法,RETURN DISTINCT与等效的COLLECT相比,节省了一些打字的次数

FOR u IN users  RETURN DISTINCT u.age
FOR u IN users  COLLECT age = u.age  RETURN age

然而,COLLECTRETURN DISTINCT灵活得多此外,结果的顺序对于RETURN DISTINCT是未定义的,而对于COLLECT,结果将被排序。



========================================================================================


REMOVE

REMOVE关键字可以用来从集合中删除的文件。在单个服务器上,文档删除是以全或无任何方式事务执行的。对于分片集合,整个删除操作不是事务性的。

每个REMOVE操作仅限于一个集合, 集合名称不能是动态的。每个AQL查询只允许每个集合单个REMOVE语句,并且访问相同集合的读操作,遍历操作或可以读取文档的AQL函数不能遵循。

删除操作的语法是:

REMOVE keyExpression IN collection options

集合必须包含从中删除文档的集合的名称。keyExpression必须是包含文档标识的表达式。这可以是一个字符串(必须包含 文档键)或一个必须包含_key属性的文档

以下查询因此是相当的:

FOR u IN users  REMOVE { _key: u._key } IN usersFOR u IN users  REMOVE u._key IN usersFOR u IN users  REMOVE u IN users

注意:删除操作可以删除任意文档,并且文档不需要与前面的FOR语句生成的文档相同

FOR i IN 1..1000  REMOVE { _key: CONCAT('test', i) } IN usersFOR u IN users  FILTER u.active == false  REMOVE { _key: u._key } IN backup

设置查询选项

可以使用选项来抑制尝试删除不存在的文档时可能发生的查询错误。例如,如果要删除的文档之一不存在,以下查询将失败:

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 }

返回已移除的文档

删除的文档也可以由查询返回。在这种情况下,REMOVE 语句必须跟在一个RETURN语句之后(LET也允许使用中间语句)。REMOVE引入伪值OLD来引用已删除的文档:

REMOVE keyExpression IN collection options RETURN OLD

以下是使用名为removed捕获已删除文档的变量的示例对于每个删除的文档,将返回文档键。

FOR u IN users  REMOVE u IN users   LET removed = OLD   RETURN removed._key
======================================================================================================



UPDATE

UPDATE关键字可以用来部分地更新集合中的文档。在单个服务器上,以全或无任何方式事务执行更新。对于分片集合,整个更新操作不是事务性的。

每个UPDATE操作都限于单个集合, 集合名称不能是动态的。每个AQL查询只允许每个集合的单个UPDATE语句,并且访问相同集合的读取操作,遍历操作或可以读取文档的AQL函数不能遵循。系统属性_id_key_rev无法更新,_from_to可以。

更新操作的两种语法如下:

UPDATE document IN collection optionsUPDATE keyExpression WITH document IN collection options

集合必须包含应更新文档的集合的名称。文档必须是包含要更新的属性和值的文档。当使用第一个语法时,文档还必须包含_key 属性来标识要更新的文档。

FOR u IN users  UPDATE { _key: u._key, name: CONCAT(u.firstName, " ", u.lastName) } IN users

以下查询无效,因为它不包含_key属性,因此无法确定要更新的文档:

FOR u IN users  UPDATE { name: CONCAT(u.firstName, " ", u.lastName) } IN users

当使用第二种语法时,keyExpression提供文档标识。这可以是一个字符串(必须包含文档键)或一个必须包含_key属性的文档

以下查询是等效的:

FOR u IN users  UPDATE u._key WITH { name: CONCAT(u.firstName, " ", u.lastName) } IN usersFOR u IN users  UPDATE { _key: u._key } WITH { name: CONCAT(u.firstName, " ", u.lastName) } IN usersFOR 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 usersFOR u IN users  FILTER u.active == false  UPDATE u WITH { status: 'inactive' } IN backup

使用文档属性的当前值

子句中OLD不支持伪变量WITH(以后可用UPDATE)。要访问当前属性值,通常可以通过FOR循环的变量来引用一个文档,该变量用于迭代集合:

FOR doc IN users  UPDATE doc WITH {    fullName: CONCAT(doc.firstName, " ", doc.lastName)  } IN users

如果没有循环,因为单个文档只是更新,那么可能没有像上面(doc一样的变量,这可以引用正在更新的文档:

UPDATE "users/john" 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

如果属性karma不存在,doc.karma则被评估为null该表达式null + 1导致将新属性karma设置为1如果属性确实存在,那么它将被增加1

当然,数组也可以被突变:

UPDATE doc WITH {  hobbies: PUSH(doc.hobbies, "swimming")} IN users

如果属性hobbies不存在,则可以方便地初始化[ "swimming" ]或扩展。

设置查询选项

可以使用选项来抑制尝试更新不存在的文档或违反唯一键约束时可能发生的查询错误:

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 }

上述查询将从文档中删除notNeeded属性,并正常更新foob​​ar属性。

如果UPDATE查询和待更新文档中都存在对象属性,那么还可以使用mergeObjects选项来控制对象内容是否被合并

以下查询将更新的文档的name属性设置为与查询中指定的完全相同的值。这是由于mergeObjects选项设置为false

FOR u IN users  UPDATE u WITH {    name: { first: "foo", middle: "b.", last: "baz" }  } IN users OPTIONS { mergeObjects: false }

相反,以下查询将原始文档中的name属性的内容与查询中指定的值合并

FOR u IN users  UPDATE u WITH {    name: { first: "foo", middle: "b.", last: "baz" }  } IN users OPTIONS { mergeObjects: true }

现在将保留在待更新文档中但不在查询中的名称中的属性两者中存在的属性将被查询中指定的值覆盖。

注:为默认值mergeObjects真的,所以没有必要明确指定。

为了确保在更新查询返回时数据是持久的,有waitForSync 查询选项:

FOR u IN users  UPDATE u WITH {    foobar: true  } IN users OPTIONS { waitForSync: true }

返回修改后的文档

修改后的文档也可以由查询返回。在这种情况下,UPDATE 语句需要遵循一个RETURN语句(LET也允许使用中间语句)。这些语句可以引用伪值OLDNEWOLD伪值是指文件修订更新之前,并NEW 涉及文件更新后的版本。

双方OLDNEW会包含所有文档属性,即使是那些在更新的表达没有规定。

UPDATE document IN collection options RETURN OLDUPDATE document IN collection options RETURN NEWUPDATE keyExpression WITH document IN collection options RETURN OLDUPDATE keyExpression WITH document IN collection options RETURN NEW

以下是使用名称为变量previous捕获原始文档的变量的示例对于每个修改的文档,返回文档键。

FOR u IN users  UPDATE u WITH { value: "test" }   LET previous = OLD   RETURN previous._key

以下查询使用NEW伪值返回更新的文档,而没有一些系统属性:

FOR u IN users  UPDATE u WITH { value: "test" }   LET updated = NEW   RETURN UNSET(updated, "_key", "_id", "_rev")

它也可以同时返回OLDNEW

FOR u IN users  UPDATE u WITH { value: "test" }   RETURN { before: OLD, after: NEW }
=================================================================================================



REPLACE

REPLACE关键字可以用来完全取代集合中的文档。在单个服务器上,替换操作是以全或无任何方式事务执行的。对于分片集合,整个替换操作不是事务性的。

每个REPLACE操作仅限于单个集合, 集合名称不能是动态的。每个AQL查询只允许每个集合使用一个REPLACE语句,并且不能遵循访问同一集合的读取操作,遍历操作或可以读取文档的AQL函数。系统属性_id_key_rev不能被替换,_from_to可以。

替换操作的两种语法有:

REPLACE document IN collection optionsREPLACE keyExpression WITH document IN collection options

集合必须包含应替换文档的集合的名称。文件是替换文件。当使用第一种语法时,文档 还必须包含_key属性来标识要替换的文档。

FOR u IN users  REPLACE { _key: u._key, name: CONCAT(u.firstName, u.lastName), status: u.status } IN users

以下查询无效,因为它不包含_key属性,因此无法确定要替换的文档:

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 usersFOR u IN users  REPLACE u._key WITH { name: CONCAT(u.firstName, u.lastName) } IN usersFOR u IN users  REPLACE { _key: u._key } WITH { name: CONCAT(u.firstName, u.lastName) } IN usersFOR u IN users  REPLACE u WITH { name: CONCAT(u.firstName, u.lastName) } IN users

替换将完全替换现有文档,但不会修改内部属性(如_id_key_from_to)的值。更换文档将使用服务器生成的值修改文档的修订版本号。

替换操作可以更新不需要与前面的FOR语句生成的文档相同的任意文档

FOR i IN 1..1000  REPLACE CONCAT('test', i) WITH { foobar: true } IN usersFOR u IN users  FILTER u.active == false  REPLACE u WITH { status: 'inactive', name: u.name } IN backup

设置查询选项

选项可用于抑制尝试替换不存在的文档或违反唯一键约束时可能发生的查询错误:

FOR i IN 1..1000  REPLACE { _key: CONCAT('test', i) } WITH { foobar: true } IN users OPTIONS { ignoreErrors: true }

为了确保替换查询返回时数据是持久的,有waitForSync 查询选项:

FOR i IN 1..1000  REPLACE { _key: CONCAT('test', i) } WITH { foobar: true } IN users OPTIONS { waitForSync: true }

返回修改后的文档

修改后的文档也可以由查询返回。在这种情况下,REPLACE 语句必须跟在一个RETURN语句之后(LET也允许使用中间语句)。所述OLD伪值可以用于参考文档的修订前的替换,并NEW涉及文件后的替换修改。

双方OLDNEW会包含所有文档属性,即使是那些在替换表达没有规定。

REPLACE document IN collection options RETURN OLDREPLACE document IN collection options RETURN NEWREPLACE keyExpression WITH document IN collection options RETURN OLDREPLACE keyExpression WITH document IN collection options RETURN NEW

以下是使用名称为变量previous返回原始文档的变量的示例对于每个替换的文档,文档键将被返回:

FOR u IN users  REPLACE u WITH { value: "test" }   LET previous = OLD   RETURN previous._key

以下查询使用NEW伪值返回已替换的文档(不包含其某些系统属性):

FOR u IN users  REPLACE u WITH { value: "test" }   LET replaced = NEW   RETURN UNSET(replaced, '_key', '_id', '_rev')
================================================================================================




INSERT

INSERT关键字可用于插入新的文件到一个集合。在单个服务器上,插入操作以全或无任何方式进行交易。对于分片集合,整个插入操作不是事务性的。

每个INSERT操作仅限于单个集合, 集合名称不能是动态的。每个AQL查询只允许一个集合中的单个INSERT语句,而不能遵循访问相同集合的读取操作,遍历操作或可以读取文档的AQL函数。

插入操作的语法是:

INSERT document IN collection options

注意INTO关键字也被允许在IN的地方

集合必须包含要插入文档的集合的名称。document是要插入的文档,它可能包含也可能不包含_key属性。如果没有提供_key属性,ArangoDB将自动生成_key值的值。插入文档还会自动生成文档的文档版本号。

FOR i IN 1..100  INSERT { value: i } IN numbers

当插入到边集合中时,必须在文档中指定属性_from_to

FOR u IN users  FOR p IN products    FILTER u._key == p.recommendedBy    INSERT { _from: u._id, _to: p._id } IN recommendations

设置查询选项

可以使用选项来抑制违反唯一键约束时可能发生的查询错误:

FOR i IN 1..1000  INSERT {    _key: CONCAT('test', i),    name: "test",    foobar: true  } INTO users OPTIONS { ignoreErrors: true }

为了确保在插入查询返回时数据是持久的,有waitForSync 查询选项:

FOR i IN 1..1000  INSERT {    _key: CONCAT('test', i),    name: "test",    foobar: true  } INTO users OPTIONS { waitForSync: true }

返回插入的文档

200的X- 200 X- 200 200 X- 200 200 X- 200 200 X- 1992:在这种情况下,INSERT 语句可以是一个RETURN语句(LET也允许使用中间语句)。要引用插入的文档,该INSERT语句引入了一个伪值NEW

中包含的文件NEW将包含所有属性,即使是那些自动生成的由数据库(例如_id_key_rev)。

INSERT document IN collection options RETURN NEW

以下是使用一个名为insertedreturn的变量返回插入的文档的示例新新新新200新新200新200新新200新200新200新200新新200新新200新200新新200新200新新200新200新新200新200新新200新新200新新新200新新200新

FOR i IN 1..100  INSERT { value: i }   LET inserted = NEW   RETURN inserted._key
==========================================================================================


UPSERT

UPSERT关键字可用于检查是否存在某些文件,并更新/更换它们的情况下,它们的存在,或者创建他们的情况下,它们不存在。在单个服务器上,插件是以全或无任何方式交易执行的。对于分片集合,整个更新操作不是事务性的。

每个UPSERT操作仅限于一个集合, 集合名称不能是动态的。每个AQL查询只允许每个集合的一个单独的UPSERT语句,并且不能遵循访问相同集合的读取操作,通过遍历操作或可以读取文档的AQL函数。

upsert操作的语法是:

UPSERT searchExpression INSERT insertExpression UPDATE updateExpression IN collection optionsUPSERT searchExpression INSERT insertExpression REPLACE updateExpression IN collection options

当使用upsert操作UPDATE变体时,找到的文档将被部分更新,这意味着只有updateExpression中指定的属性将被更新或添加。当使用upsert REPLACE变体时,现有文档将被updateExpression的上下文替换

更新文档将使用服务器生成的值修改文档的修订版本号。系统属性_id_key_rev无法更新,_from_to可以。

searchExpression包含要寻找的文件。它必须是没有动态属性名称的对象文字。如果在集合中找不到这样的文档 ,那么将在insertExpression中指定的新文档中插入一个新的文档

如果集合至少有一个文档searchExpression匹配,则将使用updateExpression进行更新当集合中的多个文档与searchExpression匹配时,未定义哪些匹配文档将被更新。因此,通过其他方式(例如唯一索引,应用程序逻辑等)确保最多一个文档与searchExpression匹配,因此通常是合理的

以下查询将在用户集合中查找具有特定名称属性值的文档 如果文档存在,其登录属性将增加1。如果不存在,将插入一个新的文档,其中包括属性名称登录名dateCreated

UPSERT { name: 'superuser' } INSERT { name: 'superuser', logins: 1, dateCreated: DATE_NOW() } UPDATE { logins: OLD.logins + 1 } IN users

请注意,在UPDATE情况下,可以使用OLD伪值参考文档的先前版本

设置查询选项

如上述几个示例中,ignoreErrors选项可用于抑制尝试违反唯一键约束时可能发生的查询错误。

当使用空值更新或替换属性时,ArangoDB不会从文档中删除该属性,而是为其存储一个空值。为了摆脱upsert操作中的属性,将它们设置为null并提供keepNull选项。

如果UPDATE查询和待更新文档中都存在对象属性,那么还可以使用mergeObjects选项来控制对象内容是否被合并

注:为默认值mergeObjects真的,所以没有必要明确指定。

为了确保在更新查询返回时数据是持久的,有waitForSync 查询选项。

退回文件

UPSERT语句可以选择返回数据。为此,他们需要跟着一个RETURN声明(LET也允许使用中间语句)。这些语句可以选择执行计算,并参考伪值OLDNEW如果upsert执行了插入操作,OLD则值为null如果upsert执行更新或替换操作,OLD将在更新/替换之前包含该文档的先前版本。

NEW将始终填充。它将包含插入的文档,以防起来执行插入,或更新/替换的文档,以防执行更新/替换。

这也可以用来检查upsert是否在内部执行了插入或更新:

UPSERT { name: 'superuser' } INSERT { name: 'superuser', logins: 1, dateCreated: DATE_NOW() } UPDATE { logins: OLD.logins + 1 } IN usersRETURN { doc: NEW, type: OLD ? 'update' : 'insert' }
===========================================================================================


WITH

AQL查询可以选择以WITH语句和查询使用的集合列表开头除了查询使用的其他集合以及由AQL查询解析器检测到的其他集合之外WITH中指定的所有集合都将在查询开始时被读取锁定。

WITH中指定进一步的集合对于动态访问集合的查询(例如,通过遍历或通过动态文档访问功能DOCUMENT()可能很有用在查询编译时,这样的集合可能对AQL查询解析器是不可见的,因此在查询开始时将不会自动被锁定。在这种情况下,AQL执行引擎将在使用它们时将这些集合懒惰地锁定,这可能导致与其他查询的死锁。在检测到这种死锁的情况下,查询将自动中止,更改将被回滚。在这种情况下,客户端应用程序可以尝试重新发送查询。但是,如果客户端应用程序使用WITH指定所有查询的已使用集合的列表

WITH为了避免死锁,从群集环境中遍历到ArangoDB 3.1起是必需的。

请注意,对于仅访问单个集合或在查询字符串中指定其他位置的所有集合名称的查询,不需要使用WITHWITH仅在AQL查询解析器无法自动确定查询将使用哪些集合时有用。 WITH仅对动态访问集合的查询有用,例如通过遍历,最短路径操作或DOCUMENT()函数。

WITH managers, usersHaveManagersFOR v, e, p IN OUTBOUND 'users/1' GRAPH 'userGraph'  RETURN { v, e, p }

请注意,常量WITH也是在其他上下文中使用的关键字,例如在UPDATE语句中。如果使用WITH来指定集合的​​额外列表,则必须将其放置在查询字符串的最初位置。

推荐阅读