[rules-users] Maps in Drools

André Thieme address.good.until.2009.dec.14 at justmail.de
Thu Aug 20 16:11:24 EDT 2009


Edson Tirelli schrieb:
> 
>  > So, in principle having Maps support in the LHS is not a big challenge?
> 
> No. Just requires developing a set of classes to work with maps. Being 
> brief:
> 
> * Implement: MapObjectType extends ObjectType
> * Implement: MapReadAccessor and MapWriteAccessor
> * Add support to them into the builders (i.e., the actual wiring of the 
> new classes)
> * Test everything... I probably forgot something, since there's been 
> quite some time since I worked on this.

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.


>  > Could you please explain this in a bit more detail?
> Lets call |A| the number of A facts in the working memory and |B| the 
> number of B facts (i.e. cardinality). Lets call p(|A|,|B|) the number of 
> partial matches a rule will create given the cardinality of A and B. 
> Imagine your rule is:
> 
> when
>    A()
>    B()
> then
> 
> This rule creates a join between A() and B() and the number of join 
> attempts will be: p(|A|,|B|) = |A| * |B|
> 
> Now, if instead of representing A and B as different classes you use 
> Maps with the type attribute as you was suggesting:
> 
> $a : Map()
> $b : Map()
> eval( ... )
> 
> The number of partial matches will be: p(|A|,|B|) = (|A|+|B|)! / 
> (|A|+|B|-2)!

Ah yes okay, I see what you mean.
Well, the current situation for me is that I have to do exactly that.
As it is typical for Clojure apps to store data in Maps I need support
for that. So, I want to allow rules like:

(map-rule "Rule name", "type"
   (when
     "Customer" ( $cust-id "id" )
     "DailyOrders" (= 1 (get "count" $cust-id)))
   (then
     (println "match")))

This is semantically exactly the same as in your example, where Customer
and DailyOrders were POJOs, while they are for me Maps.


> Draw the graph for these two functions and you will get the picture. If 
> you have more than 2 patterns, situation gets worst and worst.
> 
> If instead of eval() you use some alpha constraints, things go down to 
> the same level as using different classes:
> 
> $a : Map( this["type"] == "A" )
> $b : Map( this["type"] == "B" )
> 
> The above will result in the same p(|A|,|B|) = |A| * |B| partial matches.

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" )


And the only reason why I need to do it this way is because in its
current version Drools does not support Maps as 1st class objects.
If it would, then the eval would not be needed anymore.
So, for Clojure users (and basically everyone else who stores data
inside Maps, for whatever the reasons may be) it would be so good to
have that support.

You gave a perfect explanation of why this would be a great addition.

The actual rule syntax is not important for me. My lib will hide it,
as MVEL already does now.
But my lib will compile the rules into the fast native rule language.
It won't be interpreted or any slower than hand written rules.
But currently I am forced to produce this cross product, as there is
no direct support for Maps yet.



