AQL: (why) are document functions like HAS, UNSET not working? - arangodb

I try to use document functions like HAS, UNSET etc. (hopefyully) like they are described in the documentation. Unofortunately the lead to Syntax error 1501. I also see that they do NOT get highlighted in the AQL editor like the other signal words do.
Here is one example (which I also tested on the tutorial server):
FOR u IN users
LIMIT 1
UNSET(u, "birthday")
RETURN u
Does anybody sees what's wrong?

An AQL function cannot appear on the top-level of an AQL. The only things allowed on the top-level are statements such as FOR, FILTER, RETURN, LET, COLLECT, SORT, INSERT etc.
If a function should be executed, it's return value should be captured inside a LET statement for further processing, or, if no further processing is required, the function can be called in a RETURNs expression, e.g.
FOR u IN users
LIMIT 1
RETURN UNSET(u, "birthday")

OK, OK ... after writing this I got it: One has to assign this to something. e.g.
FOR u IN users
LIMIT 1
LET tmp = UNSET(u, "birthday")
RETURN tmp
Sorry for posting it ... but I keep it in, maybe other beginners do the same mistake :-)

This may be helpful for other users: The UNSET function does not actually replace the document in the collection. To do this, you need to run
FOR u IN users
LIMIT 1
LET u_new = UNSET(u, "birthday")
REPLACE u WITH u_new IN users

Related

How to get all mentions from a message

I made a function which needs 2 mentions, so I first check the size with:
if (message.mentions.members.size == 2)
It works, but then I need to get both mentions. The following works, but it only gets me the first one:
message.mentions.members.first()
I've also tried other ways to get both mentions, like this one below, that returns undefined.
message.mentions.members[0]
first() takes an optional count parameter if you see here.
You can use this to return an array of the members, like so:
message.members.first(2);
The reason mentions.members[0] doesn't work is because members is a Collection (a discord.js enhancement of a map) which uses a snowflake as a key, 0 is not a valid snowflake and thus returns undefined.

AEM Query builder exclude a folder in search

I need to create a query where the params are like:
queryParams.put("path", "/content/myFolder");
queryParams.put("1_property", "myProperty");
queryParams.put("1_property.operation", "exists");
queryParams.put("p.limit", "-1");
But, I need to exclude a certain path inside this blanket folder , say: "/content/myFolder/wrongFolder" and search in all other folders (whose number keeps on varying)
Is there a way to do so ? I didn't find it exactly online.
I also tried the unequals operation as the parent path is being saved in a JCR property, but still no luck. I actually need unlike to avoid all occurrences of the path. But there is no such thing:
path=/main/path/to/search/in
group.1_property=cq:parentPath
group.1_property.operation=unequals
group.1_property.value=/path/to/be/avoided
group.2_property=myProperty
group.2_property.operation=exists
group.p.or=true
p.limit=-1
This is an old question but the reason you got more results later lies in the way in which you have constructed your query. The correct way to write a query like this would be something like:
path=/main/path/where
property=myProperty
property.operation=exists
property.value=true
group.p.or=true
group.p.not=true
group.1_path=/main/path/where/first/you/donot/want/to/search
group.2_path=/main/path/where/second/you/donot/want/to/search
p.limit=-1
A couple of notes: your group.p.or in your last comment would have applied to all of your groups because they weren't delineated by a group number. If you want an OR to be applied to a specific group (but not all groups), you would use:
path=/main/path/where
group.1_property=myProperty
group.1_property.operation=exists
group.1_property.value=true
2_group.p.or=true
2_group.p.not=true
2_group.3_path=/main/path/where/first/you/donot/want/to/search
2_group.4_path=/main/path/where/second/you/donot/want/to/search
Also, the numbers themselves don't matter - they don't have to be sequential, as long as property predicate numbers aren't reused, which will cause an exception to be thrown when the QB tries to parse it. But for readability and general convention, they're usually presented that way.
I presume that your example was just thrown together for this question, but obviously your "do not search" paths would have to be children of the main path you want to search or including them in the query would be superfluous, the query would not be searching them anyway otherwise.
AEM Query Builder Documentation for 6.3
Hope this helps someone in the future.
Using QueryBuilder you can execute:
map.put("group.p.not",true)
map.put("group.1_path","/first/path/where/you/donot/want/to/search")
map.put("group.2_path","/second/path/where/you/donot/want/to/search")
Also I've checked PredicateGroup's class API and they provide a setNegated method. I've never used it myself, but I think you can negate a group and combine it into a common predicate with the path you are searching on like:
final PredicateGroup doNotSearchGroup = new PredicateGroup();
doNotSearchGroup.setNegated(true);
doNotSearchGroup.add(new Predicate("path").set("path", "/path/where/you/donot/want/to/search"));
final PredicateGroup combinedPredicate = new PredicateGroup();
combinedPredicate.add(new Predicate("path").set("path", "/path/where/you/want/to/search"));
combinedPredicate.add(doNotSearchGroup);
final Query query = queryBuilder.createQuery(combinedPredicate);
Here is the query to specify operator on given specific group id.
path=/content/course/
type=cq:Page
p.limit=-1
1_property=jcr:content/event
group.1_group.1_group.daterange.lowerBound=2019-12-26T13:39:19.358Z
group.1_group.1_group.daterange.property=jcr:content/xyz
group.1_group.2_group.daterange.upperBound=2019-12-26T13:39:19.358Z
group.1_group.2_group.daterange.property=jcr:content/abc
group.1_group.3_group.relativedaterange.property=jcr:content/courseStartDate
group.1_group.3_group.relativedaterange.lowerBound=0
group.1_group.2_group.p.not=true
group.1_group.1_group.p.not=true

