Balagan, Clojure (Script) data transformation and querying library
A tiny library for data structure transformation inspired by Enlive.
It's often hard to come up with a sensible DSL for data structure transformation. You either get too close to the data domain, and your functions get messy and difficult to write (or even maybe too nested) or you're too far away from data, and your functions operate nested structures as if they were plain.
Bałagan solves that problem for you by providing you with predicate-based selectors and arbitrary
transformation functions that suits any taste. It's mostly like get-in
and update-in
on steroids,
that allows you to build predicate queries, not only concrete ones.
Project Maturity
Balagan is a fairly young project, but it's been used in production for around a year by now, and has proven itself to be safe and stable.
Artifacts
Bałagan artifacts are released to Clojars. If you are using Maven, add the following repository definition to your pom.xml
:
<repository>
<id>clojars.org</id>
<url>http://clojars.org/repo</url>
</repository>
Most recent release version
With Leiningen:
[clojurewerkz/balagan "1.0.5"]
With Maven:
<dependency>
<groupId>clojurewerkz</groupId>
<artifactId>balagan</artifactId>
<version>1.0.5</version>
</dependency>
Documentation and Examples
Balagan builds on ideas from Enlive. It may help to get familiar with them first.
Let's say you're working on a Data Access layer for the application. You have a user entry represented as hash:
(def user
{:name "Alex"
:birth-year 1990
:nickname "ifesdjeen"})
Transformation
Now, we can start transforming users the way we want: add, remove fields based on certain conditions.
(update user
[] (add-field :cool-dude true) ;; adds a field :cool-dude with value true
(mk-path [:age]) #(- 2014 (:birth-year %)) ;; explicit adding of a new field, calculated from the existing data
(mk-path [:posts]) #(fetch-posts (:name %)) ;; fetching some related data from the DB
[:posts :*] #(update-posts %)) ;; apply some transformations to all the fetched posts, if there are any
Queries
Queries are very similar to how you'd query your data with filter
in Clojure:
(let [data {:a {:b [{:c 1} {:c 2} {:c 3}]
:d [{:c 5} {:c 6} {:c 7}]}}]
(select data [:* :* even? :c]))
;; => (1 3 5 7)
Results are returned in the order they've been seen in your data structure, however you should be aware of the fact that iterating over the hash in Clojure doesn't guarantee you order.
Path Queries
Path queries are most useful when you'd like to fire a function against some part of your data (be it processing, database initialization or anything else.
You can also run predicate queries based on your map, for example if you want to configure your database servers from rather big and complex config:
(def conf {:db
{:redis {:cache [{:host "host01" :port 1234} {:host "host02" :port 1234}]
:pubsub [{:host "host01" :port 1234} {:host "host02" :port 1234}]}}
:cassandra [{:host "host01"} {:host "host02"}]})
(with-paths conf
[:db :redis :cache] configure-redis-cache
[:db :redis :pubsub] configure-redis-pubsub
[:db :cassandra] configure-cassandra)
In this example, configure-redis-cache
funciton will receive two arguments: value
and path
:
(defn configure-redis-cache
[value path]
(println "Value: " value)
(println "Path: " path))
;; => Value: [{:host host01, :port 1234} {:host host02, :port 1234}]
;; => Path: [:db :redis :cache]
You can also do wildcard-matching with :*
, for example:
(b/select {:a {:b {:c 1} :d {:c 2}}}
[:a :* :c] (fn [val path]
(if (= path [:a :b :c])
(is (= val 1))
(is (= val 2)))))
Community
To subscribe for announcements of releases, important changes and so on, please follow @ClojureWerkz on Twitter.
Balagan Is a ClojureWerkz Project
Balagan is part of the group of libraries known as ClojureWerkz, together with Monger, Welle, Neocons, Elastisch and several others.
Continuous Integration
CI is hosted by travis-ci.org
Development
Balagan uses Leiningen 2. Make sure you have it installed and then run tests against all supported Clojure versions using
lein do clean, cljx once, all test, cljsbuild test
Then create a branch and make your changes on it. Once you are done with your changes and all tests pass, submit a pull request on Github.
Balagan?
Hash
is a synonym of mess
, Bałagan means mess
in several Slavic
languages.
License
Copyright © 2014-2016 Alex P, Michael S. Klishin, and the ClojureWerkz team.
Distributed under the Eclipse Public License, the same as Clojure.