Skip to content
Snippets Groups Projects
documents.clj 5.57 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})]
    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 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)]
     (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 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))
                                (= (:data-type last-version) :delete))
                          (save-keyframe storage document-id document (:version last-version) 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]
  (.run-query storage :create-document-frame!
              {:repository repository
               :document-id document-id
               :type "delete"
               :encoding "edn"
               :version (inc (:version (get-last-document-version storage document-id repository)))
               :reference nil
               :data (encoding/encode-document "")}))