Skip to content
Snippets Groups Projects
documents.clj 7.97 KiB
Newer Older
(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})]
(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"
                 :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 "")})))))