Skip to content

Pattern Condition

The pattern condition matches tokens of a certain form, which is specified by a pattern. Currently, a pattern can specify the type of the token, the types of its properties, and which properties are set (not null).

Example

Example Code

The complete, runnable code for this example can be found in the repository StepDP-Basic-Examples. The documentation only includes the most relevant code snippets.

Let's assume a dialog application where the user can ask a robot to bring something to a certain person. This is represented with the BringIntent type, which has a PhysicalObject as parameter. In this example, a PhysicalObject can be a kind of Food or Drink.

FusionTree

Using the pattern condition, one can define a rule that simply matches all tokens of type BringIntent, or more specialized rules that match a BringIntent of a certain form, e.g. one that concerns objects of type Food and one that concerns objects of type Drink. In addition, it can be specified that a rule should only trigger if a certain parameter is given, e.g. the recipient name. The following code snippets show how to define such a rule (assuming that the semantic tree is defined as in the figure above).

First, we need to define a pattern that specifies which constraints a token has to satisfy to be considered a match. These constraints can be defined for the top-level object (here: the BringIntent) and for inner objects (here: the PhysicalObject value of the "object" property). The code below specifies a pattern with the following constraints:
- the type of the token is BringIntent
- the property "recipientName" is not null
- the property "object" contains an object of type Drink
- the property "object" contains an object whose property "withIce" is not null

PatternBuilder builder = new PatternBuilder("BringIntent", this.getKB());
builder.hasNonNullProperties("recipientName")
       .addPatternForProperty("object")
            .hasType("Drink")
            .hasNonNullProperties("withIce")
       .endPropertyPattern();
Pattern p = builder.build();
After the pattern is specified we can create a rule and define its condition as a pattern condition with the given pattern:

Rule rule = new SimpleRule(tokens -> {
    IToken intent = tokens[0];
    String name = intent.getString("recipientName");
    System.out.println("Here you go! Enjoy your drink, " + name);
    intent.getIgnoreRuleTags().add("BringRule");
});
rule.setCondition(new PatternCondition(p));
rule.setName("BringDrinkRule");
rule.getTags().add("BringRule");
this.getBlackboard().addRule(rule);
The purpose of the tag "BringRule" and the reason why it is added to the IgnoreRuleTags of the token is explained in the next section.

Coming soon: Soon there will be an additional type constraint available, which does not only match tokens that have a certain type but also tokens that contain a property value of that type. Example:

PatternBuilder builder = new PatternBuilder("UserIntent", this.getKB());
builder.hasRecursiveType("PhysicalObject");
Pattern p = builder.build();
This will match a token of type UserIntent if one of its properties contains an object of type PhysicalObject. The pattern matching works recursively, so the property does not have to be a direct property of the token but could also be a property of an inner object of the token.

Rule Conflicts

If we define several rules with different patterns matching different forms of a BringIntent token, it is possible that some of these patterns overlap such that one token could match more than one rule. One could for example have one special rule for bringing drinks, one for food, and one general rule for other objects.

Example Patterns:

  1. BringIntent(object : Food)
  2. BringIntent(object : Drink)
  3. BringIntent()

Since pattern 3 matches any token of type BringIntent without any special constraints, a BringIntent token with a Food object would match both pattern 1 and pattern 3. Usually, it is desired that only the most specific of the rules is triggerd, which would be the one with pattern 1 in this case. To achieve this behavior, two things need to be considered:
(1) All the rules with patterns that match a BringIntent token should be given the same tag (e.g. "BringRule"). Each of the rules should add this tag to the IgnoreRuleTags of a token when it is triggered. This ensures that only the first rule that matches the token is triggered.
(2) Since only the first rule is triggered, the order in which the blackboard iterates over the rules matters here. This order is determined by the priorities of the rules and in case of equal priority by the order in which the rules are added to the blackboard. Therefore, the easiest way to ensure that the desired rule is triggered is adding the rules to the blackboard in the corresponding order, usually from most specific to least specific.

Correct order:

  1. BringIntent(object : Food)
  2. BringIntent(object : Drink)
  3. BringIntent()

Here, the rule corresponding to pattern 3 will only trigger if 1 and 2 do not match, so only for BringIntent tokens with neither a food nor a drink object.

Incorrect order:

  1. BringIntent()
  2. BringIntent(object : Food)
  3. BringIntent(object : Drink)

Here, the specific rules for food and drink would never be triggered because pattern 1 matches all BringIntent tokens.