Skip to main content

Recursive Queries

Recursive queries are a way of using a query to add data back into a query. It's a tool that let's you define complex logic in a simple way.

A classic problem in computer science is figuring out if a given graph of nodes is connected or not. We say a graph is connected if you can reach every node from every other node. We'll solve this problem to demonstrate recursive queries.

Live Editor
Result
Loading...

Most of the code above is boilerplate setup code, the real meat is this query:

const Query = datalog.query(({ node, to }) => {
Nodes({ node })
Edges({ from: node, to })
})

Query.viewExt().mapEffect(({kind, datum}) => {
// Because this datalog is differential we have to handle the cases
// where datum is Added, Removed, or Modified
if (kind === datalog.Added) {
Nodes.assert({node: datum.to})
} else {
Nodes.retract({node: datum.to})
}
//...
}).onChange(() => Query.runQuery()) // Rerun the query when the inputs change

The trick is that we assert new data on the table whenever the query gives us new data. We are essentially feeding data from a query back into the table.

This pattern is common enough that we added a shortcut:

const Query = datalog.query(({ node, to }) => {
Nodes({ node })
Edges({ from: node, to })
}).implies(({ to }) => {
Nodes({ node: to })
})

Is the same thing, but much more concise. Note that the implies method is very limited, and if you do anything more complicated you will probably want the mapEffect method instead. Next we'll look at an even more complex example: Bacon Numbers!