Filters – Project Haystack


OverviewUsageArrow OperatorRef ListsGrammar


Filters are a simple declarative language used to query Haystack data. A filter is a predicate function which takes a dict and returns true/false (match/no match). Some filter constructs require the use of defs in which case the filter must be evaluated in the context of a namespace.


The simplest filter is just a tag name which matches any record which contains that tag (regardless of its value):

site  // query any record with the "site" tag

To match a tag value you can use any of the equality or comparison operators:

geoPostalCode == "23220"   // equal to
geoPostalCode != "23220"   // not equal to
curVal < 75                // less than
curVal <= 75               // less than or equal to
curVal > 75                // greater than
curVal >= 75               // greater than or equal to

The scalars to compare against are encoded using Zinc encoding (with couple exceptions noted below).

You can combine filters using and, or, or not:

site or equip             // has site or equip tag
equip and hvac            // has equip and hvac tag
equip and not ahu         // has equip tag, but not the ahu tag

Arrow Operator

You use the -> operator to dereference a tag which has a Ref value. For example if your equip rec has a siteRef tag that references the site, you can query for equip in a given city such as:

equip and siteRef->geoCity == "Chicago"

The way to read the above expression is match an entity if:

  • it has equip tag
  • and it has a siteRef tag which is a Ref
  • and what the siteRef tag points to has the geoCity tag
  • and that the site's geoCity tag is equal to "Chicago"

The arrow operator may also be used if the tag value is a nested Dict.

Ref Lists

Haystack allows a list of refs to be used for relationships where a single ref might be used. When a tag value is configured as a list of refs, then a filter will match if any one of the refs match.

Consider an example with a VAV which has an airRef configured with as list of two refs:

// records
id:@ahu1, dis:"AHU-1", ahu, equip
id:@ahu2, dis:"AHU-2", ahu, equip
id:@vav, airRef:[@ahu1, @ahu2], vav, equip

// all of these filters will match the VAV record
airRef == @ahu1
airRef == @ahu2
airRef->dis == "AHU-1"
airRef->dis == "AHU-2"

Note that pathing through a list of refs using "->" will check the path on each ref recursively.

This technique should never be used for the fundamental containment tags siteRef, equipRef, or spaceRef.


The formal grammar of the filter langauge:

<filter>     :=  <condOr>
<condOr>     :=  <condAnd> ("or" <condAnd>)*
<condAnd>    :=  <term> ("and" <term>)*
<term>       :=  <parens> | <has> | <missing> | <cmp>
<parens>     :=  "(" <filter> ")"
<has>        :=  <path>
<missing>    :=  "not" <path>
<cmp>        :=  <path> <cmpOp> <val>
<cmpOp>      :=  "==" | "!=" | "<" | "<=" | ">" | ">="
<path>       :=  <name> ("->" <name>)*

<val>        :=  <bool> | <ref> | <str> | <uri> |
                 <number> | <date> | <time>
<bool>       := "true" or "false"
<number>     := same as Zinc (keywords not supported INF, -INF, NaN)
<ref>        := same as Zinc
<symbol>     := same as Zinc
<str>        := same as Zinc
<uri>        := same as Zinc
<date>       := same as Zinc
<time>       := same as Zinc
<name>       := same as Zinc <id>

See Zinc grammar for productions reused from Zinc. Bools are encoded as "true" or "false" (Zinc encodes as "T" or "F").