Search code examples
nesteddroolsforall

How do I do nested foralls in Drools?


Question

Considering these classes:

class BookCase { ArrayList<Book> books }

class Book { ArrayList<Page> pages }

class Page { String color }

And considering this natural language rule:

When all pages in a bookcase are black, do A

The trivial approach would be to nest forall clauses, but in Drools can't do that, because forall clauses only allow Patterns (not Conditional Elements, what a forall clause is) inside!

How do I express this in Drools then?


Solution

  • You can actually nest multiple foralls if you don't use forall(p1 p2 p3...), but the equivalent not(p1 and not(and p2 p3...)). Then, to keep individual books and pages from firing the rule, chuck an exists in between there.

    rule 'all pages in bookcase are black'
        when
            (and
            $bookCase: BookCase()
                (not (exists (and
                    $book: Book() from $bookCase.books
                    not( (and
                        not( (exists (and
                            $page: Page() from $book.pages
                            not( (and
                                // slightly different constraint than I used in question
                                eval($page.color == $book.color)
                            ) )
                        ) ) )
                    ) )
                ) ) )
            )
        then
            ...
    end
    

    Unlike using accumulate to create a flat list of all pages, this will maintain the context of $page, that is, when a page is put in a constraint with its parent book as in the example above, in this solution Drools still 'knows' what the parent book is.