Skip to content

Instantly share code, notes, and snippets.

@danielneal
Last active July 28, 2017 16:02
Show Gist options
  • Save danielneal/03118563c7d67dd4d872edb43af11e75 to your computer and use it in GitHub Desktop.
Save danielneal/03118563c7d67dd4d872edb43af11e75 to your computer and use it in GitHub Desktop.
A micro denormalised index for reframe
(ns compound.core
(:require [clojure.spec :as s]
[clojure.set :as set]
[clojure.test :as test :refer [deftest is]]))
(defn find-index-def [index-defs index]
(some #(when (= (:index %) index) %) index-defs))
(defn index-unique
"Like set/index, but for unique indexes, so the vals are just maps, not
sets"
[xrel ks]
(reduce
(fn [m x]
(let [ik (select-keys x ks)
existing-value (get m ik)]
(if existing-value
(throw (ex-info "Duplicate key" {:x x, :ik ik}))
(assoc m ik x))))
{} xrel))
(s/def ::unique? boolean?)
(s/def ::index (s/coll-of keyword? :kind vec))
(s/def ::index-def (s/keys :req-un [::unique? ::index]))
(defn compound
"Make a 'compound' from a set and index definitions
A compound contains denormalised data indexed by multiple indexes"
[coll index-defs]
(s/assert (s/coll-of ::index-def) index-defs)
[(reduce (fn [m index-def]
(let [{:keys [index unique?]} index-def]
(assoc m index (if unique?
(index-unique coll index)
(set/index coll index))))) {} index-defs)
index-defs])
(defn compound-spec
"Returns the spec for a compound given a spec for the element"
[s]
(s/cat :data (s/map-of ::index (s/map-of map? (s/or :unique s
:multiple (s/coll-of s :kind set?))))
:index-defs (s/coll-of ::index-def)))
(defn compound-extract
"Gets the coll out of the compound"
([c]
(let [[data index-defs] c]
(compound-extract c (first index-defs))))
([c index-def]
(let [[data _] c
{:keys [unique? index]} index-def
xs (vals (get data index))]
(if unique? (into #{} xs) (apply set/union xs)))))
(defn compound-find
"Gets data from the compound at the specific key"
[c m]
(let [[data _] c
index (vec (keys m))]
(get-in data [index m])))
(defn compound-update
"Updates the compound at the specific key"
[c m f & args]
(let [[data index-defs] c
{:keys [unique? index] :as index-def} (find-index-def index-defs (keys m))
c [(apply update-in data [index m] f args) index-defs]
modified-coll (compound-extract c index-def)]
(compound modified-coll index-defs)))
(defn compound-merge
"Merge a coll into the compound"
[c coll]
(let [[index-defs data] c]
(compound coll index-defs)))
(deftest compound-test
(is (= (-> (compound #{{:a 1 :b 1} {:a 2 :b 2 :c 3} {:a 1 :b 2 :c 4}} #{{:index [:a]
:unique? false}
{:index [:b]
:unique? false}
{:index [:a :b]
:unique? true}})
(compound-find {:a 1}))
#{{:a 1, :b 2, :c 4} {:a 1, :b 1}}))
(is (= (-> (compound #{{:a 1 :b 1} {:a 2 :b 2 :c 3} {:a 1 :b 2 :c 4}} #{{:index [:a]
:unique? false}
{:index [:b]
:unique? false}
{:index [:a :b]
:unique? true}})
(compound-find {:a 1 :b 2}))
{:a 1, :b 2, :c 4}))
(is (= (-> (compound #{{:a 1 :b 1} {:a 2 :b 2 :c 3} {:a 1 :b 2 :c 4}} #{{:index [:a]
:unique? false}
{:index [:b]
:unique? false}
{:index [:a :b]
:unique? true}})
(compound-update {:a 1} conj {:a 1 :b 3})
(compound-find {:a 1}))
#{{:a 1, :b 2, :c 4} {:a 1, :b 1} {:a 1, :b 3}}))
(is (= (-> (compound #{{:a 1 :b 1} {:a 2 :b 2 :c 3} {:a 1 :b 2 :c 4}} #{{:index [:a]
:unique? false}
{:index [:b]
:unique? false}
{:index [:a :b]
:unique? true}})
(compound-update {:a 1 :b 1} assoc :d 3)
(compound-find {:a 1}))
#{{:a 1, :b 2, :c 4} {:a 1, :b 1, :d 3}}))
(is (= (-> (compound #{{:a 1 :b 1} {:a 2 :b 2 :c 3} {:a 1 :b 2 :c 4}} #{{:index [:a]
:unique? false}
{:index [:b]
:unique? false}
{:index [:a :b]
:unique? true}})
(compound-update {:a 1 :b 1} assoc :d 3)
(compound-find {:a 1}))
#{{:a 1, :b 2, :c 4} {:a 1, :b 1, :d 3}}))
(is (s/valid? (compound-spec (s/keys :req-un [::a]))
(compound #{{:a 1 :b 1} {:a 2 :b 2 :c 3} {:a 1 :b 2 :c 4}} #{{:index [:a]
:unique? false}
{:index [:b]
:unique? false}
{:index [:a :b]
:unique? true}})))
(is (= (-> (compound #{{:a 1 :b 1} {:a 2 :b 2 :c 3} {:a 1 :b 2 :c 4}} #{{:index [:a]
:unique? false}
{:index [:b]
:unique? false}
{:index [:a :b]
:unique? true}})
(compound-update {:a 1 :b 1} assoc :d 3)
(compound-extract))
#{{:a 2, :b 2, :c 3} {:a 1, :b 2, :c 4} {:a 1, :b 1, :d 3}})))
(comment
(clojure.test/run-tests))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment