Matchers

Matchers provide a way to match an event to a set of criteria:

The matching criteria may be defined via:

  • A set of key-value expressions
  • A template that evaluates to a true/false equivalent
{
  "burgers-nearby": {
    "name": "burger$",
    "rssi": ">-50"
  },
  "burgers-nearby-template": "{{ and (mustRegexMatch \"burger$\" .name) (gt .rssi -50) }}"
}

The burgers-nearby matcher is defined with a set of key-value expressions, while the burgers-nearby-template matcher is defined with a template.

Either matcher will match an event with following criteria:

  • Contains key rssi with a value greater than -50
  • Contains key name with a value ending in burger

Defining Matchers

Matcher Templates

A matcher template is a text template that that evaluates to a true/false equivalent value (1/t/T/true/True or 0/f/F/false/False).

All template functions in Edge Connect are available for use in the matcher template.

If no true/false equivalent value is built by the template, an error is returned and the the event is not matched.

Matcher Key-Value Expressions

Defining Matcher Keys

Matcher keys are defined with dot notation and/or templating.

Dot Notation

This matcher uses dot notation

{
  "is-gouda-burger": {
    "burger.cheese":"^gouda$"
   }
}

to match this event

{
    "burger": {
        "cheese": "gouda",
        "patty": "beef, 80% lean"
    }
}
Templating

This matcher uses templating and dot notation in the key

{
  "is-food-with-cheese": {
    "{{ .foodType }}.cheese": ""
   }
}

to match this event

{
    "foodType": "burrito"
    "burrito": {
        "cheese": "gouda",
        "meat": "carnitas"
    }
}

Matcher Values

All Matcher values must be a string. No other input types are valid.

The following value types supported by Matchers:

  • Existence
  • Numeric (all int/uint/float types, byte)
  • String
  • Bytearrays/ble.UUID
  • Boolean
  • Arrays (limited support for []interface{}, []ble.UUID])

Text templating in the value is supported. For example, to match someKey greater than someDynamicValue in the input event:

{
   "greater-than-100": {
     "someKey": ">{{.someDynamicValue}}"
   }
}
Existence

To check for existence of a key, use the empty string ("") for the value.

To check if the key cheese exists:

{
  "has-cheese": {
    "cheese": ""
   }
}
Special Considerations
  • To match an empty string, use the regular expression ["^$"] where ^ marks the beginning of a string and $ marks the end.
Numeric Values

For numeric values the following operators are supported:

ComparisonOperator
Value less than[<]
Value greater than[>]
Value less than or equal[<=]
Value greater than or equal[>=]
Value equal[==]
Value not equal[!=]

For example, to match someKey greater than 100:

{
   "greater-than-100": {
     "someKey": ">100"
   }
}
Special Considerations
  • All comparisons are handled as float64 regardless of the underlying value type.
  • The epsilon value is 1e-8 (0.00000001) for equality.
Boolean Values

For bool values, use any of the following strings to match the desired bool value:

  • true: "true", "True", "1", "t", "T"
  • false: "false", "false", "0", "f", "F"
Strings

To match a string use a regular expression.

If the regular expression fails to compile, the matcher will fallback to a string equality comparison.

For example, this matcher:

{
  "is-typical-story-with-sandwiches" : {
      "story": "^Once upon a time.*[Ss]andwich"
  }
}

Would match this event:

{
    "story": "Once upon a time, there was a ham sandwich. The end."
}
Special Considerations
  • To avoid unexpected matches, use ^ and $ to mark the start/end of the string.
Bytearrays

To match a bytearray, use a lowercase hexstring regular expression. This also applies to ble.UUID (used for BLE services)

bytearrayregular expression
[0x12,0xff,0xaa][^12ff0a$]
[0x99, ... ,0x5a][^99([0-9a-f]{2})*5a$]
Special Considerations
  • To avoid unexpected matches, use ^ and $ to mark the start/end of the bytearray.
  • When matching ble.UUID, do not include dashes in the value.
Array Handling

There is limited support for array handling.

[]byte

[]byte (Byte arrays) are fully supported as seen in Bytearrays.

[]interface{} and []ble.UUID

There is limited support for []interface{} and []ble.UUID (ble services).

If event value is of type []interface{} or []ble.UUID and ANY item in the array matches the provided matcher value, it is considered a match.

