trydatomic

Chapters

Anatomy of a Query

You have already run a query. Before writing more of them, let's name their parts, so you can read any query and reason about it. This is the query from the first page, with each part labelled:

[:find ?name ;; what to return :where ;; what the data must match [?entity :pokemon/type "Grass"] ;; pattern 1 [?entity :pokemon/name ?name]] ;; pattern 2
  • :find says what to return. Here, one value per result: ?name.
  • :where lists the data patterns the database has to match.
  • A data pattern has the shape [entity attribute value], one fact about one entity, like the :pokemon/name of Bulbasaur in the previous chapter. Each slot holds either a fixed value, like :pokemon/type or "Grass", or a variable.
  • A logic variable starts with ?. It is a name for whatever fits, and it has to mean the same thing everywhere it appears in the query.

Read it aloud

Read a query as a sentence: "Find the ?name of every ?entity that has the type Grass and that has the name ?name." The first pattern picks the Grass entities, and the second reads their names.

Query
[:find ?name :where [?entity :pokemon/type "Grass"] [?entity :pokemon/name ?name]]

Sharing a variable is a join

Both patterns above use ?entity, so they describe the same entity. Datomic finds the entities that match the first pattern and keeps the ones that also match the second. If you know SQL, this is what JOIN ... ON does, and here the shared variable is all it takes.

Let's use it to find the Pokemon that are both Fire and Flying. Two patterns on :pokemon/type, one variable:

Query
[:find ?name :where [?e :pokemon/type "Fire"] [?e :pokemon/type "Flying"] [?e :pokemon/name ?name]]

Now give the second pattern a variable of its own. ?flyer is not ?fire, so the two patterns are unrelated. The second one only asks whether some Flying Pokemon exists, and one does, so it filters nothing. Every Fire Pokemon comes back:

Query
[:find ?name :where [?fire :pokemon/type "Fire"] [?flyer :pokemon/type "Flying"] [?fire :pokemon/name ?name]]

If two patterns are about the same thing, they have to share a variable.

Two more things to know

  • The patterns in :where are combined with AND. A result has to satisfy every one of them.
  • The result is a collection of tuples with no duplicates and no promised order. Two Pokemon that share the values you return give one row, not two. That matters when we count in the Aggregations chapter.
TRY

Find the Pokemon that are both Water and Ice. Then rename the variable in one of the type patterns, and predict how the result changes before you run it.

You can now
  • Name every part of a query: :find, :where, data patterns, and logic variables.
  • Explain why sharing a variable between two patterns performs a join.
  • Know that :where clauses are combined with AND, and that results are an unordered, deduplicated set of tuples.