Share data among rules using a Map - scope

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

Related

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

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

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

How to check if a table is already constructed?

Now I'm using Slick with Spray. I have to say Slick works much nicer alone, non-disturbingly with Spray than with Play (which is really troublesome).
However, I still can't solve a huge problem: database construction.
If there a way for me to maybe pass a list of TableQuery to a function, and it will match variables I passed in with tables in the database, and only create ones that are not created?
That would be really neat.
Assume I have two tables:
val articles = TableQuery[ArticleTable]
val users = TableQuery[UserTable]
I'm creating a function that may look like this:
def createDatabase(list: List[TableQuery[*]]) {
//.... (something like: (Article.articles.ddl ++ User.users.ddl).create)
}
Something like someTableQuery.baseTableRow.tableName should give you the table name. MTable.apply allows you to query for tables. Github search the slick code for examples of MTable.

Nested Lodash ForEach

I have an in memory database that is fairly deep, I think, I have found the best implementation of the code, if anyone has a better way to do a double forEach loop in lodash or if I am using it wrong somehow would be great to know.
The Object:
smallDB = {
user1:{
permissions:[],
locations:[
{ip:'0.0.0.0',messages:[]},
{ip:'',messages:[]}
]
},
user2:{
permissions:[],
locations:[
{ip:'0.0.0.0',messages:[]},
{ip:'0.0.1.0',messages:[{mid:'a unique id','user':'the sender',message:'the text of the message'}]}
]
}
}
sending a message I use:
ld.forEach smallDB, (a)->
ld.forEach a.locations, (b)->
b.messages.push {mid,user,message}
I just wanted to make sure this is the best way to add messages into the mini database for all the users in smallDB and in all their locations
There's one very easy optimization you can do if the objects you create with {mid,user,message} are immutable:
record = {mid,user,message}
ld.forEach smallDB, (a)->
ld.forEach a.locations, (b)->
b.messages.push record
If you do this, you save yourself the cost of creating a new object each time you push. Dropping the forEach in favor of using for would also make things faster.
It would be further optimizable by allowing a more direct access to the arrays that you access as b.messages in your original code, but this would complicate addition and removal of users or locations. I don't think is worth it unless proved by profiling that it is not a premature optimization.

Using Groovy find String from Exclusion List

In groovy, I want to search text (which is typically an xml structure) and find an occurrence of the ignore list.
For example:
My different search data requests are (reduced for clarity, but most are large):
<CustomerRQ field='a'></CustomerRQ>
<AddressRQ field='a'></AddressRQ>
My ignore list is:
CustomerRQ
CustomerRS
Based on the above two incoming requests of "customer" and "address", I want to ignore "Customer" since it's in my ignore list, but I want to identify "address" as a hit.
The overall intent is to use this for logging. I want to not log some incoming requests based on my "ignore" list, but all others will be logged.
Here's some pseudo code that may be on the right track but not really.
def list = ["CustomerRQ", "CustomerRS"]
println(list.contains("<CustomerRQ field='a'>"))
I'm not sure, but I think a closure will work in this case, but learning the groovy ropes here. Maybe a regexp will work as well. But the importance is to search in the incoming string (indexOf, exists...) across all of my exclusions list.
A quick solution:
shouldIgnore = list.inject(false) { bool, val -> bool || line.contains(val) }
Whether or not this is the best idea depends on information we don't have; it may be better to do something more-XMLy rather than checking against a string.

Resources