AQL: Bind parameter on operator - arangodb

Is there a way to have bind parameter on operator ("<", "<=" etc ...) ? I'm working on a Foxx service.
Example :
const operator = '<'
const res = query`
FOR v IN myCollection
FILTER v.value ${operator} ${maxValue}
`
I can do it with db._query :
const operator = '<'
const res = db._query('
FOR v IN myCollection
FILTER v.value ${operator} #maxValue'
{ maxValue: 100 })

Normal bind parameters (with one #) can only be used for the values null, true, false, numbers, strings, arrays and objects.
Collection bind parameters (with two ##) can be used where collection names are specified.
Passing an operator via bind parameters is not possible in AQL, as it could likely change the meaning of a query, or render it totally invalid.
Consider the following example:
FOR v IN myCollection
FILTER v.value #operator #maxValue
This query does not even parse, regardless of what values are passed in the bind parameters. And this is a good thing, because otherwise one may pass something like #operator: "abc", #maxValue: ">=", which would mean the query can be parsed fine without bind parameters, but would produce a parse error with bind parameters injected.
So the easiest solution here is to inject the comparison operator into the query via template string substituion, though of course you need to make sure the requested comparison operator is in a whitelisted of allowed operators.
But you would need to do this even with bind parameters, as otherwise people could just send #operator: "!=" or #operator: "NOT IN" or other operators which you either don't expect or that can make your query more expensive.

Related

Remove a map item in terraform

If I use the following yamldecode, I get the following output:
output "product" {
value = distinct(yamldecode(file("resource/lab/abc.yaml"))["account_list"]["resource_tags"][*]["TAG:product"])
}
Output:
+ product = [
+ "fargate",
+ "CRM",
]
I want fargate to be removed from my output and the expected output is this:
+ product = [
+ "CRM"
]
Please let me know how I can do this.
output "product" {
value = compact([for x in distinct(yamldecode(file("resource/lab/abc.yaml"))["account_list"]["resource_tags"][*]["TAG:product"]) : x == "fargate" ? "" : x])
}
I get this output:
test = [
"enter",
]
compact function solved the problem.
The Terraform language is based on functional principles rather than imperative principles and so it does not support directly modifying an existing data structure. Instead, the best way to think about goals like this is how to define a new value which differs from your existing value in some particular way.
For collection and structural types, the most common way to derive a new value with different elements is to use a for expression, which describes a rule for creating a new collection based on the elements of an existing collection. For each element in the source collection we can decide whether to use it in the result at all, and then if we do decide to use it we can also describe how to calculate a new element value based on the input element value.
In your case you seem to want to create a new list with fewer elements than an existing list. (Your question title mentions maps, but the question text shows a tuple which is behaving as a list.)
To produce a new collection with fewer elements we use the optional if clause of a for expression. For example:
[for x in var.example : x if x != "fargate"]
In the above expression, x refers to each element of var.example in turn. Terraform first evaluates the if clause, and expects it to return true if the element should be used. If so, it will then evaluate the expression immediately after the colon, which in this case is just x and so the result element is identical to the input element.
Your example expression also includes some unrelated work to decode a YAML string and then traverse to the list inside it. That expression replaces var.example in the above expression, as follows:
output "products" {
value = toset([
for x in yamldecode(file("${path.module}/resource/lab/abc.yaml"))["account_list"]["resource_tags"][*]["TAG:product"]: x
if x != "fargate"
])
}
I made some other small changes to the above compared to your example:
I named the output value "products" instead of "product", because it's returning a collection and it's Terraform language idiom to use plural names for collection values.
I wrapped the expression in toset instead of distinct, because from your description it seems like this is an unordered collection of unique strings rather than an ordered sequence of strings.
I added path.module to the front of the file path so that this will look for the file in the current module directory. If your output value is currently in a root module then this doesn't really matter because the module directory will always be the current working directory, but it's good practice to include this so that it's clear that the file belongs to the module.
Therefore returning a set is more appropriate than returning a list because it communicates to the caller of the module that they may not rely on the order of these items, and therefore allows the order to potentially change in future without it being a breaking change to your module.

Not able to use for loop in ternary operator in arangodb

How do we write conditions in arango, that includes for loops. I can elaborate the requirement below.
My requirement is if a particular attribute(array type) exists in the arango collection, i would read data from the collection(that requires a loop) or else, might do the following :
return null
return empty string ""
do nothing.
Is this possible to achieve in arango?
The helping methods could be -->
-- has(collectionname, attributename)
-- The ternary operator ?:
let attribute1 = has(doc,"attribute1") ?(
for name in doc.attribute1.names
filter name.language == "xyz"
return name.name
) : ""
But this dosent work. Seems like arango compiler first attempts to compile the for loop, finds nulls and reports error as below. Instead, it should have compiled "has" function first for the ternary operator being used.
collection or array expected as operand to FOR loop; you provided a value of type 'null' (while executing)
If there is a better way of doing it, would appreciate the advice!!
Thanks in advance!
Nilotpal
Fakhrany here from ArangoDB.
Regarding your question, this is a known limitation.
From https://www.arangodb.com/docs/3.8/aql/fundamentals-limitations.html:
The following other limitations are known for AQL queries:
Subqueries that are used inside expressions are pulled out of these
expressions and executed beforehand. That means that subqueries do not
participate in lazy evaluation of operands, for example in the ternary
operator. Also see evaluation of subqueries.
Also noted here for the ternary operator:
https://www.arangodb.com/docs/3.8/aql/operators.html#ternary-operator.
An answer to the question what to do may be to use a FILTER before enumerating over the attributes:
FOR doc IN collection
/* the following filter will only let those documents passed in which "attribute1.names" is an array */
FILTER IS_ARRAY(doc.attribute1.names)
FOR name IN doc.attribute1.names
FILTER name.language == "xyz"
RETURN name.name
Other solutions are also possible. Depends a bit on the use case.

Azure DataFlow with Filter activity for input column's value NOT IN Array parameter (expression builder)

Is it possible that I can use expression and array parameter with Filter activity in the data flow to help me filter out no need records.
My source is a parquet file with a column called FileID, and I plan to give an array parameter which contains the list of no-need FileID, then in the Filter I can use expression to implement if FileID in the array parameter then pass it to the next step OR just filter out.
I tried these expressions but they don't work.
P.S. ExistFileID is the integer array parameter I passed into the data flow e.g. [1,2,3]
FileID !in $ExistFileID
FileID in $ExistFileID == false()
So I found the answer from another post. Thanks for #Steve Zhao sharing
Azure Data Factory "Not In" Expression
so if your filter want to implement that
When your NewID NOT IN ExistID, then pass the NewID.
Use following expression.
!in(ExistID, NewID)

How to quote/escape a field name in AQL for arangodb?

I cannot find where to quote a field name that has a space in it, for example when doing
FILTER s._key = a.`Supplier Id`
The above, sql-style quote doesn't work, neither does array access. What's the correct way?
Figured it out now, I got bitten by SQL and forgot that equality comparison is made with == in AQL. Then the array access worked, so the way to use field names with spaces is this:
FILTER s._key == a['Supplier Id']
If the field is without spaces but has some special characters, it works to use backtick instead of array access:
FILTER s._key == a.`ÅterförsäljareId`
Edit: Another option is to use bind variables:
FILTER s._key == a.#field
// Passing this to the API as bind variables:
{
"field": "Supplier Id"
}

Custom MongoDB search query

In my database I have documents which all contain the property foo. For each value of foo I have a function that either returns true or false. How can I query for all the documents for which the value of foo makes the function return true?
If you need to check if your string field's value is one of several, you need the $in modifier.
db.collection.find( { field : { $in : array } } );
It works fast and uses index (if possible).
If your field is an array and you pass a string, use this syntax.
db.collection.find({array_field : string_value});
It will check every element in the array and, if any of them matches your string, it will return the document.
You could use $where.
Example:
db.myCollection.find( { $where: "this.a > 3" });
db.myCollection.find( "this.a > 3" );
db.myCollection.find( { $where: function() { return this.a > 3;}});
Note, this is run in Javascript. This means two things.
You can put arbitrary Javacript into $where expression (the function form).
It'll be significantly slower than regular queries.
It really depends on what the function is and how you are using it. Is the function constant for any given record? Is it even a function you can evaluate on the database server? ...
In the extreme, if you need to check this value often, you might, for example, create a field that exists only when f(foo) is true and then create a sparse index on that field.
$where may well be the solution you are looking for, but depending on the access patterns there may be a better solution.

Resources