Drools: Prioritize fact matching based on a field's value - drools

Consider the following scenario in Drools:
We have a rule, matching objects of type A and B against each other.
rule 1
when
$a : A()
$b : B($a.matches($b), flagged == false)
then
mofidy($b) { flag($a) }
end
However, A objects have a field called priority, and when a B arrives in the working memory, a matching A with the highest priority should flag it, above all other matching As.
How is this possible in Drools? How does it affect performance?

Assuming that B.flag() is setting its flagged attribute to true, you can try something like this:
rule 1
when
$a : A()
not A(this != $a, priority > $a.priority)
$b : B($a.matches($b), flagged == false)
then
mofidy($b) { flag($a) }
end
One thing to notice in this example is that if an A object with a high priority is inserted, any B that was already flagged with a lower A will not be reflagged.
If you need to reflag yours Bs, then you can try something like this:
rule 1
when
$a : A()
not A(this != $a, priority > $a.priority)
$b : B($a.matches($b), flag != $a)
then
mofidy($b) { flag($a) }
end
Hope it helps,

Related

How to refine a leaf's range in YANG model?

I have a grouping like -
grouping threshold-value-grouping {
container threshold-value {
description "Threshold value";
leaf upper-limit-val {
description
"Upper limit";
type uint32 {
range "1..60000";
}
}
leaf lower-limit-val {
description
"Lower limit";
type uint32 {
range "1..60000";
}
}
}
}
And i want to reuse this grouping at multiple places.
But when used at different places, the range of the leaves vary.
So i am wondering how can I use the refine statement to achieve?
Or is there any better way to address this issue?
Section 7.13.2 of RFC 7950 explicitly specifies all possible refinements and range is not one of them. Neither is type which can also be seen in the ABNF grammar (section 14):
refine-stmt = refine-keyword sep refine-arg-str optsep
"{" stmtsep
;; these stmts can appear in any order
*if-feature-stmt
*must-stmt
[presence-stmt]
*default-stmt
[config-stmt]
[mandatory-stmt]
[min-elements-stmt]
[max-elements-stmt]
[description-stmt]
[reference-stmt]
"}" stmtsep
But what you can do is to add a must constraint here, something like
uses threshold-value-grouping {
refine threshold-value/upper-limit-val {
must '(. >= 10 and . <= 100)' {
error-message "Here you can only use values between 10 and 100";
}
}
}

why pyang does not throw error in this scenario

the following when condition is referring to a non existing node. I wonder why pyang does not throw error ? It does, if I use a wrong prefix though.
can you review the when conditions (embedded in the module) please.
is it allowed (in when expression) to refer to schema out of the augment itself ?
module mod-w-1 {
namespace "http://example.org/tests/mod-w-1";
prefix m1;
container m1 {
leaf b1 {
type string;
}
}
}
module when-tests {
namespace "http://example.org/tests/when-tests";
prefix wt;
import mod-w-1 {
prefix m1;
}
augment "/m1:m1" {
// when "/m1:m1/b3 = 'abc'";
// there is no b3, so, should be invalid.
// when "/m1:m1/b1 = 'abc'";
// a payload or data situation that has m1/b1 != 'abc' will cause the
// data that fits this augment content will be invalid/rejected.
/* for ex;
<m1>
<b1>fff</b1>
<x>sfsf</x>
<conditional>
<foo>dddd</foo>
</conditional>
</m1>
is invalid, hence, the <x> and <conditional> parts will be
rejected.
*/
leaf x {
type string;
}
container conditional {
leaf foo {
type string;
}
}
}
}
That is because pyang does not validate semantics of XPath expressions at all, only their syntax - and a couple of additional checks, such as function and prefix usage. You will need another YANG compiler to validate those properly.
def v_xpath(ctx, stmt):
try:
toks = xpath.tokens(stmt.arg)
for (tokname, s) in toks:
if tokname == 'name' or tokname == 'prefix-match':
i = s.find(':')
if i != -1:
prefix = s[:i]
prefix_to_module(stmt.i_module, prefix, stmt.pos,
ctx.errors)
elif tokname == 'literal':
# kind of hack to detect qnames, and mark the prefixes
# as being used in order to avoid warnings.
if s[0] == s[-1] and s[0] in ("'", '"'):
s = s[1:-1]
i = s.find(':')
# make sure there is just one : present
if i != -1 and s[i+1:].find(':') == -1:
prefix = s[:i]
# we don't want to report an error; just mark the
# prefix as being used.
my_errors = []
prefix_to_module(stmt.i_module, prefix, stmt.pos,
my_errors)
for (pos, code, arg) in my_errors:
if code == 'PREFIX_NOT_DEFINED':
err_add(ctx.errors, pos,
'WPREFIX_NOT_DEFINED', arg)
elif ctx.lax_xpath_checks == True:
pass
elif tokname == 'variable':
err_add(ctx.errors, stmt.pos, 'XPATH_VARIABLE', s)
elif tokname == 'function':
if not (s in xpath.core_functions or
s in yang_xpath_functions or
(stmt.i_module.i_version != '1' and
s in yang_1_1_xpath_functions) or
s in extra_xpath_functions):
err_add(ctx.errors, stmt.pos, 'XPATH_FUNCTION', s)
except SyntaxError as e:
err_add(ctx.errors, stmt.pos, 'XPATH_SYNTAX_ERROR', e)
Line 1993 of statements.py.
Note that an XPath expression referring to a non-existent node is technically not invalid, not from XPath specification perspective. It just means that an empty node set will be selected by the location path (and that your condition will be false forever).
Yes, you can refer to nodes that are "above" the augment's target node or are its siblings - in fact, you always should when when statement is in play (it should not refer to any node made conditional by it).
Also, you should never attempt to break "module confinement" with a non-prefixed node test (such as b3 and b1). The XPath expression is only able to see names that are defined in imports of the defining module and the defining module itself. For example, even if b3 is later augmented by some unknown third module, your condition would still evaluate to false. It is best to assume that non-prefixed names belong to the defining module's namespace.

