views:

661

answers:

3

I'm using Drools (for the first time) to express some rules and it has been working really well so far. However I've been given a new condition that I'm not able to express in the rules language very clearly.

Essentially I need to perform an action on the players account if they have an outstanding balance on there account between a certain amount, where they haven't made a payment in the last week and where they haven't made a payment in the last 4 weeks that is greater than or equal to a weekly deduction. There are a few other rules but I've removed them in an effort to simplify the rule for this question. It's the last rule that is causing me a problem.

rule "The broken rule"
   salience 10
   no-loop
   when
      Player( $playerNumber : playerNumber )
      $a : Account( // balance between £5 and £100 and no arrangement
       playerNumber == $playerNumber &&
         accountBalanceInPence >= 500 &&
         accountBalanceInPence <= 10000
      )
      not ( // no payment in last week
         exists AccountTransaction(
            playerNumber == $playerNumber &&
            transactionDate >= oneWeekAgo &&
            transactionCode == "P" // payment
         )
      )
      /* It's this next bit that is broken */
      not ( // no payment > (weekly cost * 4) paid within last 4 weeks
         $deduction : AccountTransaction( // a recent transaction
            playerNumber == $playerNumber &&
            transactionDate >= fourWeeksAgo &&
            transactionCode == "D" // deduction
         )
         exists AccountTransaction( // the payment
            playerNumber == $playerNumber &&
            transactionDate >= fourWeeksAgo &&
            transactionCode == "P" // payment
            amountInPence >= ($deduction->amountInPence * 4)
         )
   )
   then
      // do some action to the account
end

The problem is that it just doesn't work, I keep getting org.drools.rule.InvalidRulePackage exceptions thrown. I was just guessing on the syntax but couldn't seem to find an example that showed what I'm trying to do. Is it even possible?


The full original error message is:

"unknown:50:3 mismatched token: [@255,1690:1695='exists',<39>,50:3]; expecting type RIGHT_PAREN[54,4]: unknown:54:4 mismatched token: [@284,1840:1852='amountInPence',<7>,54:4]; expecting type RIGHT_PAREN[54,22]: unknown:54:22 Unexpected token '$payment'"

After trying the suggestion in the first comment the error is:

"[50,3]: unknown:50:3 mismatched token: [@255,1690:1695='exists',<39>,50:3]; expecting type RIGHT_PAREN[54,4]: unknown:54:4 mismatched token: [@284,1840:1852='amountInPence',<7>,54:4]; expecting type RIGHT_PAREN[54,45]: unknown:54:45 mismatched token: [@293,1881:1881='*',<71>,54:45]; expecting type LEFT_PAREN[55,3]: unknown:55:3 mismatched token: [@298,1890:1890=')',<12>,55:3]; expecting type THEN"
A: 

What about ($deduction->amountInPence * 4)? I think, the -> should be a . instead.

Arne Burmeister
Unfortunately that didn't work, I'll update the question with more detail on the error messages (they don't fit in a comment).
BenM
I think that was a typo that was edited - cause yes, the "->" seems wrong. It was an old syntax that is no longer needed.
Michael Neale
+1  A: 

After some more hacking around the following doesn't cause any runtime errors (though I'm not sure if it's "correct" yet). I rewrote the clause to put the exists around both the facts and used an infix and to group them.

  not ( // no payment > (weekly cost * 4) paid within last 4 weeks
     exists (
        AccountTransaction( // a recent transaction
           playerNumber == $playerNumber &&
           transactionDate >= fourWeeksAgo &&
           transactionCode == "D" // deduction
           $recentDeducation : amountInPence
        ) and
        AccountTransaction( // the payment
           playerNumber == $playerNumber &&
           transactionDate >= fourWeeksAgo &&
           transactionCode == "P" // payment
           amountInPence >= ($recentDeducation * 4)
        )
     )
  )

Thanks for all the help so far.

BenM
Michael Neale
+1  A: 

Hi Ben - yes as you guessed, you need to put an explicit "and" inside the "not" pattern to join them together.

The only time you don't need the "and" is at the top level:

eg

when Foo() Bar()

Doesn't require a an "and"

but this is implicitly the same as

when Foo() and Bar()

So your solution seems correct. The lack of a top level "and" seems to be convention in most rule languages (going back to CLIPS !)

Michael Neale
Accepted this answer as even though I kind of hacked it out myself your answer explains why it works!
BenM
Well thanks - but you did the hard work ! I just tried to explain why it worked. FYI that exception you get is cause it failed to compile (I think in the future version you have to check for error in code, no exception will be thrown IIRC)
Michael Neale