• Stars
    star
    197
  • Rank 192,039 (Top 4 %)
  • Language
    Clojure
  • License
    Eclipse Public Li...
  • Created over 3 years ago
  • Updated about 1 year ago

Reviews

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

Repository Details

A library for storing and querying graph data in Clojure

pyramid

A library for storing and querying graph data in Clojure.

Features:

  • Graph query engine that works on any in-memory data store
  • Algorithm for taking trees of data and storing them in normal form in Clojure data.

Install & docs

Clojars Project cljdoc badge

Why

Clojure is well known for its graph databases like datomic and datascript which implement a datalog-like query language. There are contexts where the power vs performance tradeoffs of these query languages don't make sense, which is where pyramid can shine.

Pyramid focuses on doing essential things like selecting data from and traversing relationships between entities, while eschewing arbitrary logic like what SQL and datalog provide. What it lacks in features it makes up for in read performance when combined with a data store that has fast in-memory look ups of entities by key, such as Clojure maps. It can also be extended to databases like Datomic, DataScript and Asami.

What

Pyramid can be useful at each evolutionary stage of a program where one needs to traverse and make selections out of graphs of data.

Selection

Pyramid starts by working with Clojure data structures. A simple program that uses pyramid can use a query to select specific data out of a large, deeply nested tree, like a supercharged select-keys.

(def data
  {:people [{:given-name "Bob" :surname "Smith" :age 29}
            {:given-name "Alice" :surname "Meyer" :age 43}]
   :items {}})

(def query [{:people [:given-name]}])

(pyramid.core/pull data query)
;; => {:people [{:given-name "Bob"} {:given-name "Alice"}]}

Transformation

Pyramid combines querying with the Visitor pattern in a powerful way, allowing one to easily perform transformations of selections of data. Simply annotate parts of your query with metadata {:visitor (fn visit [data selection] ,,,)} and the visit function will be used to transform the data in a depth-first, post-order traversal (just like clojure.walk/postwalk).

(def data
  {:people [{:given-name "Bob" :surname "Smith" :age 29}
            {:given-name "Alice" :surname "Meyer" :age 43}]
   :items {}})

(defn fullname
  [{:keys [given-name surname] :as person}]
  (str given-name " " surname))

(def query [{:people ^{:visitor fullname} [:given-name :surname]}])

(pyramid.core/pull data query)
;; => {:people ["Bob Smith" "Alice Meyer"]}

Accretion

A more complex program may need to keep track of that data over time, or query data that contains cycles, which can be done by creating a pyramid.core/db. "Pyramid dbs" are maps that have a particular structure:

