4.3. Matchers

Matchers allow a user to match an event to a set of key-value expressions.

{
  "burgers-nearby": {
    "name": "burger$",
    "rssi": ">-50"
  }
}

The matcher above will match an event that satisfies all of the following criteria:

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

4.3.1. Defining Matcher Keys

Matcher keys can be 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",
        "isForMillenials": true
    }
}

4.3.2. 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)

Existence

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

To check that key cheese exists:

{
  "has-cheese": {
    "cheese": ""
   }
}

Special Considerations

  • If you wish to match an empty string use "^$".

Numeric Values

For numeric values the following operators are supported:

Comparison Operator
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-1.23": {
     "someKey": ">1.23"
   }
}

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 false
“true” “false”
“True” “False”
“1” “0”
“t” “f”
“T” “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

  • Use ^ and $ to explicitly define the start/end of the string to avoid unexpected matches.

Bytearrays

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

bytearray regular expression
[0x12,0xff,0xaa] ^12ff0a$
[0x99, … ,0x5a] ^99([0-9a-f]{2})*5a$

Special Considerations

  • Use ^ and $ to explicitly define the start/end of the bytearray to avoid unexpected matches.
  • When matching ble.UUID do not include dashes in the value.

Array Handling

There is limited support for array handling.

[]byte

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

[]interface{} & []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
    ]
}

4.3.3. Special Considerations

Dynamic Type Handling

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

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

Consider this example where we are expecting a boolean value:

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

"is-true" could match either 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, so entering a regular expression without defining the start/end of the string will result in most likely unexpected behavior.

Consider this example where we are trying 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”

4.3.4. 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 device group 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 allow processing of only events that match all the provided matchers in a Pipeline.

Below we have 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": [
                "..."
            ]
        }
    }
}