Query middleware

Transform changes on their way into a query.

Middleware runs over a source’s changes before the query sees them, so a query can match on data that the source does not emit in the shape the query needs.

Declaring middleware and applying it are two steps. A declaration names an instance; a source’s pipeline decides where it runs. A declaration nothing refers to does nothing.

await drasi.add_query(
    "orders-by-city",
    "MATCH (o:Order) RETURN o.id AS id, o.city AS city",
    [{"id": "orders", "pipeline": ["flatten"]}],
    middleware=[
        {
            "name": "flatten",
            "kind": "promote",
            "config": {"mappings": [{"path": "$.address.city", "target_name": "city"}]},
        }
    ],
)

The source pushes {"address": {"city": "Cambridge"}}, and the query selects o.city. Without the pipeline that column is None, because city is nested rather than a property of the node. promote lifts it, and the query resolves it.

A source that needs no middleware stays a plain string, and the two forms can be mixed:

await drasi.add_query(
    "orders-by-city",
    QUERY,
    ["payments", {"id": "orders", "pipeline": ["flatten"]}],
    middleware=[...],
)

Available kinds

kind selects the middleware type. These are compiled into the wheel:

KindWhat it does
promoteLifts a nested value to a top-level property, selected by JSONPath
unwindExpands an array property into separate elements
mapRewrites a change into one or more different changes
parse_jsonParses a JSON string property into structured data
decoderDecodes an encoded property, such as base64
relabelRenames the labels a change carries

Each kind reads its own config, which is passed through untouched, so what the keys mean is that middleware’s business.

Declarative topologies

from_config takes the same two keys on a query entry:

drasi = await Drasi.from_config(
    {
        "id": "app",
        "queries": [
            {
                "id": "orders-by-city",
                "query": "MATCH (o:Order) RETURN o.id AS id, o.city AS city",
                "sources": [{"id": "orders", "pipeline": ["flatten"]}],
                "middleware": [
                    {
                        "name": "flatten",
                        "kind": "promote",
                        "config": {"mappings": [{"path": "$.address.city", "target_name": "city"}]},
                    }
                ],
            }
        ],
    }
)

Failures

A middleware problem stops the query being registered rather than degrading it, so these raise:

  • a kind no middleware is registered for
  • a pipeline naming a middleware that was not declared
  • a declaration missing name or kind
  • a config the middleware itself rejects

Next