-
Notifications
You must be signed in to change notification settings - Fork 0
Datomic Setup
Unfortunately, due to ambiguity caused by the inability to specify what version to use for a particular package of software, Datomic must be specified as a dependency before you startup dj. Otherwise, datomic's dependency on the guava library conflicts with the guava library pulled in from cemerick.piggieback. The current work around is to add datomic as a dependency in the database profile.
lein with-profile database repl
I recommend creating a script for this as well.
Linux/Mac:
#!/bin/sh
cd ~/dj
lein with-profile database replWindows:
@echo off
cd C:\dj
C:\Users\foo\bin\lein.bat with-profile database repl
:EOF(require '[datomic.api :as d])
(def uri "datomic:mem://test")
(def conn (d/connect uri))
(def results (q '[:find ?e :where [?e :db/doc]] (d/db conn)))To make it easier to get started, instead of querying the database, you can query your own data.
The fundamental concept for querying is the relation, an n-tuple (an ordered list of length n). Datomic databases only deal with 4-tuples, but querying supports tuples of any arity.
user=> (require '[datomic.api :as d])
nil
user=> (d/q '[:find ?e :where [?e :likes]] (list ['a :likes 'b 'd 'f]['a :hates 'c]))
#<HashSet [[a]]>
datomic.api/q accepts the query as its first argument, and the relational data as its second (its can accept more, but for now this is enough). Relational data is simply a sequence of tuples. This means you can pass lists, vectors, or anything else that is sequential.
A basic query consists of elements of :find, which are the output variables, and :where, which contains the data clauses. Data clauses are tuples that can have values or variables. The clauses must have at least two items. The more items, the more specific the clause. A query will return all combinations of output variables that are "valid" for all the data clauses.
Let's see some examples:
user=> (d/q '[:find ?e :where [?e :likes]] [['Alice :likes 'Bob]['Bob :hates 'Alice]['Charlie :likes 'Alice]])
#<HashSet [[Alice][Charlie]]>
The above query finds ?es that :likes. Since ?e is in the first position, it is cycled through all the first elements in each relation.
What if we want to ask people who are in a tragic situation, like someone who likes someone who hates them back? We can do that with this query.
'[:find ?person :where [?person :likes ?hater][?hater :hates ?person]]
Querying that would return
#<HashSet [[Alice]]>
Note that ?hater isn't returned because we didn't include it in the :find part.
Now a query for social networking sites. We want to expand people's networks by introducing new people who should be their friend. Like if at least two of someone's current friends are friends with someone else.
Let's create the network of friends first.
(def data [[Alice :knows Bob][Alice :knows Charlie][Bob :knows Dan][Charlie :knows Dan]])From this topology, we would say Alice should probably get to know Dan since Alice's friends both know Dan.
user=> (d/q '[:find ?person3 :where [Alice :knows ?person1][Alice :knows ?person2][?person1 :knows ?person3][?person2 :knows ?person3]] data)
#<HashSet [[Dan]]>
Hashmaps can be bound with
[[?k ?v]]A variable can be bound to each value in a sequence with [?v ...].
Architecture Overview Read this first.
Video that goes along with the overview
Watch this video for learning about querying
Query reference to study after watching the video
In depth tutorial This is the all-in-one reference to use. The code is unfortunately in Java and not clojure, but once you understand how to use the clojure api, you should be able to figure it out.
For a more separated listing, see the reference section