[rules-users] Maps in Drools

André Thieme address.good.until.2009.dec.14 at justmail.de
Fri Aug 21 15:47:39 EDT 2009


Edson Tirelli schrieb:
> 
> 
> 2009/8/20 André Thieme <address.good.until.2009.dec.14 at justmail.de 
> <mailto:address.good.until.2009.dec.14 at justmail.de>>
> 
>     Would there not be an addition to the syntax needed, for the default
>     rule language? For mvel it would not require a change I guess.
> 
> 
> No, as I mentioned to you, the idea is for the DRL to remain the same, 
> so that the rules author does not have to worry about what the IT guys 
> are doing with the domain model. So, the rules author would write:
> 
> Customer( name == "bob" )
> 
> The IT guy would simply use a configuration to tell the engine: this 
> object type uses a map format, that one is a POJO, that other is an XML 
> entity, etc.
> For instance, if he wants to do that in DRL (he could also use API, or 
> conf files), he could do:
> 
> declare Customer
>     @format( map )
> end
> 
> declare Order
>     @format( pojo )
> end
> 
> So, we have a clear distinction between the technical aspects and the 
> business aspects of the application.

Ah okay, that makes sense and sounds very good!
So, when Maps become first class not the rules will introduce a new
syntax, but intead we would add some declarations.
The only two challenges I see with that is:
1. to allow the keys to be any object and not just only strings.
2. differentiate between fields of the Maps and keys

Point 1.:
Your example  Customer( name == "bob" )  currently is expressed as:
$m : Map(eval($m.get("type" == "Customer")) &&
          eval($m.get("name") == "bob"))
right?
In the declaration for Customer above one would probably have to specify
that for a Map being a Customer depends on the key "type".
But what would  Customer( name == "bob" ) look like if name was not a
String but some other Object?
This info should go into the declare part, yes?


Point 2.:
in your other reply you gave the example:
Map( this["type"] == "Point", $x : this["x"], size == 5 )

Projected on the  Customer( name == "bob" )  example we would need to
differentiate, in Java terms, between:
map.get("size") == 5    and   map.size() == 5
So   Customer(name=="Bob" && size==5)   needs to be clear.



> Your clojure macro would generate always the same DRL code "Customer( 
> name == "bob" )", but you said yourself that clojure is 100% java 
> compatible, so imagine the enterprise had a domain model implemented as 
> pojos already and as part of the new application some new entities are 
> modeled in clojure. The macro would generate always the same code, but 
> you would configure some entities in the domain as POJOs and other 
> entities as Maps.

Yes, it would be similar like that.
What I first do is to write a general macro named "rule" which should
be able to express everything that currently is possible in Drools.
On top of that macro I will define some more, for exmaple there could be
a macro "map-rule" which is specialized on maps.
This can get even more specialized, but in the end all those macros will
expand into the general one, and the general one will generate a
string S which contains all rules and then do a
ResourceFactory.newByteArrayResource(S.getBytes("utf-8")) to get the
rules into Drools, which will from then on continue.

And the way as you suggest it, to let the Drools syntax always be the
same, it may even be easier for me to write my lib. Good :)


>     Hmm, but the MVEL syntax can not magically eliminate the eval. Under the
>     hood the map accesses will still be inside an eval. Marc confirmed that
>     a few days ago.
>     MVEL only hides this from the user. This is what I will also do.
>     But under the hood it will become
> 
>       $a:Map()
>       $b:Map()
>       eval( $a.get("type") == "Customer" )
>       eval( $b.get("type") == "DailyOrders" )
> 
> 
> Here I think we have other misunderstanding.  I will try to explain, but
> ideally you need to learn a bit about the Rete algorithm to see the 
> whole picture.

Yes, I will read soon (I hope) Chapter 12 of my Drools book,
by Michal Bali.



> There are 2 types of eval(). Inline eval() and top level eval().

Ah, I see, I didn't know that.


> What you wrote above is a top level eval, meaning it will become 
> a node in the rete network. So your example above generates an 
> "execution plan" (making an analogy with SQL) that will get all Maps in 
> the working memory, join them in tuples size 2, and then test each tuple 
> for the 2 evals. So, you see why this will generate C(n,2) partial 
> matches, while C(n,2) as we know is n!/(n-2)!, what is really bad for 
> growing "n".

Very good, now I understand what you mean. Thanks for the sql analogy.


> Now, the same thing could be written using inline evals as:
> 
> $a:Map( eval( $a.get("type") == "Customer" ) )
> $b:Map( eval( $b.get("type") == "DailyOrders" ) )

Excellent, this is very helpful. I just tried this out and was also
successful with it. Now I can have my code generating alpha constraints
without having to use MVEL.


>   In this case, the inline eval() will generate an alpha constraint in 
> the network, i.e., it will be applied BEFORE the joins. So, instead of 
> doing all combinaions possible between all maps as above, it will first 
> find all Customer maps and all DailyOrders maps and only after that will 
> make a join between them. So you get Customers * DailyOrders partial 
> matches. The above evals are semantically equivalent as:
> 
> $a:Map( this[ "type" ] == "Customer" )
> $b:Map( this[ "type" ] == "DailyOrders" )
> 
>   
>  > But currently I am forced to produce this cross product, as there is 
> no direct support for Maps yet.
> 
> I hope that by the above you see that the problem of the cross products 
> is not a problem with Maps support, but rather a question of how to 
> write better rules.

Yes, thanks to your good explanation this is now totally clear.
I just didn't know about inline evals. In my first tries I did something
like  Map(this.get("type") == "Customer") but that didn't work.
I did not try Map(this["type] == "Customer") because I thought this is
not possible without setting the dialect to MVEL. Now I understand that
part.

For me only two issues remain
1. I can not set vars to values of Maps if they keys don't happen to be
    strings. So, I now *can* do:
    $a:Map( eval($a.get("type") == "Customer"), $id : this["id"] )
    and then refer to $id in other parts of the LHS or in the RHS.
    But I get an error for
    global Foo $x;
    ...
    $a:Map( eval($a.get("type") == "Customer"), $id : this[$x] )

    This results to a compilation error:
    Unable to create Field Extractor for 'this[$x]' : [Rule name='Clojure
    test 2']
    org.drools.RuntimeDroolsException: Field/method '$x' not found for
    class 'java.util.Map'Rule Compilation error : [Rule name='Clojure
    test 2']
    myNamespace/Rule_Clojure_test_2_0.java (14:722) :
    $id cannot be resolved

    So it seems that MVEL does not like global vars between the brackets
    IF the expression is not followed by some operator. I updated my
    mvel.jar as you suggested some days ago, and now I can use vars for
    binary operators:
    global Foo $key;
    ...
    $a:Map( this[$key] == 45 )


2. This is a minor issue: I can't do it without MVEL. I tried
    $a:Map( eval($a.get("type") == "Customer"), $id : eval($a.get("id")))
    and I tried
    $a:Map( eval($a.get("type") == "Customer"), eval($id : $a.get("id")))
    but both didn't work.


Thanks Edson, and many greetings,
André
-- 
Lisp is not dead. It’s just the URL that has changed:
http://clojure.org/



More information about the rules-users mailing list