Masking answer options in Confirmit (jscript)

I'm trying to mask the answer options that show up in a 3DGrid question item in Confirmit, using the value of a background variable.
E.g. when "background1" ==1, display answer category 1. If "background1" ==0, do not display answer category 1. If "background2" ==1, display category 3, otherwise do not. In any case, display answer category 2.
Hopefully this is easy for someone out there (I'm a psychologist, not a coder...so not so much so for me :/)
Thanks!
In order to access the data inside a question/variable we can use the f function of confirmit.
for instance:
f('my_question_id').get();
When masking a question, we need to pass in a Set object so Confirmit knows what Code's to show and not to show.
Often you will mask using a Set from a previous question. So you pass in the question_id and Confirmit does all the other magic.
Here we have the problem of not having a Set, so we will have to create our own.
For this, there are 2 approaches (can be found in the scripting manual under Working with Sets > Methods of the set Object > add and remove and Working with Sets > User defined functions...)
I'm going to stick to the first one because it is easier to use ;)
What we will do first is create a script node (it doesn't matter where you create it, just somewhere in the survey, I often have a folder Functions with all my script nodes in somewhere at the bottom of my survey)
In that script file we will have our function that crates our set:
function CreateMyAwesomeSet()
{
//create an empty Set
var mySet = new Set();
//if background1 equals 1, add 1 to our Set
if ( f('background1').get() == '1' )
{
mySet.add(1);
}
//return the Set of allowed Codes
return mySet;
}
Here we declare a function that we now can use wherever we want to.
So now, If we want to use this Set, we add a Code Mask to your grid:
CreateMyAwesomeSet()
You can ofcourse change the name of the function, and add extra if statements.
hope this helps

Select only one row in dql subquery

I have to execute following query:
create dm_myobject object
set my_id_attribute = (select r_object_id from dm_otherobject where <some clause here>)
where ...
But subquery in brackets returns more than one id. I can't make whereclause more detailed to retrieve only one value.
How to take first?
ENABLE(FETCH_ALL_RESULTS 1) or ENABLE(RETURN_TOP 1) doesn't help.
In my experience it is impossible to use DQL hints in a sub query like you suggested, because the hint is applied to the query as a whole. It is indeed possible to use, say, ENABLE(RETURN_TOP 1) on a query that contains a sub query, however that hint will then be used on the outer query and never on the inner one. In your case, however, you'll end up with an error message telling that the sub query returns more than one result.
Try using an aggregate function on the selected attribute instead:
CREATE dm_myobject OBJECT
SET my_id_attribute = (
SELECT MIN(r_object_id)
FROM dm_otherobject
WHERE <some clause>
)
The MIN and MAX functions work with ints and strings, and I suspect they work with IDs too. Since it is ok for you to set only the first ID that's returned from your sub query, I suspect you're returning them in a sorted order and want to use the first -- hence the usage of the MIN function.
An alternative approach would of course be to write a script or a small Java program that executes several DQL statements, but that might or might not work for you in your case.

Share data among rules using a Map

This should be simple, but I am still lost.
There is a very similar post here: How to share data between Drools rules in a map?
but it doesn't fix my problem:
I have a set of rules and, before launching them, I insert a Map<String, Object> as a fact.
In these rules I use the map to write some conclusions like:
when
$map : Map();
something ocurrs;
then
$map.put("conclusion1", 100);
Now I would like to use these intermediate conclusions in other rules, something like:
when
$map : Map(this["conclusion1"] > 50)
then
do something cool;
The problem is that when I execute the rules it is like the second rule doesn't see the conclusions of the first one, and it does not fire.
I have tried putting a break point and analysing the working memory, and, in fact, the Map would contain the conclusion1, 100 after the first rule is fired.
I have also tried by making an update($map) in the conclusion, but that would trigger an infinite loop.
Any idea of why this wouldn't work, or any alternative solution to my problem?
Thanks !
When you modify a fact you need to notify the engine you are doing so. One of the ways of doing that is by using modify(). E.g.:
when
$map : Map();
something ocurrs;
then
modify( $map ) {
put("conclusion1", 100)
}
end

Resources