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