;; for any entity identified by `[key id]`, it follows the shape:
{key {id {,,,}}

Adding data to a db will normalize the data into a flat structure allowing for easy updating of entities as new data is obtained and allow relationships that are hard to represent in trees. Queries can traverse the references inside this data.

See docs/GUIDE.md.

Durability

A program may grow to need durable storage and other features that more full featured in-memory databases provide. Pyramid provides a protocol, IPullable, which can be extended to allow queries to run over any store that data can be looked up by a tuple, [primary-key value]. This is generalizable to most databases like Datomic, DataScript, Asami and SQLite.

Full stack

The above shows the evolution of a single program, but many programs never grow beyond the accretion stage. Pyramid has been used primarily in user interfaces where data is stored in a data structure and queried over time to show different views on a large graph. Through its protocols, it can now be extended to be used with durable storage on the server as well.

Concepts

Query: A query is written using EQL, a query language implemented inside Clojure. It provides the ability to select data in a nested, recursive way, making it ideal for traversing graphs of data. It does not provide arbitrary logic like SQL or Datalog.

Entity map: a Clojure map which contains information that uniquely identifies the domain entity it is about. E.g. {:person/id 1234 :person/name "Bill" :person/age 67} could be uniquely identified by it's :person/id key. By default, any map which contains a key which (= "id" (name key)) is true, is an entity map and can be normalized using pyramid.core/db.

Ident function: a function that takes a map and returns a tuple [:key val] that uniquely identifies the entity the map describes.

Lookup ref: a 2-element Clojure vector that has a keyword and a value which together act as a pointer to a domain entity. E.g. [:person/id 1234]. pyramid.core/pull will attempt to look them up in the db value if they appear in the result at a location where the query expects to do a join.

Usage

See docs/GUIDE.md

Prior art

Copyright

Copyright © 2023 Will Acton. Distributed under the EPL 2.0.

More Repositories

1

helix

A simple, easy to use library for React development in ClojureScript.
Clojure
601
star
2

punk

A data REBL built for the web
Clojure
150
star
3

cascade

A library of continuation-passing, thunk-producing versions of many Clojure core functions.
Clojure
70
star
4

flex

flex is a reactive signal library for Clojure(Script)
Clojure
67
star
5

eql-cli

A CLI for executing EQL queries on EDN data
Clojure
42
star
6

bs-most

Reason/BuckleScript bindings for the Most.js reactive toolkit
OCaml
41
star
7

observe-component

A library for accessing React component events as reactive observables
JavaScript
36
star
8

hooks-demo

Demonstration of React Hooks used in ClojureScript
Clojure
35
star
9

storybook-cljs

Example using shadow-cljs and storybook
Clojure
34
star
10

7-humble-guis

An implementation of 7 GUIs using HumbleUI
Clojure
30
star
11

apollo-cljs-example

apollo-cljs-example
Clojure
24
star
12

plum

Clojure
23
star
13

humble-starter

A starter project for developing desktop apps using HumbleUI
Clojure
23
star
14

dom

Clojure
20
star
15

reagent-context

Easy access to React context in ClojureScript & Reagent
Clojure
17
star
16

advent-2017

Advent of Code 2017
OCaml
15
star
17

thump

Clojure
14
star
18

helix-todo-mvc

Clojure
12
star
19

react-clj

Clojure
12
star
20

datascript-notes

12
star
21

serenity

Clojure
10
star
22

advent-2019

Advent of Code 2019
Clojure
10
star
23

helix-spec-alpha

Clojure
9
star
24

good-dog

A Clojure(Script) library for fetching data over HTTP(S)
Clojure
9
star
25

react-repl

A library for interacting with a live React application at a REPL.
Clojure
8
star
26

apollo-reagent

ClojureScript & Reagent integration for Apollo Client
Clojure
8
star
27

flex-old

A library for creating dynamic, incremental dataflow programs in Clojure(Script).
Clojure
7
star
28

lilac.town

Clojure
7
star
29

reason-autocomplete

An example ReasonML project
OCaml
7
star
30

exo

Clojure
7
star
31

harmony

multiversion concurrency control for JS
TypeScript
6
star
32

hx-frisk

Clojure
5
star
33

kitten

My custom Emacs configuration, based around meow
Emacs Lisp
5
star
34

clerk-mode

An Emacs minor mode for presenting Clojure files in Clerk
Emacs Lisp
4
star
35

7-flex-dom-guis

Clojure
4
star
36

punk2

Clojure
4
star
37

flex-dom

Clojure
4
star
38

habitbot

Slack bot for Habitica (formerly HabitRPG) party
JavaScript
4
star
39

solid

Clojure
3
star
40

immutable-editor

React component for live-editing Immutable.JS data
JavaScript
3
star
41

homebrew-brew

Ruby
2
star
42

zmk-config

C
2
star
43

hux

A declarative DSL for writing functional programs in javascript
TypeScript
2
star
44

clinch

React Hooks for ClojureScript
Clojure
2
star
45

react-template-server

An example system where a server (in this case Node.js + Express) serves up server-rendered React components as page templates.
JavaScript
2
star
46

bastardkb-qmk

C
2
star
47

lambda-tools

Tools for building serverless Clojure applications in AWS Lambda
Clojure
2
star
48

mim

A task runner built for Clojure
Clojure
2
star
49

advent-2022

Factor
2
star
50

advent-2020

Scheme
2
star
51

edn-tree

Clojure
2
star
52

fighweel-helix

C++
2
star
53

module-example

Clojure
2
star
54

refold

Clojure
2
star
55

shadow-cljs-const-repro

Clojure
1
star
56

clojure-lambda

Clojure
1
star
57

scaffold-web

Scaffolding for a basic web project using webpack/babel
JavaScript
1
star
58

reagent-list-broken

Clojure
1
star
59

cactus

Experimental MVI framework for React, codename "Cactus"
TypeScript
1
star
60

willacton.com

HTML
1
star
61

dataloader

Clojure
1
star
62

advent-2018

Rust
1
star
63

async-seq

Clojure
1
star
64

shadow-reagent-flip-move-example

Clojure
1
star
65

shadow-cljs-macchiato

Clojure
1
star
66

mashup

A node.js web application that generates random reddit headlines.
JavaScript
1
star
67

dxl

Clojure
1
star
68

observable-drag-drop-example

Example of implementing drag and drop using React and observe-component
JavaScript
1
star
69

sci

Configurable Clojure interpreter suitable for scripting and Clojure DSLs.
Clojure
1
star