0

I want to create a collection of facts from a nested collection, where the facts do not share a property with another fact in working memory.

Let's say I have a Person class that has a collection of Address objects. I want the set of all the Address facts from Person John that do not have the same zip code as an Address fact currently in working memory.

I'm thinking that the only way to do this is with a "from accumulate", but I can't figure out how to add an additional condition in the source pattern line. When I try the below, I get a rule compilation error:

when
    p:   Person(name == "John")
    h:   HashSet(size > 0) from accumulate (addr: Address(zc: zipcode) from p.addresses /*and not Address(zipcode == zc)*/, 
                                            init(Set s = new HashSet();)
                                            action(s.add(addr);),
                                            result(s) 
                                           )    

I need the contents of the final collection all at once to iterate over in the "then" clause; otherwise I would move the source pattern and the "not" check out of the "from accumulate" and do away with the "from accumulate" entirely.

Is there any way to do what I'm describing? Thanks in advance.

Emily
  • 145
  • 2
  • 8

2 Answers2

0

Since the "on-the-fly" facts from $person.address cannot be combined with proper facts in the WM network, you'll have to fall back on Java.

when
$person: Person(name == "John")
$except: HashSet from collect Address()
hashset: HashSet(size > 0)
            from accumulate (addr: Address() from $person.addresses, 
               init(Set s = new HashSet();)
               action( if( ! $except.contains(addr) ){
                         s.add(addr);
                       } ),
               result(s) )

All of this isn't necessary if the Address components of Person were proper facts as well. A data model according to the Normal Forms in DB design is better suited for Drools as well.

Edit If you just need to skip Addresses with a certain zipcode, accumulate the set of zipcodes.

when
$person: Person(name == "John")
accumulate( Address($zip: zipcode); $except: collectSet($zip) )
hashset: HashSet(size > 0)
            from accumulate ($addr: Address( $zx: zipcode) from $person.addresses, 
               init(Set s = new HashSet();)
               action( if( ! $except.contains( $zx ) ){
                         s.add( $addr );
                       } ),
               result(s) )
laune
  • 31,114
  • 3
  • 29
  • 42
  • Doesn't `zipcode != $except` compare the zipcode against a set? How would you get the zipcode of every element inside $except? I tried your solution, and I got `[Error: incompatible types in statement: class java.util.HashSet (compared from: class java.lang.String)]`. As a bit of an explanation, the Address objects of $p.addresses were actually proper facts in WM at one point, but they were deleted by other rules. Essentially what I want to do with this rule is find the difference between facts that are currently in WM, and the facts that were deleted and stored in $p.addresses. – Emily Feb 05 '16 at 17:40
  • Oops, that was a left-over from a previous try. Edited. – laune Feb 05 '16 at 21:33
  • The modified solution doesn't check the zipcode though, correct? It will add the Address to the final hashset only if the Address object itself is not equal to the Address fact in WM. – Emily Feb 05 '16 at 21:54
  • Difficult to do right if one doesn't know the layout and semantics of the data. If you only need to skip certain zipcodes, the HashSet should be made up from the zipcodes alone. See **Edit**. – laune Feb 06 '16 at 06:10
0

If the Address you want to compare against is already a fact in your session (and you either have one of them in the session or you have a way to uniquely identify it), then you can try something like this:

rule "Test"
    $p: Person(name == "John")
    $a: Address()
    $s: Set( size > 0 ) from collect (
        Address(zipcode != $a.zipcode) from $p.getAdresses()
    )
then
    //The Set $s contains all the addresses from John having
    //a different zipcode than the Address in your working memory. 
end

Hope it helps,

Esteban Aliverti
  • 6,259
  • 2
  • 19
  • 31
  • Unfortunately, there are two reasons why this won't work for me: 1) I also want to build the set when there are no Address facts in WM. That final set then should just have all of the elements from $p.addresses. 2) If I have more than one Address fact in WM that shares a zip code with an Address in $p.addresses, but they are different zip codes (e.g, 01234 and 56789 are both in WM and $p.addresses), then the rule will fire twice. I need to the final set in a single pass. – Emily Feb 05 '16 at 17:32