Odd use of False constant in if-then statement

Python is my main language, but have to maintain a rather large legacy Perl codebase.
I have an odd logic statement that I can't make heads or tails over.
At top, a constant is defined as:
use constant FALSE => 0;
sub thisFunc {
FALSE if ($self->{_thisVar} ne "tif");
...
...
return statement,etc..
}
So I'm reading that as a kinda' fancy, non-standard if-then statement,
that if $thisVar string is not equal to "tif", then FALSE. Huh?
Not something like $that = FALSE, just FALSE.
The form of this statement appears in the file several times.
This codebase is in use, and vetted over the years by very good team,
so I think it is valid and has meaning. "use strict;" is set at top.
Could someone be so kind as to explain what is meant by logic.
I've Google'd it but no joy.
Thanks in advance,
"if" logic in Perl can be constructed in couple of ways:
the obvious one:
if ($flag) { do_something() }
less obvious one:
do_something() if ($flag);
This example shows how exactly behaves that odd "FALSE if" statement - which only meaning is found when it is LAST statement in subroutine:
use strict;
use constant FALSE => 0;
sub thisFunc {
my $arg = shift;
FALSE if ($arg ne "tif");
}
print "return val: ".thisFunc("ble")."\n";
print "return val: ".thisFunc("tif")."\n";
output from running above is:
return val: 0
return val:
It is pointless. I suspect it's suppose to be
return FALSE if $self->{_thisVar} ne "tif";
There is a similar construct that isn't pointless. If the loop condition has side-effects, the following isn't pointless:
1 while some_sub();
aka
while (some_sub()) { }
aka
while (1) {
some_sub()
or last;
}
Practical example:
$ perl -E'$_ = "xaabbx"; 1 while s/ab//; say'
xx

Drools, how to check if an object with a specific property exists more than once in the list