> 2009/8/19 André Thieme <address.good.until.2009.dec.14 at justmail.de 
> <mailto:address.good.until.2009.dec.14 at justmail.de>>
> 
>     Edson Tirelli schrieb:
>      >
>      >     I will skip the first half of your e-mail as I am not sure
>     what were
>      > the reasons for your nit-picking. If my explanation was not
>     helpful for
>      > the public it was intended to, you are welcome to explain yourself.
> 
>     Oh, I did not intend it to sound like nit-picking. I only meant that
>     with a specialized syntax one can make rules operating on Maps looking
>     basically identical to the ones operating on POJOs.
> 
> 
>      >     Regarding the part that matters, i.e., adding the support to
>     other
>      > fact types, Drools is prepared to support them in the LHS. Let me
>      > explain by example:
>      >
>      > when
>      >     $c : Customer( name == "bob" )
>      > then
>      >
>      >    For the reasoning algorithm, does not matter if Customer is a
>     POJO,
>      > an XML document, a Map with the "type" attribute set to "Customer" as
>      > you mentioned, or whatever you can think. We use a set of interfaces
>      > that allows us to completely abstract that and we even supported 2
>      > different fact types as a proof of concept in the past.
> 
>     That sounds good!
>     So, in principle having Maps support in the LHS is not a big challenge?
>     As I understand it, code inside an eval can not be cached and needs to
>     get executed every time and results in less performant code.
> 
> 
>      >    The only reason we did not support multiple fact types yet for
>     Drools
>      > is the RHS. Our reasoning is that does not make sense to abstract the
>      > LHS if you don't do the same for the RHS. So, for instance, using
>     java
>      > dialect:
>      >
>      > when
>      >     $c : Person( name == "bob" )
>      > then
>      >     $c.setAge( 30 ) ;
>      > end
>      >
>      >     If we will support that rule, written as is, for POJOs, and
>     we want
>      > to support Maps as facts, then our java parser needs to properly
>     handle
>      > the consequence code as $c.put("age", 30). Same thing if Person
>     was an
>      > XML document and so on.
> 
>      From my perspective the RHS is not important at all for my lib and for
>     Clojure users who like to work with Drools.
>     For me mostly one thing is interesting: getting Map lookups out of eval,
>     so they can profit from exactly the same caching and optimizations that
>     exist for POJOs.
> 
>     The RHS will be fully written in Clojure, and all challenges that occur
>     in it would have to be solved by myself.
>     If you Drools Devs could make it possible to give support for Maps in
>     the LHS, then most issues for Clojure users could be solved.
> 
> 
>      >
>      >     If you want to contribute to the project solving this
>     problem, you
>      > are most welcome.
> 
>     Unfortunately I have not enough Java knowledege and not time.
>     But I would like to contribute indirectly, by writing a lib for Clojure
>     users which will make Drools easily accessible to them. It would also
>     provide other users of Drools with an alternative syntax, which gets
>     compiled into the default rule language. (Not into mvel, as that seems
>     to be interpreted and runs a bit slower.)
> 
> 
>      >     Regarding your rule rewrite, the way you propose is not feasible.
>      > Using multiple patterns of the same type without proper alpha
>      > constraints will lead to combinatorial explosion.
> 
>     Could you please explain this in a bit more detail?
>     If Maps as 1st class rule objects, shouldn't my example then be exactly
>     the same as the one that you gave?
> 
>     Your example was:
> 
>     when
>       Customer( $custId : id )
>       DailyOrders( count[$custId] == 1 )
>     then ...
> 
> 
>     My example was:
> 
>     rule "Rule name"
>       when
>         m1:clojure.lang.APersistentMap()
>         m2:clojure.lang.APersistentMap()
>         eval( m1.get("type") == "Customer" )
>         eval( m2.get("type") == "DailyOrders" &&
>              (m2.get("count")).get(m1.get("id")) == 1 )
>       then
>         ((clojure.lang.IFn)globalVarWithClojureCode.get(17)).call())
>     end
> 
> 
>     And with direct Map support it could become something like:
> 
>     rule "Rule name"
>       MapDefaultKey "type"
>       Salience      2
>       when
>         "Customer"( $custId : "id" )
>         "DailyOrders"( get("count", $custId) == 1 )
>       then
>         ((clojure.lang.IFn)globalVarWithClojureCode.get(17)).call())
>     end
> 
>     Instead of the one could have syntactical sugar which may look
>     unfamiliar: "count"[$custId]
> 
>     If the key is not a string but a float one would even have
>     88.5[$custId] instead get(88, $custId).
>     This is just a matter of taste, and users could have their DSLs anyway.
> 
> 
>     André
>     --
>     Lisp is not dead. It’s just the URL that has changed:
>     http://clojure.org/
>     _______________________________________________
>     rules-users mailing list
>     rules-users at lists.jboss.org <mailto:rules-users at lists.jboss.org>
>     https://lists.jboss.org/mailman/listinfo/rules-users
> 
> 
> 
> 
> -- 
>  Edson Tirelli
>  JBoss Drools Core Development
>  JBoss by Red Hat @ www.jboss.com <http://www.jboss.com>
> 
> 
> ------------------------------------------------------------------------
> 
> _______________________________________________
> rules-users mailing list
> rules-users at lists.jboss.org
> https://lists.jboss.org/mailman/listinfo/rules-users


André
-- 



More information about the rules-users mailing list