For example, this matcher

{
  "is-over-9000" : {
      "power-level": ">9000"
   }
}

would match this event, despite the two other values in the array failing to match the given value.

{
    "power-level": [
        123.4,
        "negative one million",
        9000.1
    ]
}

Special Considerations

Dynamic Type Handling

Since input types and comparisons are handled dynamically, false matches may occur if unexpected types are in the input event.

The following example applies to boolean types, but this could happen with any type in a similar manner.

Consider this example where we are expecting a boolean value:

{
  "is-true":{
      "aBoolean": "True"
  }
}

"is-true" matches both of these events:

  • event with a bool (as expected):

    {
        "aBoolean": true
    }
    
  • event with a string (unexpected):

    {
        "aBoolean": "Arnold's best movie is True Lies, not Terminator 2"
    }
    
Regular Expressions

Keep in mind that regular expressions are used by default. Entering a regular expression without defining the start/end of the string will likely result unexpected behavior.

Consider this example that tries to match an event that has {"value" : "hamburger"}:

{
  "is-hamburger-bad":{
  "value": "hamburger"

  },
  "is-hamburger-good":{
      "value": "^hamburger$"
  }
}
  • is-hamburger-bad has no explicit start/end for the regular expression
  • is-hamburger-good has an explicit start/end for the regular expression

Given these events:

{
 "event1" : {
     "value": "hamburger"
 },
 "event2": {
     "value": "hamburger helper"
 }
}
  • "event1" will be matched by both "is-hamburger-good" and "is-hamburger-bad"
  • "event2" will be matched by "is-hamburger-bad" since the string starts with "hamburger"

Using Matchers

Matchers can be used in the following places:

  • Matcher filter
  • Pipeline matchers
  • DeviceGroup inclusion matchers

Regardless of the place, the configuration of Matchers is the performed with the following keys.

  • matchers defines a list of top-level matchers
  • match defines an inline matcher

All provided Matchers must match the input event to be considered a match.

Matcher Filters

A Matcher filter allows a user to insert matchers anywhere in a pipeline/connection sequence.

In the following example we use matchers to send events to the cloud only if the device's power-level is over 9000.

{
    "matchers": {
        "is-over-9000": {
            "power-level": ">9000"
        }
    },
    "filters" : {
        "convert-power-level": {
            "type": "typeConvert",
            "config" : {
                "injectKeys": {
                    "power-level": {
                        "source": "mfg"
                        "offset": 2,
                        "length": 4,
                        "type": "uintLE"
                    }
                }
            }
        },
        "match-over-9000" : {
            "type": "matcher",
            "config" : {
                "matchers": [
                    "is-over-9000"
                ]
            }
        },
        "publish": {
            "type": "publish",
            "config": {
                "connector": "cloud9000"
            }
        },
    }
    "pipelines" : {
        "publish-over-9000": {
            "filters": [
                "convert-power-level",
                "match-over-9000",
                "publish"
            ]
        }
    }
}

DeviceGroup Inclusion Matchers

Setting a DeviceGroup inclusion matcher allows dynamically adding devices to a DeviceGroup.

For example, the snippet below would automatically add new devices to the nearby-tacos DeviceGroup as they are discovered.

{
    "matchers": {
        "is-nearby" : {
            "rssi": ">-50"
        },
        "is-taco": {
            "name": "^taco$"
        }
    },
    "groups" : {
        "nearby-tacos" : {
            "matchers": [
                "is-nearby",
                "is-taco"
            ]
        }
    }
}

Pipeline Matchers

Setting matchers at the Pipeline level allows processing of only those events which match all the provided matchers in a Pipeline.

Below is a mix of inline and top-level matchers:

{
    "matchers": {
        "is-nearby" : {
            "rssi": ">-50"
        },
        "is-taco": {
            "name": "^taco$"
        }
    },
    "pipelines" : {
        "nearby-hamburgers" : {
            "matchers": [
                "is-nearby"
            ],
            "match": {
                "name": "^hamburger$"
            },
            "filters": [
                "..."
            ]
        },
        "nearby-tacos" : {
            "matchers": [
                "is-nearby",
                "is-taco"
            ],
            "filters": [
                "..."
            ]
        }
    }
}