I have a number of PersonEntitys in my working memory. I need to write a rule to check if two PersonEntitys with the same someProperty are existed in the working memory. I have written as follows, but the problem is, when there are really two PersonEntitys with same someProperty, the sys.out is executing twice:
when
$person : PersonEntity(person.personType == PersonType.LegalPerson);
$personList : ArrayList( size > 1 ) from collect( PersonEntity(someProperty.id == $legalPerson.someProperty.id))
then
System.out.println("error occured");
I also tried this, but I think since my working memory has a number of PersonEntities, the rule is executing 4times (number of PersonEntitys) and my "error occurred " sentence appears 4times in console:
PersonEntity(some conditions, $relatedId :relatedPerson.id);
exists PersonEntity(some conditions, relatedPerson.id > $relatedId)
The standard solution to this problem is to add another constraint using a unique property which guarantees an ordering.
when
PersonEntity(personType == PersonType.LegalPerson, $pid: personId)
exists PersonEntity(personType == PersonType.LegalPerson, personId > $pid)
then
// two LegalPerson
If you expect three or more of the same, you may even have to add another pattern to block multiple activations:
when
PersonEntity(personType == PersonType.LegalPerson, $pid: personId)
not PersonEntity(personType == PersonType.LegalPerson, personId < $pid )
exists PersonEntity(personType == PersonType.LegalPerson, personId > $pid)
then
// two or more LegalPerson
(The suggestion to use collect isn't good unless you really need all of these.)
This will work even when you need to check for any personType:
when
PersonEntity( $pt: personType, $pid: personId)
not PersonEntity(personType == $pt, personId < $pid )
exists PersonEntity(personType == $pt, personId > $pid)
then
// two or more $pt
Here you may add a collect instead of the third pattern to get all such PersonEntity facts.

Coffeescript isn't empty object [duplicate]

This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
is object empty?
update: (id, data) ->
toUpdate = #find(id)
if toUpdate isnt {}
console.log "hi mom"
console.log toUpdate
toUpdate.setProperty(key, value) for own key, value of data
return toUpdate
find:(id) ->
result = record for record in #storage when record.id is id
return result or {}
Given the following Mocha tests
describe '#update', ->
it 'should return an updated record from a given id and data when the record exists', ->
boogie = createData()
archive = new Archive("Dog")
dog = archive.create(boogie)
result = archive.update(1, {name:"Chompie", age:1})
result.name.should.eql "Chompie"
result.age.should.eql 1
result.emotion.should.eql dog.emotion
it 'should return an updated record from a given id and data when the record does not exist', ->
boogie = createData()
archive = new Archive("Dog")
dog = archive.create(boogie)
result = archive.update(50, {name:"Chompie", age:1})
result.should.not.exist
The result is
Archive #update should return an updated record from a given id and data when the record exists: hi mom
{ id: 1,
validationStrategies: {},
name: 'Boogie',
age: 2,
emotion: 'happy' }
✓ Archive #update should return an updated record from a given id and data when the record exists: 1ms
Archive #update should return empty when the record does not exist: hi mom
{}
✖ 1 of 13 tests failed:
1) Archive #update should return empty when the record does not exist:
TypeError: Object #<Object> has no method 'setProperty'
...surprising, isnt it?
CoffeeScript's is (AKA ==) is just JavaScript's === and isnt (AKA !=) is just JavaScript's !==. So your condition:
if toUpdate isnt {}
will always be true since toUpdate and the object literal {} will never be the same object.
However, if #find could return a known "empty" object that was available in a constant, then you could use isnt:
EMPTY = {}
find: ->
# ...
EMPTY
and later:
if toUpdate isnt EMPTY
#...
For example, consider this simple code:
a = { }
b = { }
console.log("a is b: #{a is b}")
console.log("a isnt b: #{a isnt b}")
That will give you this in your console:
a is b: false
a isnt b: true
But this:
class C
EMPTY = { }
find: -> EMPTY
check: -> console.log("#find() == EMPTY: #{#find() == EMPTY}")
(new C).check()
will say:
#find() == EMPTY: true
Demo: http://jsfiddle.net/ambiguous/7JGdq/
So you need another way to check if toUpdate isn't empty. You could count the properties in toUpdate:
if (k for own k of toUpdate).length isnt 0
or you could use the special EMTPY constant approach outlined above. There are various other ways to check for an empty object, Ricardo Tomasi​ has suggested a few:
Underscore offers _.isEmpty which is basically the for loop approach with some special case handling and a short circuit.
Underscore also offers _.values so you could look at _(toUpdate).values().length. This calls map internally and that will be the native map function if available.
You could even go through JSON using JSON.stringify(toUpdate) is '{}', this seems a bit fragile to me and rather round about.
You could use Object.keys instead of the for loop: Object.keys(toUpdate).length isnt 0. keys isn't supported everywhere though but it will work with Node, up-to-date non-IE browsers, and IE9+.
Sugar also has Object.isEmpty and jQuery has $.isEmptyObject.
A short-circuiting for loop appears to be the quickest way to check emptiness:
(obj) ->
for k of toUpdate
return true
false
That assumes that you don't need own to avoid iterating over the wrong things. But given that this is just a test suite and that an emptiness test almost certainly won't be a bottle neck in your code, I'd go with whichever of Underscore, Sugar, or jQuery you have (if you need portability and have to deal with the usual browser nonsense), Object.keys(x).length if you know it will be available, and (k for own k of toUpdate).length if you don't have the libraries and have to deal with browser nonsense and aren't certain that toUpdate will be a simple object.