trydatomic

Chapters

Input Parameters

Every query so far has had its filter values written directly into the query. The :in clause lets you declare named inputs, so the same query can be run with different values, exactly like function parameters.

Every one of those earlier queries actually had an implicit :in $: the database itself, conventionally named $. When you write :in yourself, you list $ explicitly, followed by your own inputs, in the same order the query expects them. The site passes $ for you either way. In Clojure you would call a query with one extra input like this:

(d/q '[:find ?name :in $ ?type :where [?e :pokemon/name ?name] [?e :pokemon/type ?type]] db "Fire")

In the editors below, the inputs are written after the query, one per line, in the same order as :in (after $). You can edit them and run the query again.

Single scalar

A bare variable in :in binds a single value. This is the most common form. Here ?type is bound to "Fire":

Query
[:find ?name :in $ ?type :where [?e :pokemon/name ?name] [?e :pokemon/type ?type]] ;; inputs, in the order of :in (after $) "Fire"

You can declare as many inputs as you need. Pass one value for each, in order. If a value is missing, Datomic tells you how many inputs it expected. That count includes $, so a query with one input of its own expects 2.

Query
[:find ?name ?speed :in $ ?type ?min-speed :where [?e :pokemon/name ?name] [?e :pokemon/type ?type] [?e :stat/speed ?speed] [(>= ?speed ?min-speed)]] ;; inputs, in the order of :in (after $) "Fire" 100

Collection binding

Wrap the variable in [?var ...] to accept a collection of values. The query matches any entity whose attribute value appears in the collection, like SQL's IN (...):

Query
[:find ?name ?type :in $ [?type ...] :where [?e :pokemon/name ?name] [?e :pokemon/type ?type]] ;; inputs, in the order of :in (after $) ["Ghost" "Dragon"]

Tuple binding

Wrap multiple variables in [?a ?b] to destructure a single input tuple. Useful for passing a pair of related values, like a range:

Query
[:find ?name ?speed :in $ [?min-speed ?max-speed] :where [?e :pokemon/name ?name] [?e :stat/speed ?speed] [(>= ?speed ?min-speed)] [(<= ?speed ?max-speed)]] ;; inputs, in the order of :in (after $) [80 100]

Relation binding

A relation binding accepts a collection of tuples, like a small in-memory table. Each tuple is destructured into the declared variables. Here every row pairs a Pokemon's name with the least HP we want to see for it:

Query
[:find ?name ?hp :in $ [[?name ?min-hp]] :where [?e :pokemon/name ?name] [?e :stat/hp ?hp] [(>= ?hp ?min-hp)]] ;; inputs, in the order of :in (after $) [["Pikachu" 30] ["Charizard" 70]]

Each row in the input relation acts like a separate filter applied in parallel, then the results are combined.

TRY

Change the range in the tuple example to [50 70] to see which Pokemon fall in that band. Or add another type, like "Fire", to the collection example and run it again.

You can now
  • Parameterize a query with :in instead of hard-coding a value into it.
  • Bind a collection, a tuple, or a relation of inputs, not just a single scalar.
  • Know that every query has an implicit :in $, and that any input you add comes after it, in order.