• Stars
    star
    137
  • Rank 266,121 (Top 6 %)
  • Language
    Clojure
  • License
    Eclipse Public Li...
  • Created almost 15 years ago
  • Updated about 11 years ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

A graph database with pluggable backends, written in Clojure.

Build Status

Jiraph is an embedded graph database for Clojure. It is extremely fast and can walk 100,000 edges in about 3 seconds on my laptop. It uses Tokyo Cabinet for backend storage.

Multi-layer Graph

For performance and scalability, graphs in Jiraph are multi-layer graphs. Nodes exist on every layer. In this way, node data can be partitioned across all layers (similar to column families in some nosql databases). For our purposes, we'll call the node data on a particular layer a node slice. Edges, on the other hand, can only exist on a single layer. All edges on a specific layer generally correspond in some way. The layer name can be thought of as the edge type, or alternatively, multiple similar edge types can exist on one layer.

Though layers can be used to organize your data, the primary motivation for layers is performance. All data for each layer is stored in a separate data store, which partitions the graph and speeds up walks by allowing them to load only the subset of the graph data they need. This means you should strive to put all graph data needed for a particular walk in one layer. This isn't always possible, but it will improve speed because only one disk read will be required per walk step.

A Jiraph graph is just a clojure map of layer names (keywords) to datatypes that implement the jiraph.layer/Layer protocol.

Nodes and Edges

Every node slice is just a clojure map of attributes. It is conventional to use keywords for the keys, but the values can be arbitrary clojure data structures. Each edge is also a map of attributes. Internally, outgoing edges are stored as a map from node-ids to attributes in the :edges attribute on the corresponding node slice. This way, a node and all its outgoing edges can be loaded with one disk read.

Nodes are not required to have a type, but it is conventional to include the node type in its id if there are multiple types of nodes (e.g. "human-144567", "robot-23131"). Here is a sample node:

{:name      "Justin"
 :nicknames ["Judd" "Huck" "Judd Huck"]
 :edges     {"human-2" {:type :spouse}
             "robot-1" {:type :friend}
             "dog-2"   {:type :pet}}
}

Usage

