Skip to main content

Clojure

Fluree has a Clojure client.

A note on terminology#

If you are familiar with Datomic, Datahike, Datascript, or Crux (or other Clojure triple-store+ databases) you may wonder at the difference in terminology between those databases and Fluree. Fluree uses RDF terminology to describe the relationships between entities, or subjects.

Here's a quick glossary of how your knowledge may map on to Fluree's terminology:

Clojure Triple-Store+ termFluree term
entitysubject
attributepredicate
valueobject
datomflake

Setup#

Make sure you have a running Fluree ledger service to connect to. See the Getting Started docs for more details.

Add to the artifact to your dependencies:

Leiningen -

[com.fluree/db "1.0.0-rc22"]

deps.edn -

com.fluree/db {:mvn/version "1.0.0-rc22"}

Require the api and establish a connection to your Fluree server:

(require '[fluree.db.api :as fdb])
(def conn (fdb/connect "http://localhost:8090"))
(def ledger "example/test")
@(fdb/new-ledger conn ledger)

Transacting#

transact is used to make any changes to the ledger, either adding data or schema. It's all data, and it all uses the same api to effect changes.

Schema#

All data transacted into a ledger must conform to a schema. We'll transact a basic schema below. See the Schema documentation for more details.

Create a collection, which you can think of as a type of entity, or a relational database table.

;; creates book and author collections@(fdb/transact conn ledger [{:_id :_collection :_collection/name :book}                            {:_id :_collection :_collection/name :author}])
;; can also use strings like in the Fluree docs:;; {"_id" "_collection" "_collection/name" "author"}

Create some predicates for the collection, analogous to entity attributes or relational database columns.

@(fdb/transact conn ledger [{:_id :_predicate                             :_predicate/name :author/name                             :_predicate/doc "An author's complete name."                             :_predicate/unique true                             :_predicate/type :string                             :_predicate/upsert true}
                            {:_id :_predicate                             :_predicate/name :book/title                             :_predicate/doc "A book's title."                             :_predicate/unique true                             :_predicate/upsert true                             :_predicate/fullText true                             :_predicate/type :string}                            {:_id :_predicate                             :_predicate/name :book/notes                             :_predicate/doc "Any notes about the book"                             :_predicate/type :string}                            {:_id :_predicate                             :_predicate/name :book/authors                             :_predicate/doc "A book's authors."                             :_predicate/type :ref                             :_predicate/multi true                             :_predicate/restrictCollection :author}                            {:_id :_predicate                             :_predicate/name :book/tags                             :_predicate/doc "Tags for categorizing books."                             :_predicate/type :tag                             :_predicate/multi true}])

Data#

Insert some subject data that conforms to the schema we've created. See the Transact documentation for more details.

;; adding data@(fdb/transact conn ledger [{:_id :book                             :book/title "Watership Down"                             :book/tags [:rabbits :fiction]                             :book/authors [{:_id :author :name "Richard Adams"}]}                            {:_id :book                             :book/title "The Joy of Clojure"                             :book/tags [:programming :non-fiction]                             :book/authors [{:_id :author :name "Michael Fogus"}                                       {:_id :author :name "Chris Houser"}]}                            {:_id :book                             :book/title "Cryptonomicon"                             :book/tags [:fiction :sci-fi]                             :book/authors [{:_id :author :name "Neal Stephenson"}]}])
@(fdb/transact conn ledger [{:_id [:book/title "Cryptonomicon"]                             :book/notes "I rly liked it."}])
;; updating a single-cardinality predicate@(fdb/transact conn ledger [{:_id [:book/title "Cryptonomicon"]                             :book/notes "I really liked it."}])
;; updating a multi-cardinality predicate@(fdb/transact conn ledger [{:_id [:book/title "The Joy of Clojure"]                             :book/title "The Joy of Clojure"                             :book/tags [:favorite]}])  ; adding the :favorite tag
;; retracting a predicate@(fdb/transact conn ledger [{:_id [:book/title "Cryptonomicon"] :book/notes nil}])
;; retracting multi-cardinality member@(fdb/transact conn ledger [{:_id [:book/title "The Joy of Clojure"]                             :_action :delete                             :book/tags [:favorite]}]) ; only removing :favorite tag
;; retracting a whole subject@(fdb/transact conn ledger [{:_id [:book/title "Watership Down"] :_action :delete}])

Querying#

Analytical queries#

The syntax for these queries is similar to datalog and eql. These don't support keywords for matching on flake values in the :where clause.

The query function takes a db value to execute a query against, as well as a query map. See the docs for Analytical queries and Basic queries for more details.

;; store the current immutable database value. See API docs for getting a prior version of a db, applying permissions to a db, and other options.(def db (fdb/db conn ledger))
;; all books@(fdb/query (fdb/db conn ledger) {:select {"?book" [:*, {:book/authors [:*]}]}                              :where [["?book" "book/title" nil]]})
;; all non-fiction books@(fdb/query (fdb/db conn ledger) {:select "?title"                              :where [["?book" "book/title" "?title"]                                      ["?book" "book/tags" "non-fiction"]]})

History queries#

History queries can show you the raw history of a subject. Use :pretty-print to get back maps of predicate names to object values instead of raw flake data.

;; see the history of the "Cryptonomicon" book.@(fdb/history-query (fdb/db conn ledger) {:history ["book/title" "Cryptonomicon"] :pretty-print true})

Block queries#

Block queries return all of the raw flake data stored in a block. Use :pretty-print to get back maps of predicate names to object values instead of raw flake data.

;; see all the flakes from block 3@(fdb/block_query-async conn ledger {:block 3 :pretty-print true})

Other query languages#

In addition to the FlureeQL syntax used above, Fluree also supports queries in other query languages: SQL, SPARQL, GraphQL. These can be used from Clojure by supplying a database value and a query string, or, in the case of GraphQL, a query map.

@(fdb/sql (fdb/db conn ledger) "select * from book")
@(fdb/sparql (fdb/db conn ledger) "SELECT ?title WHERE { ?book fd:book/title ?title; fd:book/tags \"sci-fi\".}")
@(fdb/graphql conn ledger {:query "{graph { book { _id title}}}"} )

Async API#

All of the interactions with the Fluree ledger server are performed asynchronously, returning a promise. If you prefer to work with core.async channels instead of promises, there are -async variants of all the functions mentioned here that will return a core.async channel which will receive the result when the operation has finished.

(require '[clojure.core.async :as async])
(async/<!! (fdb/query-async (fdb/db conn ledger) {:select ["*"] :from "book"}))