axnet
axnet

Reputation: 5780

how to do custom comparison operations in drools rule file?

Note: drools version used is 8.29.0

I want to perform a set based operations in drl file using mvel.

for e.g. drl file for simple rule match


// Test Fact Class
package com.abc;
@Data
@AllArgsConstructor
public class Test {
    private String name;
    private List<String> contactNumbers;
}

// drl rules file
import com.abc.Test;

rule "name-rule"
  when
    t : Test( name == "abc" );
  then
    System.out.print("abc found");
end;



// not working
rule "M1 rule-data matches fact"
  when
    p : Test( ["1234", "5678"] contains contactNumbers );
  then
    System.out.print("M1 rule-data matches fact FOUND");
end;

// not working
rule "M2 fact matches rule-data"
  when
    p : Test( contactNumbers contains  ["1234", "5678"] );
  then
    System.out.print("M2 fact matches rule-data FOUND");
end;

// main method snippet
// creating Fact objects
String[] s1 = {"1234", "5678", "2222"};
Test t1 = new Test("abc", Arrays.asList("1111"));
Test t2 = new Test("xyz", Arrays.asList(s1));


// inserting facts to check against rules
// kieContainer is created using above mentioned drl file
KieSession kieSession = kieContainer.newKieSession();
kieSession.insert(t1);
kieSession.insert(t2);
kieSession.fireAllRules();
kieSession.dispose();

When I execute above, I only get to match rule 1, which outputs "abc found".

Question:

  1. How can I achieve many to many match in drl file.
    Ans : one way i figured is below.

    Test( contactNumbers contains "1234" && contactNumbers contains "5678" );


    if any other better way than above, please let me know.

  2. How can I achieve or implement the custom comparisons in MVEL?
    i.e. If any 2 items of the facts matches the list present in rule etc., or any custom filtering on collections.
    any example of such implementation would be really useful.

Upvotes: 0

Views: 327

Answers (1)

Roddy of the Frozen Peas
Roddy of the Frozen Peas

Reputation: 15184

contains and memberOf are for a single value and a list. Which you use depends on which you have in the object in working memory, and which you're defining in the rule.

Examples:

"contains":

ClassMates( names contains "Jim" )

"memberOf":

Person( $name: name )
Classmates( $name memberOf names )

So for your example, you'd likely do:

Test(contactNumbers contains "12345",
     contactNumbers contains "67890" )

This would trigger if the Test instance has a contactNumbers field that includes "12345" and "67890". contactNumbers could contain other values as well. If there are two Test instances, and one contains "12345" and the other contains "67890", this rule will not fire.

You can test that contactNumbers contains only the values you're testing for using an accumulate and a length check:

$t: Test( $numbers: contactNumbers )
Set( size == $numbers.size() ) from accumulate(
  $n: String(this in ("12345", "67890")) from $numbers,
  collectSet($n)
)

I used a Set to exclude duplicates. Older Drools you can do a similar with 'collect'.


I've matched your syntax here using Test(...). Note that the current docs recommend the "oopath" style of /tests [contactNumbers contains "12345", contactNumbers contains "6789"] ... other than the harder-to-read syntax it should be functionally the same.

Upvotes: 1

Related Questions