{:crux.db/id :dbpedia.resource/Pablo-Picasso
:name "Pablo"
:last-name "Picasso"}
There are four transaction (write) operations:
Operation | Purpose |
---|---|
| Write a version of a document |
| Deletes the specific document at a given |
| Check the document state against the given document |
| Evicts a document entirely, including all historical versions |
A document looks like this:
{:crux.db/id :dbpedia.resource/Pablo-Picasso
:name "Pablo"
:last-name "Picasso"}
In practice when using Crux, one calls crux.db/submit-tx
with a
sequence of transaction operations:
[[:crux.tx/put
{:crux.db/id :dbpedia.resource/Pablo-Picasso
:name "Pablo"
:last-name "Picasso"}
#inst "2018-05-18T09:20:27.966-00:00"]]
If the transaction contains pre-conditions, all pre-conditions must pass or the entire transaction is aborted. This happens at the query node during indexing, and not when submitting the transaction.
For operations containing documents, the id and the document are
hashed, and the operation and hash is submitted to the tx-topic
in
the event log. The document itself is submitted to the doc-topic
,
using its content hash as key. In Kafka, the doc-topic
is compacted,
which enables later deletion of documents.
The following types of :crux.db/id
are allowed:
Keyword (e.g. {:crux.db/id :my-id}
or {:crux.db/id :dbpedia.resource/Pablo-Picasso}
)
String (e.g. {:crux.db/id "my-id"}
)
Integers/Longs (e.g. {:crux.db/id 42}
)
UUID (e.g. {:crux.db/id #uuid "6f0232d0-f3f9-4020-a75f-17b067f41203"}
)
URI (e.g. {:crux.db/id #crux/id "mailto:crux@juxt.pro"}
)
URL (e.g. {:crux.db/id #crux/id "https://github.com/juxt/crux"}
), including http
, https
, ftp
and file
protocols
Maps (e.g. {:crux.db/id {:this :id-field}}
) (Note: see issue #362).
The #crux/id
reader literal will take URI/URL strings and attempt to coerce them into valid IDs.
URIs and URLs are interpreted using Java classes (java.net.URI and java.net.URL respectively) and therefore you can also use these directly.
Put’s a document into Crux. If a document already exists with the
given :crux.db/id
, a new version of this document will be created at
the supplied valid time
.
[:crux.tx/put
{:crux.db/id :dbpedia.resource/Pablo-Picasso :first-name :Pablo} (1)
#inst "2018-05-18T09:20:27.966-00:00"] (2)
1 | The document itself. Note that the ID must be included as part of the document. |
2 | valid time |
Note that valid time
is optional and defaults to transaction time,
which is taken from the Kafka log.
Crux puts
into the past at a single point by default, so to overwrite several
versions across a range in valid time, you can either submit a transaction
containing several operations or supply a third argument to specify an end
valid time
. This period is inclusive-exclusive, such that the start of the
validity period is included in the validity range, while the end is excluded.
Deletes a document at a given valid time
.
Historical versions of the document will still be available.
[:crux.tx/delete :dbpedia.resource/Pablo-Picasso
#inst "2018-05-18T09:21:52.151-00:00"]
Match operations check the current state of an entity - if the entity doesn’t match the provided doc, the transaction will not continue.
You can also pass nil
to check that the entity doesn’t exist prior to your transaction.
[:crux.tx/match
:ivan (1)
{..} (2)
#inst "2018-05-18T09:21:31.846-00:00"] (3)
1 | Entity id |
2 | Document (or nil) |
3 | (optional) valid time |
Evicts a document from Crux. Historical versions of the document will no longer be available.
[:crux.tx/evict :dbpedia.resource/Pablo-Picasso]
Transaction functions are user-supplied functions that run on the individual Crux nodes when a transaction is being ingested.
They can take any number of parameters, and return normal transaction operations which are then indexed as above.
If they return false
or throw an exception, the whole transaction will roll back.
Transaction functions can be used, for example, to safely check the current database state before applying a transaction, for integrity checks, or to patch an entity.
Transaction functions are created/updated by submitting a document to Crux with a crux.db/fn
key.
These functions are passed a 'context' parameter, which can be used to obtain a database value using db
or open-db
.
(crux/submit-tx node [[:crux.tx/put {:crux.db/id :increment-age
;; note that the function body is quoted.
:crux.db/fn '(fn [ctx eid]
(let [db (crux.api/db ctx)
entity (crux.api/entity db eid)]
[[:crux.tx/put (update entity :age inc)]]))}]])
You can then invoke these transaction functions by submitting a :crux.tx/fn
operation:
(crux/submit-tx node [[:crux.tx/put {:crux.db/id :ivan, :age 40}]])
(crux/submit-tx node [[:crux.tx/fn :increment-age :ivan]])
;; once those transactions have been indexed
(crux/entity (crux/db node) :ivan)
;; => {:crux.db/id :ivan, :age 41}
You can subscribe to Crux events using the (crux.api/listen node event-opts f)
function.
Currently we expose one event type, :crux/indexed-tx
, called when Crux indexes a transaction.
(require '[crux.api :as crux])
(crux/listen node {:crux/event-type :crux/indexed-tx, :with-tx-ops? true}
(fn [ev]
(println "event received!")
(clojure.pprint/pprint ev)))
(crux/submit-tx node [[:crux.tx/put {:crux.db/id :ivan, :name "Ivan"}]])
prints:
event received!
{:crux/event-type :crux/indexed-tx,
:crux.tx/tx-id ...,
:crux.tx/tx-time #inst "...",
:committed? true,
:crux/tx-ops [[:crux.tx/put {:crux.db/id :ivan, :name "Ivan"}]]}
You can .close
the return value from (crux.api/listen …)
to detach the listener, should you need to.
You can submit speculative transactions to Crux, to see what the results of your queries would be if a new transaction were to be applied. This is particularly useful for forecasting/projections or further integrity checks, without persisting the changes or affecting other users of the database.
You’ll receive a new database value, against which you can make queries and entity requests as you would any normal database value. Only you will see the effect of these transactions - they’re not submitted to the cluster, and they’re not visible to any other database value in your application.
We submit these transactions to a database value using with-tx
:
(let [real-tx (crux/submit-tx node [[:crux.tx/put {:crux.db/id :ivan, :name "Ivan"}]])
_ (crux/await-tx node real-tx)
all-names '{:find [?name], :where [[?e :name ?name]]}
db (crux/db node)]
(crux/q db all-names) ; => #{["Ivan"]}
(let [speculative-db (crux/with-tx db
[[:crux.tx/put {:crux.db/id :petr, :name "Petr"}]])]
(crux/q speculative-db all-names) ; => #{["Petr"] ["Ivan"]}
)
;; we haven't impacted the original db value, nor the node
(crux/q db all-names) ; => #{["Ivan"]}
(crux/q (crux/db node) all-names) ; => #{["Ivan"]}
)
The entities submitted by the speculative :crux.tx/put
take their valid time (if not explicitly specified) from the valid time of the db
they were forked from.
Can you improve this documentation? These fine people already did:
Daniel Mason & James HendersonEdit on GitHub
cljdoc is a website building & hosting documentation for Clojure/Script libraries
× close