Menu

Quickstart

This quickstart guide shows you how to set up Crux, ingest a document, and query it. In a few minutes, you will have a single Crux node that persists records to disk using an in-process RocksDB instance. This means you do not need to install RocksDB separately — it will just work out of the box.

Examples are provided in Java and Clojure. Your data will be stored on the same computer your Java or Clojure process is running. This configuration is sufficient for fault-tolerant, low-load production services. It is an easy way to get familiar with Crux in a minimal production-like environment. The section "Advanced Crux Configuration" below contains more advanced configuration options.

This guide was extracted from the "Crux-in-a-Box" guide, which has minimal (working) project examples for Clojure and Java you can see in the crux-in-a-box GitHub repo. If you prefer to see all the code at once, head there first. We also have thorough Reference Docs, if that’s your speed.

Each step below contains some advanced options in collapsible notes. You can safely ignore all of these if you just want to get up and running quickly. You can always revisit them later.

1. Setup

Install Dependencies

Ensure you are using Java 8 or higher:

java -version

All System Requirements

  • Java: JDK 8 or higher

  • Clojure: Clojure 1.10.3 or higher

  • macOS: 10.14.3 (Mojave) or higher

    • MacOS 10.14.3 is a RocksDB libc dependency for macOS users. (See RocksDB#5064.)

    • JDK: You may require AdoptOpenJDK on certain macOS versions. (See crux#894.)

  • Linux: No known version constraints

  • Windows: No known version constraints

Add Crux to your Clojure or Java project:

  • deps.edn

  • project.clj

  • pom.xml

juxt/crux-core {:mvn/version "21.04-1.16.0-beta"}
juxt/crux-rocksdb {:mvn/version "21.04-1.16.0-beta"}
[juxt/crux-core "21.04-1.16.0-beta"]
[juxt/crux-rocksdb "21.04-1.16.0-beta"]
<dependency>
    <groupId>juxt</groupId>
    <artifactId>crux-core</artifactId>
    <version>21.04-1.16.0-beta</version>
</dependency>
<dependency>
    <groupId>juxt</groupId>
    <artifactId>crux-rocksdb</artifactId>
    <version>21.04-1.16.0-beta</version>
</dependency>

Advanced Installation
This guide will walk you through the installation process. If you would prefer to see all available installation options — including pre-built JARs, custom Docker containers, and Clojure CLI tooling — you will want to read the Reference Docs: Installation page.

Advanced Logging
Ignore this note if you just want to get up and running quickly. It’s completely optional, but nice to have.

Without setting up logging, you will see a SLF4J: Defaulting to no-operation (NOP) logger implementation message on STDOUT from Crux. This is harmless, but in a real application you will want to configure Logback with the SLF4J API to see INFO messages from Crux.

To do this in Clojure:

To do this in Java:

Configure Crux

The following configuration uses an in-process RocksDB instance to write your data to disk. It will save your data in a directory named ./data/dev, relative to your project root. You do not need to create this directory. Crux will create it for you.

  • Clojure

  • Java

  • config.json

(ns crux-in-a-box.db
  (:require [clojure.java.io :as io]
            [crux.api :as crux]))

(defn start-crux! []
  (letfn [(kv-store [dir]
            {:kv-store {:crux/module 'crux.rocksdb/->kv-store
                        :db-dir      (io/file dir)
                        :sync?       true}})]
    (crux/start-node
     {:crux/tx-log              (kv-store "data/dev/tx-log")
      :crux/document-store      (kv-store "data/dev/doc-store")
      :crux/index-store         (kv-store "data/dev/index-store")})))

(def crux-node (start-crux!))

(defn stop-crux! []
  (.close crux-node))
package cruxinabox;

import java.io.File;
import java.io.IOException;
import crux.api.Crux;
import crux.api.ICruxAPI;

import java.util.HashMap;
import java.util.List;

import crux.api.ICruxDatasource;
import crux.api.ICursor;
import crux.api.CruxDocument;
import crux.api.TransactionInstant;
import crux.api.tx.Transaction;

class Box {
    public static void main(String[] args) {
        try(ICruxAPI cruxNode = Crux.startNode(new File("config.json"))) {
            System.out.println("Crux Started.");
            // ... ingest goes here
            // ... query goes here
        }
        catch (IOException e) {
            // ...
        }
    }
}
{
  "crux/index-store": {
    "kv-store": {
      "crux/module": "crux.rocksdb/->kv-store",
      "db-dir": "data/index-store"
    }
  },
  "crux/document-store": {
    "kv-store": {
      "crux/module": "crux.rocksdb/->kv-store",
      "db-dir": "data/doc-store"
    }
  },
  "crux/tx-log": {
    "kv-store": {
      "crux/module": "crux.rocksdb/->kv-store",
      "db-dir": "data/tx-log"
    }
  },
  "crux.lucene/lucene-store": {
    "db-dir": "data/dev/lucene-dir"
  },
  "crux.http-server/server": {
    "port": 9999
  }
}

Advanced Config Libraries
This guide gets you up and running quickly by hard-coding configuration values. Store these configuration values with your favourite configuration library. In Clojure, you might use Aero.

This guide also does not make any assumptions about how you manage stateful services. Crux is a stateful service, however, and you should store the Crux node in your favourite state management library. In Clojure, you might use Mount.

Advanced Crux Configuration
Crux configuration has three components:

  1. Transaction Log

  2. Document Store

  3. Index Store

For more advanced configuration, Crux allows you to choose the underlying data storage technology (for each of the three components) from a number of different modules. The Reference Docs contain a list of available modules, each with instructions on how to configure them.

2. Ingest

  • Clojure REPL

  • Java

crux-in-a-box.db> (crux/submit-tx crux-node [[:crux.tx/put
                                              {:crux.db/id "hi2u"
                                               :user/name "zig"}]])
;; => #:crux.tx{:tx-id 0, :tx-time #inst "2021-03-11T02:27:09.176-00:00"}
HashMap<String, Object> data = new HashMap<>();
data.put("user/name", "zig");
CruxDocument document = CruxDocument.create("hi2u", data);
TransactionInstant transaction = node.submitTx(Transaction.buildTx(tx -> {
    tx.put(document);
}));
System.out.println(data.toString());

Advanced Transactions
This is the simplest possible transaction. Crux has more advanced transaction features including synchronous awaits, eviction, transaction functions, speculative transactions, and bitemporal valid-time put/delete. You can read about them in Reference Docs: Transactions.

3. Query

  • Clojure REPL

  • Java

crux-in-a-box.db> (crux/q (crux/db crux-node) '{:find [e]
                                                :where [[e :user/name "zig"]]} )
;; => #{["hi2u"]}

crux-in-a-box.db> (stop-crux!)
;; => nil
String query = "{:find [e] :where [[e :user/name \"zig\"]]}";
ICruxDatasource db = node.db();
ICursor<List<?>> results = db.openQuery(query);
if (results.hasNext()) {
    List<?> result = results.next();
    System.out.println(result.toString());
}
db.close();
node.close();

Advanced Queries
This is the simplest possible query. Crux has very powerful bitemporal graph queries with Datalog and SQL. To learn more about aggregates, pull syntax, returning maps, binding, subqueries, predicates, ordering, pagination, rules, bitemporal time-travel, streaming, and entity history you can read more in Reference Docs: Queries and Reference Docs: SQL.

Further Reading

Crux requires very little effort to set up and start using immediately. Now that you are comfortable with the basics, you can read through the Reference Docs or try the Tutorial. We also have Articles and blog posts talking about the philosophy and history of Crux. If you are feeling particularly excited, you can browse our Bibliography.

As always, we encourage you to chat with us on Zulip or #crux on Clojurians if you have questions or ideas. Enjoy Crux!