[rules-users] rules for promotional offers

Wolfgang Laun wolfgang.laun at gmail.com
Mon Sep 12 12:10:35 EDT 2011


2011/9/12 Sandeep Bandela <gibsosmat at gmail.com>

> wolfgang,
> I didnt get it, so does that mean that all CE will be re-evaluated on every
> change of workingmemory no matter which rule fired?


Not "all CE", only the ones that are affected by the changed WME.


> even when executing RHS. I didnt test it with copy of list yet. I will do
> it and see.


By all means, do so.
-W


>
> On Sun, Sep 11, 2011 at 11:28 AM, Wolfgang Laun <wolfgang.laun at gmail.com>wrote:
>
>> The result of the collect CE is changed due to the modify. You have to
>> copy the list and use the copy in the loop.
>> -W
>>
>> 2011/9/9 Sandeep Bandela <gibsosmat at gmail.com>:
>> > Thanks wolfgang, for the info and that workaround.
>> > I think there is some off by one error in for loop, or may be some
>> mistake
>> > that I didnt notice in my code. the following rule with 3 objects gives
>> > index out of bound error. I am attaching java & drl file (package
>> > com.example.drools.testing). I am using 5.2.0 final release.
>> >
>> > rule "Buy X units of Product A and Get Y units of Product B Free"
>> >     when
>> >         $itemsA : ArrayList( $a : size >= 2 ) from collect( CartItem(
>> > productId == "A", processed == false) )
>> >         $itemsB : ArrayList( $b : size >= 1 ) from collect( CartItem(
>> > productId == "B", processed == false) )
>> >         $m : RuleMessage()
>> >  then
>> >          $m.addMessage("sizes- A="+$a + " B="+$b);
>> >          int $x = 2;
>> >         int $y = 1;
>> >         $m.addMessage("sizes- X="+$x + " Y="+$y);
>> >         for(int i=0 ; i < $x ; i++){
>> >              CartItem $ci = (CartItem) $itemsA.get(i);
>> >              $m.addMessage( $ci.getId()+")Found item:
>> "+$ci.getProductId());
>> >              modify((CartItem)$ci){
>> >                 setProcessed(true),
>> >                 setPromoItemDiscount(0D),
>> >                 setPromoId("2"),
>> >                 setBundleId(bundler.getId()),
>> >                 setAggregatorId(1)
>> >             };
>> >          };
>> >          for(int i=0 ; i < $y  ; i++){
>> >          CartItem $ci = (CartItem) $itemsB.get(i);
>> >              $m.addMessage( $ci.getId()+")Found item:
>> "+$ci.getProductId());
>> >              modify((CartItem)$ci){
>> >                 setProcessed(true),
>> >                 setPromoItemDiscount($ci.getPrice()),
>> >                 setPromoId("2"),
>> >                 setBundleId(bundler.getId()),
>> >                 setAggregatorId(2)
>> >             };
>> >          };
>> >         bundler.increment();
>> > end
>> >
>> >
>> > error
>> > Exception in thread "main" org.drools.runtime.rule.ConsequenceException:
>> > rule: Buy X units of Product A and Get Y units of Product B Free
>> >
>> >     at
>> >
>> org.drools.runtime.rule.impl.DefaultConsequenceExceptionHandler.handleException(DefaultConsequenceExceptionHandler.java:39)
>> >     at
>> > org.drools.common.DefaultAgenda.fireActivation(DefaultAgenda.java:916)
>> >     at
>> org.drools.common.DefaultAgenda.fireNextItem(DefaultAgenda.java:845)
>> >     at
>> org.drools.common.DefaultAgenda.fireAllRules(DefaultAgenda.java:1056)
>> >     at
>> >
>> org.drools.common.AbstractWorkingMemory.fireAllRules(AbstractWorkingMemory.java:733)
>> >     at
>> >
>> org.drools.common.AbstractWorkingMemory.fireAllRules(AbstractWorkingMemory.java:699)
>> >     at
>> >
>> org.drools.impl.StatefulKnowledgeSessionImpl.fireAllRules(StatefulKnowledgeSessionImpl.java:218)
>> >     at com.example.drools.testing.Main.main(Main.java:52)
>> > Caused by: java.lang.IndexOutOfBoundsException: Index: 1, Size: 1
>> >     at java.util.ArrayList.RangeCheck(ArrayList.java:547)
>> >     at java.util.ArrayList.get(ArrayList.java:322)
>> >     at
>> >
>> com.example.drools.testing.Rule_Buy_X_units_of_Product_A_and_Get_Y_units_of_Product_B_Free_0.defaultConsequence(Rule_Buy_X_units_of_Product_A_and_Get_Y_units_of_Product_B_Free_0.java:12)
>> >     at
>> >
>> com.example.drools.testing.Rule_Buy_X_units_of_Product_A_and_Get_Y_units_of_Product_B_Free_0DefaultConsequenceInvoker.evaluate(Rule_Buy_X_units_of_Product_A_and_Get_Y_units_of_Product_B_Free_0DefaultConsequenceInvoker.java:44)
>> >     at
>> > org.drools.common.DefaultAgenda.fireActivation(DefaultAgenda.java:906)
>> >     ... 6 more
>> >
>> >
>> > 2011/9/9 Wolfgang Laun <wolfgang.laun at gmail.com>
>> >>
>> >> If the declared type of productId and getProductId() is String, it
>> should
>> >> be compared to a string literal. (A conversion might be made, but don't
>> rely
>> >> on that.)
>> >>
>> >> You cannot access components of a global in RHS code using the
>> simplified
>> >> notation (not unless you use "mvel" - but don't do that.) Here, all
>> accesses
>> >> should be written the usual Java way, with getter and setter calls.
>> >>
>> >> The problem you have with modify(ci) is a bug (fixed for 5.3.0). As a
>> >> workaround,use
>> >>
>> >> modify( (CartItem)ci ){ ... }
>> >>
>> >> -W
>> >>
>> >>
>> >> 2011/9/9 Sandeep Bandela <gibsosmat at gmail.com>
>> >>>
>> >>> some more info for the modify() problem, the CartItem is an interface
>> and
>> >>> the implementation is CartItemImpl. if this info leads to some clues.
>> >>>
>> >>>
>> >>> On Fri, Sep 9, 2011 at 1:28 PM, Sandeep Bandela <gibsosmat at gmail.com>
>> >>> wrote:
>> >>>>
>> >>>> Hi here is the same thing converted to mvl
>> >>>>
>> >>>> // this Bundler.java has integer member that keeps incrementing
>> >>>> global Bundler bundler
>> >>>>
>> >>>> rule "Apply 10% discount if you purchase 2 items"
>> >>>>     when
>> >>>>         $s1 : CartItem( productId == 219759, processed == false)
>> >>>>         $s2 : CartItem( this != $s1, productId == $s1.productId,
>> >>>> processed == false)
>> >>>>         $m : RuleMessage()
>> >>>>  then
>> >>>>         $m.addMessage("Found item1: " + $s1.getName() + " id:
>> >>>> "+$s1.getId());
>> >>>>         $m.addMessage("Found item2: " + $s2.getName() + " id:
>> >>>> "+$s2.getId());
>> >>>>         modify($s1){
>> >>>>         setProcessed(true),
>> >>>>         setPromoItemDiscount($s1.getPrice() * 0.1),
>> >>>>         setPromoId("1"),
>> >>>>         setBundleId(bundler.getId()),
>> >>>>         setAggrigatorId(1)
>> >>>>         };
>> >>>>         modify($s2){
>> >>>>         setProcessed(true),
>> >>>>         setPromoItemDiscount($s2.getPrice() * 0.1),
>> >>>>         setPromoId("1"),
>> >>>>         setBundleId(bundler.getId()),
>> >>>>         setAggrigatorId(1)
>> >>>>         };
>> >>>>         $m.addMessage("bundled ["+bundler.getId()+"]
>> >>>> "+$s1.getId()+","+$s2.getId());
>> >>>>         bundler.increment();
>> >>>> end
>> >>>>
>> >>>> the above rule dosent fire, but if I change the highlighter to
>> >>>> getProductId() == 219759, or use productId == "219759", may be this
>> is some
>> >>>> edge case but it took long time for me to figure it out productId is
>> String.
>> >>>> now consider the bundler.getId(), if I use bundler.id I get
>> compilation
>> >>>> error.
>> >>>>
>> >>>> Rule Compilation error : [Rule name='Apply 10% discount if you
>> purchase
>> >>>> 2 items']
>> >>>>
>> >>>>
>> com/example/promotions/promotionEngine/Rule_Apply_10__discount_if_you_purchase_2_items_0.java
>> >>>> (23:1592) : The field Bundler.id is not visible
>> >>>> 2011-09-09 13:18:39,863  INFO [STDOUT] - 2011-09-09 13:18:39,863
>> INFO
>> >>>> [PromotionsManager] - ksession initialized
>> >>>> 2011-09-09 13:18:39,863  INFO [STDOUT] - 2011-09-09 13:18:39,863
>> INFO
>> >>>> [PromotionsManager] - Exploding items and inserting cart
>> >>>> 2011-09-09 13:18:39,864  INFO [STDOUT] - 2011-09-09 13:18:39,863
>> ERROR
>> >>>> [CheckOutBasedUtilities] - Failed to apply promo offers
>> >>>> java.lang.RuntimeException: Unexpected global [bundler]
>> >>>>     at
>> >>>>
>> org.drools.common.AbstractWorkingMemory.setGlobal(AbstractWorkingMemory.java:613)
>> >>>>     at
>> >>>>
>> org.drools.impl.StatefulKnowledgeSessionImpl.setGlobal(StatefulKnowledgeSessionImpl.java:332)
>> >>>>     at
>> >>>>
>> com.example.promotions.promotionEngine.PromotionsManager.applyOffers(PromotionsManager.java:154)
>> >>>>     at
>> >>>>
>> com.example.promotions.promotionEngine.PromotionsManager.shuffleForOffers(PromotionsManager.java:61)`
>> >>>>
>> >>>> lastly my actual problem that I am looking for a solution lets say we
>> >>>> have "Buy X items of A and get Y Items of B". X=5 & Y=4 from the
>> original
>> >>>> problem I want to template the rule and load the values of X,A,Y,B
>> from
>> >>>> database or excel etc. if I use modify the error is as follows
>> >>>>
>> >>>> my rule
>> >>>>
>> >>>>
>> >>>> rule "Buy X units of Product A and Get Y units of Product B Free"
>> >>>>     when
>> >>>>         $itemsX : ArrayList( size == 2 ) from collect( CartItem(
>> >>>> getProductId() == 24257, !isProcessed()))
>> >>>>         $itemsY : ArrayList( size == 1) from collect(CartItem(
>> >>>> getProductId() == 24260, !isProcessed()))
>> >>>>         $m : RuleMessage()
>> >>>>  then
>> >>>>          for(Object item : $itemsX){
>> >>>>              CartItem ci = (CartItem) item;
>> >>>>              $m.addMessage( ci.getId()+")Found item:
>> "+ci.getItemId()+"
>> >>>> name: "+ci.getName());
>> >>>>              modify(ci){
>> >>>>                 setProcessed(true),
>> >>>>                 setPromoItemDiscount(0),
>> >>>>                 setPromoId("2"),
>> >>>>                 setBundleId(bundler.getId()),
>> >>>>                 setAggrigatorId(1)
>> >>>>             };
>> >>>>          }
>> >>>>          for(Object item : $itemsY){
>> >>>>          CartItem ci = (CartItem) item;
>> >>>>              $m.addMessage( ci.getId()+")Found item:
>> "+ci.getItemId()+"
>> >>>> name: "+ci.getName());
>> >>>>              modify(ci){
>> >>>>                 setProcessed(true),
>> >>>>                 setPromoItemDiscount(ci.getPrice()),
>> >>>>                 setPromoId("2"),
>> >>>>                 setBundleId(bundler.getId()),
>> >>>>                 setAggrigatorId(2)
>> >>>>             };
>> >>>>          }
>> >>>>         bundler.increment();
>> >>>> end
>> >>>>
>> >>>> Rule Compilation error : [Rule name='Buy X units of Product A and Get
>> Y
>> >>>> units of Product B Free']
>> >>>>
>> >>>>
>> com/example/promotions/promotionEngine/Rule_Buy_X_units_of_Product_A_and_Get_Y_units_of_Product_B_Free_0.java
>> >>>> (11:1236) : The method setProcessed(boolean) is undefined for the
>> type
>> >>>> Object
>> >>>>
>> >>>>
>> com/example/promotions/promotionEngine/Rule_Buy_X_units_of_Product_A_and_Get_Y_units_of_Product_B_Free_0.java
>> >>>> (12:1265) : The method setPromoItemDiscount(int) is undefined for the
>> type
>> >>>> Object
>> >>>>
>> >>>>
>> com/example/promotions/promotionEngine/Rule_Buy_X_units_of_Product_A_and_Get_Y_units_of_Product_B_Free_0.java
>> >>>> (13:1299) : The method setPromoId(String) is undefined for the type
>> Object
>> >>>>
>> >>>>
>> com/example/promotions/promotionEngine/Rule_Buy_X_units_of_Product_A_and_Get_Y_units_of_Product_B_Free_0.java
>> >>>> (14:1325) : The method setBundleId(Integer) is undefined for the type
>> Object
>> >>>>
>> >>>>
>> com/example/promotions/promotionEngine/Rule_Buy_X_units_of_Product_A_and_Get_Y_units_of_Product_B_Free_0.java
>> >>>> (15:1364) : The method setAggrigatorId(int) is undefined for the type
>> Object
>> >>>>
>> >>>>
>> com/example/promotions/promotionEngine/Rule_Buy_X_units_of_Product_A_and_Get_Y_units_of_Product_B_Free_0.java
>> >>>> (22:1702) : The method setProcessed(boolean) is undefined for the
>> type
>> >>>> Object
>> >>>>
>> >>>>
>> com/example/promotions/promotionEngine/Rule_Buy_X_units_of_Product_A_and_Get_Y_units_of_Product_B_Free_0.java
>> >>>> (23:1731) : The method setPromoItemDiscount(Double) is undefined for
>> the
>> >>>> type Object
>> >>>>
>> >>>>
>> com/example/promotions/promotionEngine/Rule_Buy_X_units_of_Product_A_and_Get_Y_units_of_Product_B_Free_0.java
>> >>>> (24:1777) : The method setPromoId(String) is undefined for the type
>> Object
>> >>>>
>> >>>>
>> com/example/promotions/promotionEngine/Rule_Buy_X_units_of_Product_A_and_Get_Y_units_of_Product_B_Free_0.java
>> >>>> (25:1803) : The method setBundleId(Integer) is undefined for the type
>> Object
>> >>>>
>> >>>>
>> com/example/promotions/promotionEngine/Rule_Buy_X_units_of_Product_A_and_Get_Y_units_of_Product_B_Free_0.java
>> >>>> (26:1842) : The method setAggrigatorId(int) is undefined for the type
>> Object
>> >>>>
>> >>>> Rule Compilation error : [Rule name='Apply 10% discount if you
>> purchase
>> >>>> 2 items']
>> >>>>
>> >>>>
>> com/example/promotions/promotionEngine/Rule_Apply_10__discount_if_you_purchase_2_items_0.java
>> >>>> (23:1592) : The field Bundler.id is not visible
>> >>>>
>> >>>>
>> >>>> On Fri, Sep 9, 2011 at 12:40 PM, Sandeep Bandela <
>> gibsosmat at gmail.com>
>> >>>> wrote:
>> >>>>>
>> >>>>> Hi Wolfgang,
>> >>>>> This is what I did initially but for product > 2 I didnt find a
>> generic
>> >>>>> way to do this. following is my first rule. I didnt use field names
>> because
>> >>>>> it was giving error that member is not accessible. I will attach the
>> errors
>> >>>>> of each case in a few mins.
>> >>>>>
>> >>>>> rule "Apply 10% discount if you purchase 2 items"
>> >>>>>     dialect "java"
>> >>>>>     when
>> >>>>>         $s1 : CartItem( getProductId() == 236061, !isProcessed())
>> >>>>>         $s2 : CartItem( getProductId() == 236061, getId() !=
>> >>>>> $s1.getId(), !isProcessed())
>> >>>>>         $m : RuleMessage()
>> >>>>>  then
>> >>>>>         $m.addMessage("Found item1: " + $s1.getName() + " id:
>> >>>>> "+$s1.getId());
>> >>>>>         $m.addMessage("Found item2: " + $s2.getName() + " id:
>> >>>>> "+$s2.getId());
>> >>>>>         modify($s1){
>> >>>>>         setProcessed(true),
>> >>>>>         setPromoItemDiscount($s1.getPrice() * 0.1),
>> >>>>>         setPromoId("1"),
>> >>>>>         setBundleId(bundler.getId()),
>> >>>>>         setAggrigatorId(1)
>> >>>>>         };
>> >>>>>         modify($s2){
>> >>>>>         setProcessed(true),
>> >>>>>         setPromoItemDiscount($s2.getPrice() * 0.1),
>> >>>>>         setPromoId("1"),
>> >>>>>         setBundleId(bundler.getId()),
>> >>>>>         setAggrigatorId(1)
>> >>>>>         };
>> >>>>>         bundler.increment();
>> >>>>> end
>> >>>>>
>> >>>>> 2011/9/9 Wolfgang Laun <wolfgang.laun at gmail.com>
>> >>>>>>
>> >>>>>> Your rule exhibits procedural thinking. (For an aspiring rule
>> >>>>>> programmer his is typical teething troubles.)
>> >>>>>>
>> >>>>>> There's no need to collect all A's and B's in order to determine
>> their
>> >>>>>> number being greater than one or two - this is achieved
>> automatically by a
>> >>>>>> simple triple match rule. Consider
>> >>>>>>
>> >>>>>> rule "2A - 1B"
>> >>>>>> when
>> >>>>>>    $a1: CartItem( productId == "A", processed == false )
>> >>>>>>    $a2: CartItem( this != $a1, productId == "A", processed == false
>> )
>> >>>>>>    $b: CartItem( productId == "B", processed == false )
>> >>>>>> then
>> >>>>>>    modify( $a1 ){...}
>> >>>>>>    modify( $a2 ){...}
>> >>>>>>    modify( $b ){...}
>> >>>>>> end
>> >>>>>>
>> >>>>>> Note:
>> >>>>>>
>> >>>>>> The modify is essential. (When you say "doesn't work", always
>> provide
>> >>>>>> details.)
>> >>>>>> Use the field name rather than the getter call - it improves
>> >>>>>> readability.
>> >>>>>> I'm not sure whether 5.2.0 final handles boolean fields correctly -
>> >>>>>> IIRC, there were some issues. It's possible that CartItem(...,
>> !processed)
>> >>>>>> works.
>> >>>>>>
>> >>>>>> -W
>> >>>>>>
>> >>>>>> 2011/9/8 Sandeep Bandela <gibsosmat at gmail.com>
>> >>>>>>>
>> >>>>>>> Hi,
>> >>>>>>> I am new to rules engine, after going through the examples shipped
>> >>>>>>> (shopping.drl, petstore.drl)
>> >>>>>>> I am trying to implement promotional discounting using drools
>> 5.2.0
>> >>>>>>> final, stateful session. assume cart has all the items that
>> customer wants
>> >>>>>>> to buy,
>> >>>>>>> I am trying to bundle them together with the existing offers
>> >>>>>>>
>> >>>>>>> rule "Buy X units of Product A and Get Y units of Product B Free"
>> >>>>>>>     dialect "java"
>> >>>>>>> when
>> >>>>>>>     $itemsA : ArrayList( size >= 2) from collect( CartItem(
>> >>>>>>> getProductId() == "A", !isProcessed()))
>> >>>>>>>     $itemsB : ArrayList( size >= 1) from collect( CartItem(
>> >>>>>>> getProductId() == "B", !isProcessed()))
>> >>>>>>> then
>> >>>>>>>     // current scenario buy 2*A and get 1*B
>> >>>>>>>     int x = 2;
>> >>>>>>>     int y = 1;
>> >>>>>>>     for(int i=0 ; i < x ; i++){
>> >>>>>>>         CartItem $ci = (CartItem) $itemsA.get(i);
>> >>>>>>>         // modify($ci){setProcessed(true) ... } dosent work
>> >>>>>>>         $ci.setProcessed(true);
>> >>>>>>>         $ci.setItemDiscount(0.0);
>> >>>>>>>         $ci.setBundleId(bundler.getId());
>> >>>>>>>     }
>> >>>>>>>
>> >>>>>>>     for(int i=0 ; i < y  ; i++){
>> >>>>>>>         CartItem $ci = (CartItem) $itemsB.get(i);
>> >>>>>>>         $ci.setProcessed(true);
>> >>>>>>>         $ci.setItemDiscount($ci.getPrice());
>> >>>>>>>         $ci.setBundleId(bundler.getId());
>> >>>>>>>     }
>> >>>>>>>     // global counter to identify bundled items
>> >>>>>>>     bundler.increment();
>> >>>>>>> end
>> >>>>>>>
>> >>>>>>> the above rule calculates only for 1 set of offer i.e first 2*A &
>> 1*B
>> >>>>>>> are considered, rest in the cart are not bundled.
>> >>>>>>> if customer buys 4*A + 2*B it dosent consider it. am I missing
>> >>>>>>> anything? is this the right way to do it?
>> >>>>>>> any feedback is appreciated.
>> >>>>>>>
>> >>>>>>>
>> >>>>>>>
>> >>>>>>>
>> >>>>>>> --
>> >>>>>>> Regards,
>> >>>>>>> Sandeep Bandela.
>> >>>>>>>
>> >>>>>>>
>> >>>>>>> _______________________________________________
>> >>>>>>> rules-users mailing list
>> >>>>>>> rules-users at lists.jboss.org
>> >>>>>>> https://lists.jboss.org/mailman/listinfo/rules-users
>> >>>>>>>
>> >>>>>>
>> >>>>>>
>> >>>>>> _______________________________________________
>> >>>>>> rules-users mailing list
>> >>>>>> rules-users at lists.jboss.org
>> >>>>>> https://lists.jboss.org/mailman/listinfo/rules-users
>> >>>>>>
>> >>>>>
>> >>>>>
>> >>>>>
>> >>>>> --
>> >>>>> Regards,
>> >>>>> Sandeep Bandela.
>> >>>>>
>> >>>>
>> >>>>
>> >>>>
>> >>>> --
>> >>>> Regards,
>> >>>> Sandeep Bandela.
>> >>>>
>> >>>
>> >>>
>> >>>
>> >>> --
>> >>> Regards,
>> >>> Sandeep Bandela.
>> >>>
>> >>>
>> >>> _______________________________________________
>> >>> rules-users mailing list
>> >>> rules-users at lists.jboss.org
>> >>> https://lists.jboss.org/mailman/listinfo/rules-users
>> >>>
>> >>
>> >>
>> >> _______________________________________________
>> >> rules-users mailing list
>> >> rules-users at lists.jboss.org
>> >> https://lists.jboss.org/mailman/listinfo/rules-users
>> >>
>> >
>> >
>> >
>> > --
>> > Regards,
>> > Sandeep Bandela.
>> >
>> >
>> > _______________________________________________
>> > rules-users mailing list
>> > rules-users at lists.jboss.org
>> > https://lists.jboss.org/mailman/listinfo/rules-users
>> >
>> >
>>
>> _______________________________________________
>> rules-users mailing list
>> rules-users at lists.jboss.org
>> https://lists.jboss.org/mailman/listinfo/rules-users
>>
>
>
>
> --
> Regards,
> Sandeep Bandela.
>
>
> _______________________________________________
> rules-users mailing list
> rules-users at lists.jboss.org
> https://lists.jboss.org/mailman/listinfo/rules-users
>
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: http://lists.jboss.org/pipermail/rules-users/attachments/20110912/5e2277c4/attachment.html 


More information about the rules-users mailing list