'Loop through a list on Drool rules

I need to write a rule using Drool that will validate that the phone number is valid US number (for the sake of this answer we can use this regex "^([0-9]{3})[0-9]{3}-[0-9]{4}$")

The object structure is like this:

{
  "name": {"firstName":"John", "lastName":"Smith"},
  "phone": [{"phoneType": "mobile", "phoneNumber":"123456789"}],
  "dob":"01/01/2000"
}

Because phone is a list, I don't know how to loop and validate this object. For instance the validation for "dob" field looks something like this:

rule "RuleId: Drool01, Validate dob"
  when
     $user: user(dob != null)
     user(dob not matches "(?:0[1-9]|1[012])" from $user
  then
    modify($user){setDob(null)};

I appreciate any help on this. I apologize if this is very basic but I literally just started to use Drools.



Solution 1:[1]

I'm going to assume these models (irrelevant bits omitted; assume getters and setters):

class User {
  private Name name; // Name model not shown, not relevant to this question
  private Phone phone;
  private String dob;
}
class Phone {
  private String phoneType;
  private String phoneNumber;
}

We can then write a rule like this:

rule "Invalid Phone Number"
when
  $user: User( $phoneNumbers: phone )
  exists( Phone( phoneNumber not matches "^([0-9]{3})[0-9]{3}-[0-9]{4}$" ) from $phoneNumbers)
then
  // $user has at least 1 phone number which doesn't match the pattern
end

This rule is useful when we only need to know that there exists a bad value. It gets a little more interesting if we actually need to get those bad values (eg. if we want to report out a better error message.)

If we want to trigger the consequences for each bad phone number (eg. if there are 3 phone numbers and 2 are bad, we want to trigger the "then" twice) we could do like this:

rule "Invalid Phone Number - trigger for each"
when
  $user: User( $phoneNumbers: phone )
  $badPhone: Phone( phoneNumber not matches "^([0-9]{3})[0-9]{3}-[0-9]{4}$") from $phoneNumbers
then
  // here $badPhone will be a phone number for $user that doesn't match the pattern
end

You might want to write a rule like this if you want to do something for each bad phone number individually. Consider maybe if we were doing "verified" phone numbers -- for example, they text you passcode that you have to use to verify your phone number; in this case you could text each phone number, one-by-one, to go through this verification process.

Alternatively we could just collect up all of the bad phone numbers and trigger the consequences once for all of those numbers, collectively.

rule "Invalid Phone Number - trigger once for all"
when
  $user: User( $phoneNumbers: phone )
  $badNumbers: List( size > 0 ) from collect (
      Phone( phoneNumber not matches "^([0-9]{3})[0-9]{3}-[0-9]{4}$") from $phoneNumbers
  )
then
  // $badNumbers is a list of all of the Phone objects that don't match
  // the pattern and belong to $user
end

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Roddy of the Frozen Peas