Last active
July 28, 2017 16:02
-
-
Save danielneal/03118563c7d67dd4d872edb43af11e75 to your computer and use it in GitHub Desktop.
A micro denormalised index for reframe
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(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