Search code examples
droolsdsl

drools dsl adding expression to last pattern with '-' not working


I have been working with drools rules for a while and just recently started on a dsl to make the rule authoring easier for end users. While I have been able to get a simple dsl defined and correctly compiling into drl as expected, i cannot get the dsl feature of 'adding constraints to previous expression' to work. I am even trying the simplest of examples from the drools dsl guide and this will not compile the Conditions i have defined beginning with '-' into the previous expression. I keep getting a 'mismatched input 'price' in rule "Rule1Sample_0" error on compiling it. as i said I have this working for straightforward Condition expressions and Consequence expressions. but adding contraints following the docs is just not working at all. I am using drools version 7.0.0.Final, is this something thats not supported until a later version?

in the simple example i am testing, my dsl file just contains:

[condition][]There is a {ShoppingCart} that=${ShoppingCart!lc} : ${ShoppingCart!ucfirst}()
[condition][]- total price is greater than 1000 =totalPrice > 1000

[consequence]Update {ShoppingCart}=System.out.println("{ShoppingCart}" + " test")

Here is the Condition

"There is a ShoppingCart that total price is greater than 1000"

and Action which i am specifying for the when and then part of my template:

"Action" "Update ShoppingCart"

Here is the compiled drl before I pass it to the DrlParser:


    rule "Test1"
      dialect "mvel"
      when
         "There is a ShoppingCart that total price is greater than 1000"
      then
        "Update ShoppingCart"
    end

This is what expandedDrl string contains after above code snippet runs:

package com.sample.test

rule "Test1"
  dialect "mvel"
  when
     $shoppingcart : $Shoppingcart() total price is greater than 1000
  then
    System.out.println("ShoppingCart" + " test")
end

And here is the generated drl for this when i parse it using the DRLParser:

(code snippet here, some omitted)

DrlParser parser = new DrlParser();
        DefaultExpanderResolver resolver = new DefaultExpanderResolver(new StringReader(dsl));
        String expandedDrl = parser.getExpandedDRL(dslr, resolver);

This is what expandedDrl string contains after above code snippet runs:

package com.sample.test

rule "Test1"
  dialect "mvel"
  when
     $shoppingcart : $Shoppingcart() total price is greater than 1000
  then
    System.out.println("ShoppingCart" + " test")
end

And the compiler error i see in the console:

[[13,43]: [ERR 102] Line 13:43 mismatched input 'price' in rule "Test1"  ....

Solution

  • Could you try condition

    There is a ShoppingCart that
    - total price is greater than 1000
    

    Could you try following dsl

    ... business definitions
    [when]complex condition = (simple condition 
                               or another condition)
    [when]simple condition = (total price is not 0)
    [when]another condition = (total price greater than 10)
    
    ... field definitions
    [when]total price = totalPrice
    
    ... consequences I advise to wrap in java util class each as a static method to write java code in java code
    [consequence]Update {ShoppingCart}=System.out.println("{ShoppingCart}" + " test")
    
    ... tech dsl at the bottom
    
    [when]There (is( an?)?|are) {entityType}s?( that)? = ${entityType}: {entityType}()
    [when](is\s+)?not less than(\s+an?)? = >=
    [when](is\s+)?less than(\s+an?)? = <
    [when](is\s+)?not greater than(\s+an?)? = <=
    [when](is\s+)?greater than(\s+an?)? = >
    [when]((is|do(es)?)\s+)?not equals?(\s+to)? = !=
    [when](is\s+)?equals?(\s+to)? = ==
    [when]is not(\s+an?)? = !=
    [when]is(\s+an?)? = ==
    [when]like(\s+an?)? = matches
    [when]{prefix}?\s*(?<![\w])and(?![\w])\s*{suffix}? = {prefix} && {suffix}
    [when]{prefix}?\s*(?<![\w])or(?![\w])\s*{suffix}? = {prefix} || {suffix}
    [when]{prefix}?\s*(?<![\w])not(?! (in|matches|contains|memberOf|soundslike|str))(\s+an?)?(?![\w])\s*\({suffix}? = {prefix} false == (true && {suffix}
    [when]{prefix}?\s*(?<![\w])not(?! (in|matches|contains|memberOf|soundslike|str))(\s+an?)?(?![\w])\s*{suffix}? = {prefix} !{suffix}
    [when](?<![^\(,])\s*- = 
    

    this should be able to process rules like

    There is a ShoppingCart that
    - total price is greater than 1000
    - not complex condition
    

    If you want to reuse conditions in other conditions you want them to be atomic. Good rule of the thumb to embrace RHS with braces () not to break atomicy of the record


    test

    @DroolsSession({ "classpath:/test.rdslr", "classpath:/business.dsl", "classpath:/keywords.dsl" })
    public class PlaygroundTest {
    
        @Rule
        public DroolsAssert drools = new DroolsAssert();
    
        @Test
        public void testIt() {
            drools.insertAndFire(new ShoppingCart(BigDecimal.valueOf(1000.5)));
            drools.insertAndFire(new ShoppingCart(BigDecimal.valueOf(999)));
        }
    }
    

    domain

    public class ShoppingCart {
        public BigDecimal totalPrice;
    
        public ShoppingCart(BigDecimal totalPrice) {
            this.totalPrice = totalPrice; 
        }
    }
    

    rdslr

    rule Test1
        when
            There is a ShoppingCart that 
            - total price is greater than 1000
        then
            print eligible price
    end
    
    rule Test2
        when
            There is a ShoppingCart that 
            - worth a discount
        then
            print eligible price
    end
    

    business.dsl

    [when]worth a discount = 
        (total price is not less than 500
        and not over limit
        or total price is greater than 5000)
    // stupid condition just for demonstration
    [when]over limit = ((total price + total price * 0.05) > total price + 50)
    [when]total price = totalPrice
    
    [then]print eligible price = System.out.println($ShoppingCart.totalPrice);
    

    keywords.dsl

    [when]There (is( an?)?|are) {entityType}s?( that)? = ${entityType}: {entityType}()
    [when](is )?not within\s*\({tail}?= not in ({tail}
    [when](is )?within\s*\({tail}?= in ({tail}
    [when](is )?not one of\s*\({tail}?= not in ({tail}
    [when](is )?one of\s*\({tail}?= in ({tail}
    [when]ignore case '{tail}?= '(?i){tail}
    [when]{param:[$\w\.!'\[\]]+} as {param2:[$\w\.!'\[\]]+}=(({param2}) {param})
    [when](is\s+)?not less than(\s+an?)? = >=
    [when](is\s+)?less than(\s+an?)? = <
    [when](is\s+)?not greater than(\s+an?)? = <=
    [when](is\s+)?greater than(\s+an?)? = >
    [when]((is|do(es)?)\s+)?not equals?(\s+to)? = !=
    [when](is\s+)?equals?(\s+to)? = ==
    [when]is not(\s+an?)? = !=
    [when]is(\s+an?)? = ==
    [when]like(\s+an?)? = matches
    [when]{prefix}?\s*(?<![\w])and(?![\w])\s*{suffix}? = {prefix} && {suffix}
    [when]{prefix}?\s*(?<![\w])or(?![\w])\s*{suffix}? = {prefix} || {suffix}
    [when]{prefix}?\s*(?<![\w])not(?! (in|matches|contains|memberOf|soundslike|str))(\s+an?)?(?![\w])\s*\({suffix}? = {prefix} false == (true && {suffix}
    [when]{prefix}?\s*(?<![\w])not(?! (in|matches|contains|memberOf|soundslike|str))(\s+an?)?(?![\w])\s*{suffix}? = {prefix} !{suffix}
    [when](?<![^\(,])\s*- = 
    

    test output

    00:00:00 --> inserted: ShoppingCart[totalPrice=1000.5]
    00:00:00 --> fireAllRules
    00:00:00 <-- 'Test1' has been activated by the tuple [ShoppingCart]
    1000.5
    00:00:00 --> inserted: ShoppingCart[totalPrice=999]
    00:00:00 --> fireAllRules
    00:00:00 <-- 'Test2' has been activated by the tuple [ShoppingCart]
    999