AQL template in arango Foxx does not work correctly with array

匿名 (未验证) 提交于 2019-12-03 01:41:02

问题:

The below query would not work if I used an array but work normally if we use a string to construct the snippet. Did I make a mistake somewhere or is this one is a bug related to arango?

var array=["1","2"] //Make a snippet depend on array. var snippet=aql.literal(`FILTER u.id IN "${array}"`)  //Main query. Result is incorrect even though it runs var query=db._query(aql` For u in Collection {$snippet} RETURN u ` ) 

Example of the correct query using string

var string="1" var snippet=aql.literal(`FILTER u.id == "${array}"`) 

回答1:

FILTER u.id IN "${array}" produces FILTER u.id IN "1,2". Note that what is on the right-hand side of IN is a string and not an array of strings anymore.

What happens is that the string interpolation (backticks) uses the result of array.toString(), which is 1,2. The surrounding quote marks are taken over literally, which is probably not what you want. See an example at the very end of Queries docs for ArangoJS.

Even if the array representation was ["1","2"], then these quotes would result in "["1","2"]", which would give you a syntax error. With the inner quote marks escaped like "[\"1\",\"2\"]" you would test u.id IN "some string", but the IN operator will return false for any string on the right-hand side (you need an array here).

RETURN "1" IN "1"       // false RETURN "1" IN "[\"1\"]" // false RETURN "1" IN ["1"]     // true 

Let's try different ways to use aql.literal:

> aql.literal(`FILTER u.id IN ${array}`).toAQL() 'FILTER u.id IN 1,2' 

There is a template literal which is evaluate and then passed as argument to the function. The function merely wraps the string produced by the template literal, and the call to toAQL() unwraps it again. toAQL() is normally called internally by the aql helper.

> aql.literal`FILTER u.id IN ${array}`.toAQL() 'FILTER u.id IN ,' 

This uses aql.literal for a tagged template, but it is not meant to be a function to process template strings. The output is of no use.

> aql.literal('FILTER u.id IN ${array}').toAQL() 'FILTER u.id IN ${array}' 

A string in quote marks (no template literal!) is passed as argument to aql.literal. The result is identical to the input, with a dollar sign and curly braces. It is literally the same as the input. This is the intended way how aql.literal() should be used. It does not support bind parameters.

You could rely on JSON.stringify() to correctly escape the array like so:

> aql.literal('FILTER u.id IN ' + JSON.stringify(array)).toAQL() 'FILTER u.id IN ["1","2"]' 

Usage example:

> var snippet = aql.literal('FILTER u.id IN ' + JSON.stringify(array)) > aql`FOR u IN Collection ${snippet} RETURN u`.query 'FOR u IN Collection FILTER u.id IN ["1","2"] RETURN u' 

However, this isn't very graceful and from v6.7.0 of ArangoJS on, it actually supports nesting of aql template literals:

>var snippet = aql`FILTER u.id IN ${array}`  >snippet // see what happens under the hood { query: 'FILTER u.id IN @value0',  // a bind parameter is used!   bindVars: { value0: [ '1', '2' ] },   _source: [Function: _source] }  > aql`FOR u IN Collection ${snippet} RETURN u`  { query: 'FOR u IN Collection FILTER u.id IN @value0 RETURN u',   bindVars: { value0: [ '1', '2' ] }, // still bind parameter with the snippet integrated!   _source: [Function: _source] } 

Usage example (for ArangoJS):

var array = [ "1", "2" ] var snippet = aql`FILTER u.id IN ${array}` var query = db.query(aql`   FOR u IN Collection     ${snippet}     RETURN u `) 

You will be able to use it in Arangosh/Foxx too (db._query()) with ArangoDB v3.4.

Also see: ArangoJS Changelog



标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!