A query contains a list of query templates. Each one is a function taking a target object and returning a template object. Create a dummy target object and invoke the function. Then traverse the tree of fields looking for the dummy target object.
A query is compiled into a list of joins. Each join has a direction (predecessor or successor), a role name, and an optional condition. When a query is executed against a message, each join instructs the execution engine to walk to a related set of messages. A predecessor join gets the set of all values (single values or arrays) of fields, by the role name. A successor join gets the set of all messages having a field of the role name containing the source message.
A condition is a set of clauses. Each one checks for the existence or absence of a message matching a query. Queries are executed recursively.
Alternatively, a condition clause may check for the value of a field. It filters the resulting messages to those having the required field matching the expected value.
When a template contains a field having a primitive value, that field and value are converted into a condition clause.
When a template contains a field having an object, then a successor join is pushed to a stack. The value is evaluated as another template. Joins are appended to the list as they are popped off the stack (i.e. in reverse order).
When a template contains a dummy target, the fields of that target are converted into predecessor joins. these are appended to the list in order. Then the stack of successor joins is popped and added to the list until empty.
Each successive query template in the array is processed in the same way. When finished, the join list represents a path that finds messages matching the query.
A query contains a list of query templates. Each one is a function taking a target object and returning a template object. Create a dummy target object and invoke the function. Then traverse the tree of fields looking for the dummy target object.
A query is compiled into a list of joins. Each join has a direction (predecessor or successor), a role name, and an optional condition. When a query is executed against a message, each join instructs the execution engine to walk to a related set of messages. A predecessor join gets the set of all values (single values or arrays) of fields, by the role name. A successor join gets the set of all messages having a field of the role name containing the source message.
A condition is a set of clauses. Each one checks for the existence or absence of a message matching a query. Queries are executed recursively.
Alternatively, a condition clause may check for the value of a field. It filters the resulting messages to those having the required field matching the expected value.
When a template contains a field having a primitive value, that field and value are converted into a condition clause.
When a template contains a field having an object, then a successor join is pushed to a stack. The value is evaluated as another template. Joins are appended to the list as they are popped off the stack (i.e. in reverse order).
When a template contains a dummy target, the fields of that target are converted into predecessor joins. these are appended to the list in order. Then the stack of successor joins is popped and added to the list until empty.
Each successive query template in the array is processed in the same way. When finished, the join list represents a path that finds messages matching the query.