Edson Tirelli (JIRA) was heard to exclaim, On 04/11/07 05:57:
Edson Tirelli commented on JBRULES-740:
Being brief, using the following rule:
rule "Find all PRs for user"
when
$user : User( )
$pr_list : RecordSet( size > 0 ) from
collect( PRRecord( $resp : responsible -> ( $user.hasReport($resp) ) ) )
then
$user.setRecords($pr_list, DataSource.GNATS);
end
if you already have "User" facts asserted into the working memory, it means
that for every PRRecord that matches your <constraints>, it will recalculate
the "collect". So, if you have a User fact, and assert a PRRecord, it will
create a RecordSet and add your PRRecord to the set. When you assert a second
PRRecord, it will create a new RecordSet and add the 2 PRRecord. As you see,
it is a progression: 1+2+3+4...+N. Your case is even worst because your
constraint is a predicate (not indexable right now), so, it means the
progression above is executed in full.
As it turns out, this is actually quite fast; it takes 50-100 msec per PR
assertion, so I can cram in 10,000 of them in a few minutes. Nonetheless, I
understand that it is less than optimal at the moment.
Anyway, what you need to do:
1) If possible, adjust your business model in a way to replace the predicate
for a regular "==" constraint, avoiding full iteration because of indexing.
There's no way to do that. What I really want is this:
$user : User( $reports : reports )
$pr_list : RecordSet( size > 0 ) from
collect( PRRecord( $reports contains responsible ) )
(the syntax could also be "responsible containedIn $reports").
But Drools doesn't (yet?) support that.
For now, I could warp my application such that I could write this:
collect( PRRecord( responsible_users contains $user ) )
Is that something that Drools could index?
That would introduce some unpleasant dependencies in my code, but if it allows
indexing, it might be worth it.
2) Use a control fact to limit the partial matches. Example:
rule "Find all PRs for user"
when
$control : ControlFact( phase == "findPR" )
$user : User( )
$pr_list : RecordSet( size > 0 ) from
collect( PRRecord( $resp : responsible -> ( $user.hasReport($resp) ) ) )
then
$user.setRecords($pr_list, DataSource.GNATS);
$control.setPhase( "do everything else" )
modify( $control )
end
Doing the above, make sure you assert all PRRecords first and only then, set
(or assert) your control fact to phase "findPR". The above will make the
engine avoid wasting time calculating partial matches that will never be
used. It seems ugly at first, but it is a technique that unfortunately needs
to be used in some special cases.
That doesn't really fit with the application flow. I could have a rule like this:
when
ControlFact( )
$user : User( )
$pr_list : RecordSet( size > 0 ) from
collect( PRRecord( $resp : responsible -> ( $user.hasReport($resp) ) ) )
then
$user.setRecords($pr_list, DataSource.GNATS);
And retract the ControlFact before asserting PR objects, then assert it when I'm
ready to fireAllRules(). Would that have the desired effect?
Thanks.
--
Dirk Bergstrom dirk(a)juniper.net
_____________________________________________
Juniper Networks Inc., Computer Geek
Tel: 408.745.3182 Fax: 408.745.8905