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?
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.