diff --git a/bin/install.sh b/bin/install.sh new file mode 100755 index 0000000..c245901 --- /dev/null +++ b/bin/install.sh @@ -0,0 +1,40 @@ +#!/usr/bin/env bash + +numArgs=$# + +set -e -o pipefail + +function die() { + MESG="${1:-Died}" + echo "${MESG}" >&2 + exit 1 +} + + +if [[ !$numArgs -gt 0 ]]; then + die "Please supply a version number e.g. 0.0.1" +fi + +group="de.mpg.shh" +artifact="util-datomic-peer" +version=$1 + +lein clean +lein deps +lein jar +lein pom + +test -f "target/${artifact}-${version}.jar" || die "Build failed: jar not found" + +lein localrepo install -p pom.xml target/${artifact}-${version}.jar ${group}/${artifact} ${version} + +# create sha1sums for the jar and pom +group_path=$(echo "${group}" | tr "." "/") +jar_path="${HOME}/.m2/repository/${group_path}/${artifact}/${version}/${artifact}-${version}.jar" +jar_sum_path="${jar_path}.sha1" + +pom_path="${HOME}/.m2/repository/${group_path}/${artifact}/${version}/${artifact}-${version}.pom" +pom_sum_path="${pom_path}.sha1" + +shasum ${jar_path} | cut -d ' ' -f 1 > ${jar_sum_path} +shasum ${pom_path} | cut -d ' ' -f 1 > ${pom_sum_path} diff --git a/project.clj b/project.clj new file mode 100755 index 0000000..3f39cc0 --- /dev/null +++ b/project.clj @@ -0,0 +1,13 @@ +(defproject de.mpg.shh/util-datomic-peer "0.0.1" + :description "Utilities for using datomic databases" + :url "http://www.shh.mpg.de/" + :license {:name "Eclipse Public License" + :url "http://www.eclipse.org/legal/epl-v10.html"} + :dependencies [[org.clojure/clojure "1.8.0"] + [org.clojure/tools.logging "0.3.1"] + [org.apache.logging.log4j/log4j-api "2.5"] + [org.apache.logging.log4j/log4j-core "2.5"] + [org.apache.logging.log4j/log4j-1.2-api "2.5"] + [org.slf4j/slf4j-log4j12 "1.6.4"] + [com.datomic/datomic-pro "0.9.5554" :exclusions [org.slf4j/slf4j-nop org.slf4j/log4j-over-slf4j]]] + :source-paths ["src/main/clojure"]) diff --git a/src/main/clojure/de/mpg/shh/util_datomic_peer/datom.clj b/src/main/clojure/de/mpg/shh/util_datomic_peer/datom.clj new file mode 100644 index 0000000..634e52d --- /dev/null +++ b/src/main/clojure/de/mpg/shh/util_datomic_peer/datom.clj @@ -0,0 +1,64 @@ +(ns de.mpg.shh.util-datomic-peer.datom + (:require [clojure.tools.logging :refer [info error]] + [clojure.string :as str] + [clojure.pprint :as pp] + [datomic.api :as d]) + (:import [java.util UUID] + [java.util.concurrent ExecutionException] + [datomic.db DbId])) + +(defn flatten-eid-pull + [e] + (letfn [(extract-eid [accumulator [k v]] + (if (= :db/id k) + (conj accumulator v) + (cond + (and (coll? v) (map? v)) + (reduce extract-eid accumulator v) + (sequential? v) + (concat accumulator (flatten (map #(reduce extract-eid [] %) v))) + :else (throw (Exception. (str/join "" ["Don't know how to extract eid from: " v]))))))] + (reduce extract-eid [] e))) + +(defn datoms-from-query + "Return raw datoms from pull query results" + [db pull-results] + (let [eids-only (into #{} (flatten (map flatten-eid-pull pull-results)))] + (flatten (map #(vec (d/datoms db :eavt %)) eids-only)))) + +(defn attr-filtered-datoms + [db e-id attr-ids] + (vec (concat (apply #(d/datoms db :eavt e-id %) attr-ids)))) + +(defn datoms-from-query-limit-attr + "Return raw datoms from pull query results" + [db pull-results attrs] + (let [attr-ids (map :db/id (d/q '[:find [(pull ?e [:db/id]) ...] + :in $ [?ident ...] + :where [?e :db/ident ?ident]] + db + attrs)) + eids-only (into #{} (flatten (map flatten-eid-pull pull-results)))] + (flatten (map #(attr-filtered-datoms db % attr-ids) eids-only)))) + +(defn trunc + "Return a string rep of x, shortened to n chars or less" + [x n] + (let [s (str x)] + (if (<= (count s) n) + s + (str (subs s 0 (- n 3)) "...")))) + +(defn datom-table + "Print a collection of datoms in an org-mode compatible table." + [db datoms] + (->> datoms + (map + (fn [{:keys [e a v tx added]}] + {"part" (d/part e) + "e" (format "0x%016x" e) + "a" (d/ident db a) + "v" (trunc v 24) + "tx" (format "0x%x" tx) + "added" added})) + (pp/print-table ["part" "e" "a" "v" "tx" "added"]))) diff --git a/src/main/clojure/de/mpg/shh/util_datomic_peer/transaction.clj b/src/main/clojure/de/mpg/shh/util_datomic_peer/transaction.clj new file mode 100644 index 0000000..f306054 --- /dev/null +++ b/src/main/clojure/de/mpg/shh/util_datomic_peer/transaction.clj @@ -0,0 +1,68 @@ +(ns de.mpg.shh.util-datomic-peer.transaction + (:require [clojure.tools.logging :refer [info error]] + [clojure.string :as str] + [datomic.api :as d]) + (:import [java.util.concurrent ExecutionException] + [datomic.db DbId])) + +(defn wrap-tx + [tx-future] + (try + {:tx-result (deref tx-future)} + (catch ExecutionException eex + {:error (.getMessage (.getCause eex))}))) + +(defn transact-entity-and-resolve-id + [tx-future temp-eid] + (let [tx-result (wrap-tx tx-future)] + (if (contains? tx-result :error) + tx-result + (assoc tx-result :eid (d/resolve-tempid (:db-after (:tx-result tx-result)) (:tempids (:tx-result tx-result)) temp-eid))))) + +(defn resolve-tx-map-temp-id + [id-map accumulator [k v]] + (if (and (= DbId (type v)) + (contains? id-map v)) + (assoc accumulator k (get id-map v)) + (assoc accumulator k v))) + +(defn resolve-tx-datom-temp-id + [id-map accumulator v] + (if (and (= DbId (type v)) + (contains? id-map v)) + (conj accumulator (get id-map v)) + (conj accumulator v))) + +(defn resolve-tx-temp-ids + [id-map tx] + (if (map? tx) + (reduce (partial resolve-tx-map-temp-id id-map) {} tx) + (reduce (partial resolve-tx-datom-temp-id id-map) [] tx))) + +(defn resolve-id-map + [tx-result accumulator [k v]] + (if (= DbId (type v)) + (let [resolved-id (d/resolve-tempid (:db-after tx-result) (:tempids tx-result) v)] + (if (nil? resolved-id) + (assoc accumulator k v) + (assoc accumulator k v + v resolved-id))) + (assoc accumulator k v))) + +(defn apply-tx-with-db + "apply the transaction to the given db using with" + [accumulator item] + (let [resolved-item (vec (map (partial resolve-tx-temp-ids (:id-map accumulator)) item)) + tx-result (d/with (:db accumulator) resolved-item) + resolved-id-map (reduce (partial resolve-id-map tx-result) {} (:id-map accumulator))] + (assoc accumulator :db (:db-after tx-result) + :id-map resolved-id-map))) + +(defn apply-tx + "apply the transaction to the given db" + [accumulator item] + (let [resolved-item (vec (map (partial resolve-tx-temp-ids (:id-map accumulator)) item)) + tx-result (.get (d/transact (:conn accumulator) resolved-item)) + resolved-id-map (reduce (partial resolve-id-map tx-result) {} (:id-map accumulator))] + (assoc accumulator :db (:db-after tx-result) + :id-map resolved-id-map)))