(ns document-storage.postgres.documents (:require [document-storage.postgres.encoding :as encoding] [document-storage.postgres.cache :as cache] [editscript.core :as escript] [clj-helper.vector :as vhelper]) (:import [java.util.concurrent ArrayBlockingQueue])) (defn get-document-frame-by-version [storage document-id version repository] (.run-query storage :get-document-frame-by-version {:repository repository :version version :document-id document-id})) (defn get-document-frame-by-id [storage frame-id repository] (.run-query storage :get-document-frame {:repository repository :id frame-id})) (defn get-last-document-frame [storage document-id repository] ;;;TODO check if this can be speed up by using index in db (when-let [frame (.run-query storage :get-last-document-frame {:repository repository :document-id document-id})] frame)) (defn get-last-document-version [storage document-id repository] (when-let [frame (get-last-document-frame storage document-id repository)] {:version (:version frame) :data-type (keyword (:data_type frame))})) (defn restore-document-version [storage document-id version repository] (when-let [frames (.run-query storage :select-relevant-document-frames-by-version {:repository repository :document-id document-id :version version})] (let [next-frames (into {} (map (juxt :reference identity)) frames) keyframe (vhelper/get-by frames (comp name :data_type) "key")] (if keyframe (loop [active-frame keyframe data (encoding/decode-document (:data active-frame))] (let [next-frame (get next-frames (:id active-frame))] (if-not next-frame data (recur next-frame (escript/patch data (escript/edits->script (encoding/decode-document (:data next-frame)))))))) (throw (ex-info (str "No keyframe for document " document-id " with version " version " in repository " repository) {:document-id document-id :version version :repository repository})))))) (defn log-request [storage repository document-id version] (.start (Thread. (fn [] (.run-query storage :log-request! {:repository repository :document-id document-id :version version}) (.run-query storage :create-document-stats! {:repository repository :document-id document-id :version version}))))) (defn load-document ([storage document-id repository] (when-let [last-document-version (get-last-document-version storage document-id repository)] (when-not (= (name (:data-type last-document-version)) "delete") (load-document storage document-id (:version last-document-version) repository)))) ([storage document-id version repository] (let [restore-document-fn #(restore-document-version storage document-id version repository)] (log-request storage repository document-id version) (if (.cached-repository? storage repository) (cache/load-cached-document storage repository document-id version restore-document-fn) (restore-document-fn))))) (defn save-keyframe [storage document-id document last-version repository] (let [doc (encoding/encode-document document) last-version (or last-version 0)] ;;VALUES (:uuid, :document-id, :type, :encoding, :version, :reference, :data) (.run-query storage :create-document-frame! {:repository repository :document-id document-id :type "key" :encoding "nippy" :version (inc last-version) :reference nil :data doc}))) (defn overwrite-keyframe [storage document-id document version repository] (let [doc (encoding/encode-document document) version (or version 0)] ;;VALUES (:uuid, :document-id, :type, :encoding, :version, :reference, :data) (.run-query storage :overwrite-document-frame! {:repository repository :document-id document-id :type "key" :encoding "nippy" :version version :reference nil :data doc}))) (defn save-pframe [storage document-id doc last-version repository] (let [last-doc (load-document storage document-id last-version repository) last-frame (get-document-frame-by-version storage document-id last-version repository) diff (escript/diff last-doc doc {:str-diff? false :algo :quick}) diff-doc (encoding/encode-document (escript/get-edits diff))] (.run-query storage :create-document-frame! {:repository repository :document-id document-id :type "diff" :encoding "nippy" :version (inc last-version) :reference (:id last-frame) ;;;FIXME save ID in reference not version :data diff-doc}))) (def get-repo-queue (memoize (fn [reponame] (new ArrayBlockingQueue 1 true)))) (defn save-document [storage document-id document repository options] (let [queue (get-repo-queue repository) job-id (gensym)] (.put queue job-id) (cache/update-query-cache-document storage repository document-id document) (let [save-result (if-let [last-version (get-last-document-version storage document-id repository)] (if (or (true? (:force-keyframe options)) (false? (.versioned-repository? storage repository)) (= (keyword (name (:data-type last-version))) :delete)) (let [version-num (:version last-version)] (if (.versioned-repository? storage repository) (save-keyframe storage document-id document version-num repository) (overwrite-keyframe storage document-id document version-num repository))) (save-pframe storage document-id document (:version last-version) repository)) (save-keyframe storage document-id document -1 repository))] (.take queue) save-result))) (defn delete-document [storage document-id repository] (if (true? (.delete-permanently? storage repository)) (doseq [v (map :version (.list-versions storage document-id repository))] (.run-query storage :overwrite-document-frame! {:repository repository :document-id document-id :type "delete" :encoding "edn" :version v :reference nil :data (encoding/encode-document "")})) (when-let [last-version-num (:version (get-last-document-version storage document-id repository))] (if (.versioned-repository? storage repository) (.run-query storage :create-document-frame! {:repository repository :document-id document-id :type "delete" :encoding "edn" :version (inc last-version-num) :reference nil :data (encoding/encode-document "")}) (.run-query storage :overwrite-document-frame! {:repository repository :document-id document-id :type "delete" :encoding "edn" :version last-version-num :reference nil :data (encoding/encode-document "")})))))