[rules-users] rules for promotional offers

Sandeep Bandela gibsosmat at gmail.com
Fri Sep 9 13:44:57 EDT 2011


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.
-------------- next part --------------
An HTML attachment was scrubbed...
URL: http://lists.jboss.org/pipermail/rules-users/attachments/20110909/870cd549/attachment.html 
-------------- next part --------------
A non-text attachment was scrubbed...
Name: Main.java
Type: application/octet-stream
Size: 4333 bytes
Desc: not available
Url : http://lists.jboss.org/pipermail/rules-users/attachments/20110909/870cd549/attachment.obj 
-------------- next part --------------
A non-text attachment was scrubbed...
Name: ShoppingRules.drl
Type: application/octet-stream
Size: 1717 bytes
Desc: not available
Url : http://lists.jboss.org/pipermail/rules-users/attachments/20110909/870cd549/attachment-0001.obj 


More information about the rules-users mailing list