Looking at this query:
[:find ?title
:where
[?p :person/name "Sylvester Stallone"]
[?m :movie/cast ?p]
[?m :movie/title ?title]]
It would be great if we could reuse this query to find movie titles for any actor and not just for "Sylvester Stallone". This is possible with an :in
clause, which provides the query with input parameters, much in the same way that function or method arguments does in your programming language.
Here's that query with an input parameter for the actor:
[:find ?title
:in $ ?name
:where
[?p :person/name ?name]
[?m :movie/cast ?p]
[?m :movie/title ?title]]
This query takes two arguments: $
is the database itself (implicit, if no :in
clause is specified) and ?name
which presumably will be the name of some actor.
The above query is executed like (q query db "Sylvester Stallone")
, where query
is the query we just saw, and db
is a database value. You can have any number of inputs to a query.
In the above query, the input pattern variable ?name
is bound to a scalar - a string in this case. There are four different kinds of input: scalars, tuples, collections and relations.
Hold on. Where does that $
get used? In query, each of these data patterns is actually a 5 tuple, of the form:
[<database> <entity-id> <attribute> <value> <transaction-id>]
It's just that the database
part is implicit, much like the first parameter in the :in
clause. This query is functionally identical to the previous one:
[:find ?title
:in $ ?name
:where
[$ ?p :person/name ?name]
[$ ?m :movie/cast ?p]
[$ ?m :movie/title ?title]]
A tuple input is written as e.g. [?name ?age]
and can be used when you want to destructure an input. Let's say you have the vector ["James Cameron" "Arnold Schwarzenegger"]
and you want to use this as input to find all movies where these two people collaborated:
[:find ?title
:in $ [?director ?actor]
:where
[?d :person/name ?director]
[?a :person/name ?actor]
[?m :movie/director ?d]
[?m :movie/cast ?a]
[?m :movie/title ?title]]
Of course, in this case, you could just as well use two distinct inputs instead:
:in $ ?director ?actor
You can use collection destructuring to implement a kind of logical or in your query. Say you want to find all movies directed by either James Cameron or Ridley Scott:
[:find ?title
:in $ [?director ...]
:where
[?p :person/name ?director]
[?m :movie/director ?p]
[?m :movie/title ?title]]
Here, the ?director
pattern variable is initially bound to both "James Cameron" and "Ridley Scott". Note that the ellipsis following ?director
is a literal, not elided code.
Relations - a set of tuples - are the most interesting and powerful of input types, since you can join external relations with the datoms in your database.
As a simple example, let's consider a relation with tuples [movie-title box-office-earnings]
:
[
...
["Die Hard" 140700000]
["Alien" 104931801]
["Lethal Weapon" 120207127]
["Commando" 57491000]
...
]
Let's use this data and the data in our database to find box office earnings for a particular director:
[:find ?title ?box-office
:in $ ?director [[?title ?box-office]]
:where
[?p :person/name ?director]
[?m :movie/director ?p]
[?m :movie/title ?title]]
Note that the ?box-office
pattern variable does not appear in any of the data patterns in the :where
clause.
Given a list of movie titles, find the title and the year that movie was released.
Query:[ I give up! ]
Input #1:
Find all movie ?title
s where the ?actor
and the ?director
has worked together
Write a query that, given an actor name and a relation with movie-title/rating, finds the movie titles and corresponding rating for which that actor was a cast member.