Reputation: 121
I am working with clojure's datomic.api. I wish to refactor a somewhat complex datalog query such as this:
(datomic.api/q '[:find [?value ...] :in $ ?uid ?component :where
[...some clause...]
[...some other clause...]
(or-join [?entitlement ?component]
(and [...some conditional stuff...])
(and [...some other conditional stuff...]))]
db uid component)
...into something more readable. My desire is to locally bind the (and...)
components of the query inside a let
and refer to them by a name inside of the datalog list. Like so:
(datomic.api/q '[:find [?value ...] :in $ ?uid ?component :where
[...some clause...]
[...some other clause...]
(or-join [?entitlement ?component]
entitled-for-component
entitled-for-application)]
db uid component)
Various quoting in let
(and unquoting inside of datomic.api/q's list) have not worked. Any suggestions?
Upvotes: 2
Views: 314
Reputation: 121
Datomic uses rules to solve this problem.
Datomic datalog allows you to package up sets of :where clauses into named rules. These rules make query logic reusable, and also composable, meaning that you can bind portions of a query's logic at query time.
Rulesets are defined as a list of lists, then used as an additional input with datomic.api/q bound to the % character.
(def rules [[(name-for-id restaurant-id?)
[restaurant-id? :restaurant/name name?]]])
(datomic.api/q '[:find ?name . :in $ % ?restaurant-id :where
(name-for-id restaurant-id?)] db rules 42)
=> "Milliways"
Note that datomic.api/q
expects a rule set, passing a single rule will not work.
The first list in a rule defines the rule name as the first item, followed by one or more parameters. The subsequent vectors contain one or more :where clauses.
Also,
As with other where clauses, you may specify a database before the rule-name to scope the rule to that database. Databases cannot be used as arguments in a rule.
Upvotes: 2