(use 'jiraph.graph)

(def g
  {:foo (jiraph.masai-layer/make "/tmp/foo")
   :bar (jiraph.masai-layer/make "/tmp/bar")
   :baz (jiraph.masai-layer/make "/tmp/baz")})

(with-graph g
  (add-node! :foo "human-1" {:name "Justin"  :edges {"human-2" {:type :spouse}}})
  (add-node! :foo "human-2" {:name "Heather" :edges {"human-1" {:type :spouse}}})

  (get-node :foo "human-1"))
  ;; {:name "Justin", :edges {"human-2" {:type :spouse}}}

(with-graph g
  (add-node! :foo "robot-1" {:name "Bender" :edges {"human-1" {:type :friend}}})
  (append-node! :foo "human-1" {:edges {"robot-1" {:type :friend}}})

  (get-node :foo "human-1"))
  ;; {:name "Justin", :edges {"robot-1" {:type :friend}, "human-2" {:type :spouse}}}

(with-graph g
  (assoc-node! :foo "robot-1" {:designation "Bending Unit 22"})

  (get-node :foo "robot-1"))
  ;; {:edges {"human-1" {:type :friend}}, :name "Bender", :designation "Bending Unit 22"}

(with-graph g
  (update-node! :foo "robot-1" dissoc :designation)

  (get-node :foo "robot-1"))
  ;; {:name "Bender", :edges {"human-1" {:type :friend}}}

Revisions

You can use at-revision to mark changes with a given revision and rewind the state of the graph back to that revision later.

(with-graph g
  (at-revision 1
    (add-node! :foo "human-2" {:name "Ceruzzi"}))

  (at-revision 2
    (append-node! :foo "human-2" {:name "Hatcher"}))

  (:name (get-node :foo "human-2")) ;; "Hatcher"

  (at-revision 1
    (:name (get-node :foo "human-2"))) ;; "Ceruzzi"

  (:name (get-node :foo "human-2"))) ;; "Hatcher"

You can only use at-revision to rewind a layer's state if the layer was updated using only add-node! and append-node!. All other update operations are destructive, and nodes modified with update-node!, assoc-node! or delete-node! will not exist if you use at revision to go back to before they were modified. To ensure that no destructive operations are permitted on a layer, you can set :append-only in the metadata on the graph (either a set of layer names that are append-only, or true for all layers). Even if a layer is marked append-only, you can still call compact-node! to reduce the storage requirement and remove historical data.

Transactions also behave slightly different inside of at-revision. When with-transaction is complete, it sets the :rev property on current layer to the current-revision. Also only the first transaction on a layer for a given revision will be applied. Subsequent transactions are assumed to be duplicates. This permits cross-layer transactions to be performed by assigning the same revision number to all of them. Then if there is a failure in the middle of a revision, the entire revision can be reapplied and layers that have already been updated will be skipped.

Performance

For faster performance, Jiraph supports using protocol buffers for node slices and edge data.

Installation

The easiest way to use Jiraph in your project is via Cake. Add the following to the :dependencies key in your project.clj:

[jiraph "0.5.0-SNAPSHOT"]

Using Cake allows you to automatically pull in the native libraries for tokyocabinet and compile protocol buffers if you are using them. Protocol Buffers, if used, should be placed in your project's proto/ directory and can be compiled by running cake proto.

More Repositories

1

drip

Fast JVM launching without the hassle of persistent JVMs.
Shell
1,545
star
2

clojure-protobuf

Google protocol buffers wrapper for Clojure.
Clojure
217
star
3

cake

A tasty build tool for Clojure.
Clojure
171
star
4

classlojure

Advanced classloading for clojure.
Clojure
93
star
5

ring-async

Ring adapter supporting asynchronous responses using core.async channels.
Clojure
49
star
6

memcache

Advanced ruby memcache client
Ruby
48
star
7

eventual

Server-Sent Event and EventSource helpers for core.async.
Clojure
47
star
8

record_cache

Active Record caching and indexing in memcache. An alternative to cache_fu
Ruby
39
star
9

clojure-complete

Standalone Clojure completion library adapted from swank-clojure.
Clojure
35
star
10

wakeful

restful routing alternative for Clojure
Clojure
27
star
11

rupture

Ruby + Clojure
Ruby
19
star
12

lein-protobuf

Leiningen plugin for compiling google protocol buffers.
Clojure
18
star
13

action_flow

A state-machine inspired mixin for controllers that makes creating flows and wizards really simple.
Ruby
14
star
14

priority_queue

A simple priority queue for Ruby
Ruby
10
star
15

masai

Key-value database for Clojure with pluggable backends.
Clojure
10
star
16

method_cache

Powerful memcache-based memoization library for Ruby
Ruby
9
star
17

portal

A unified REPL and command server for Clojure.
Clojure
7
star
18

minisphinx

A simple Sphinx indexing library for Ruby.
Ruby
7
star
19

model_set

Easy manipulation of sets of ActiveRecord models
Ruby
7
star
20

ordered-collections

A Clojure set that maintains the insertion order of its contents.
Java
7
star
21

tokyocabinet

native tokyo cabinet libraries
C
7
star
22

active_document

Active models in Berkeley DB.
Ruby
6
star
23

cache_version

Store the version of any class for cache invalidation
Ruby
6
star
24

io

Clojure I/O utils.
Clojure
6
star
25

pg_queue

A ruby interface to skytools-pgq.
Ruby
5
star
26

deep_clonable

Deep cloning magic for Ruby objects.
Ruby
5
star
27

multi_table_inheritance

A Rails plugin that adds multi-table inheritance support using Postgres inheritance.
Ruby
5
star
28

deferrable

Simple extension for deferring method calls.
Ruby
5
star
29

github-uploader

Add file attachment capabilities to Github:issues and wikis.
JavaScript
4
star
30

retro

A protocol for revisioned transactions in Clojure.
Clojure
4
star
31

uncle

A lancet-inspired ant library for clojure.
Clojure
4
star
32

ordered_set

Like Set except it maintains the order of objects
Ruby
4
star
33

sqleton

a sql migration framework for clojure
Clojure
3
star
34

nested_transactions

Support for nested transactions in Rails (Postgres and MySQL)
Ruby
3
star
35

phonograph

A round-robin database, based on graphite's whisper.
Clojure
3
star
36

markdone-mode

Add todos to markdown-mode.
Emacs Lisp
3
star
37

grammer

Yammer notifications with Growl.
Clojure
3
star
38

icunicode

ICU Unicode Transliteration and Collation in Ruby.
C
3
star
39

replicator

Easy Postgres replication for Rails
Ruby
3
star
40

depot

A place for storing goods or motor vehicles.
Clojure
2
star
41

romp

A Stomp client library for Ruby.
Ruby
2
star
42

functor

Advanced functional stubbing in Ruby
2
star
43

meta

Ruby meta programming helpers.
Ruby
2
star
44

colorized

Cross application color theme generator inspired by Solarized.
Ruby
2
star
45

anti_object

Invert any object
Ruby
2
star
46

telegraph

A library that lets you build beautiful dashboards for telemetry and turntable.
JavaScript
2
star
47

ninjudd.github.com

HTML
1
star
48

test

test
1
star
49

try_git

1
star
50

resting

All I need is a place to lay my head.
JavaScript
1
star
51

cache_machine

Coming soon: A Rails engine for quick, convenient caching.
1
star
52

github-issues-plus

A simple github issues viewer that supports grouping by assignee and milestone.
Ruby
1
star
53

cake-marginalia

A Marginalia plugin to Cake
Clojure
1
star
54

yammerbot

Everyone's favorite Yammer irc bot.
Clojure
1
star
55

ego

Because your identity is important.
Clojure
1
star
56

ruminate

A map-reduce layer for jiraph.
Clojure
1
star