mirror of
https://github.com/logseq/logseq.git
synced 2026-05-17 17:32:35 +00:00
fix: delete recycled node cleanup
This commit is contained in:
77
deps/db/src/logseq/db/common/delete_blocks.cljs
vendored
77
deps/db/src/logseq/db/common/delete_blocks.cljs
vendored
@@ -45,35 +45,56 @@
|
||||
tx))
|
||||
refs)))
|
||||
|
||||
(defn- block-entity?
|
||||
[entity]
|
||||
(and (:block/uuid entity)
|
||||
(:block/page entity)
|
||||
(not (entity-util/page? entity))))
|
||||
|
||||
(defn- retracted-entities
|
||||
[db txs]
|
||||
(->> txs
|
||||
(keep (fn [tx]
|
||||
(when (and (vector? tx)
|
||||
(contains? #{:db.fn/retractEntity :db/retractEntity} (first tx)))
|
||||
(d/entity db (second tx)))))
|
||||
(common-util/distinct-by :db/id)))
|
||||
|
||||
(defn- direct-cleanup-tx
|
||||
[entities]
|
||||
(let [retracted-blocks (filter block-entity? entities)
|
||||
reaction-entities (->> entities
|
||||
(mapcat :logseq.property.reaction/_target)
|
||||
(common-util/distinct-by :db/id))
|
||||
retract-reactions-tx (map (fn [reaction] [:db/retractEntity (:db/id reaction)])
|
||||
reaction-entities)
|
||||
retracted-tx (build-retracted-tx retracted-blocks)
|
||||
history-entities (->> entities
|
||||
(mapcat (fn [entity]
|
||||
(concat (:logseq.property.history/_block entity)
|
||||
(:logseq.property.history/_ref-value entity))))
|
||||
(common-util/distinct-by :db/id))
|
||||
retract-history-tx (map (fn [history] [:db/retractEntity (:db/id history)])
|
||||
history-entities)
|
||||
delete-views (->> entities
|
||||
(mapcat :logseq.property/_view-for)
|
||||
(map (fn [view] [:db/retractEntity (:db/id view)])))]
|
||||
(vec (concat retracted-tx delete-views retract-history-tx retract-reactions-tx))))
|
||||
|
||||
(defn- build-cleanup-tx
|
||||
[db txs]
|
||||
(loop [pending-entities (retracted-entities db txs)
|
||||
seen-ids #{}
|
||||
cleanup-tx []]
|
||||
(if-let [entities (seq (remove #(contains? seen-ids (:db/id %))
|
||||
pending-entities))]
|
||||
(let [seen-ids' (into seen-ids (map :db/id) entities)
|
||||
next-tx (direct-cleanup-tx entities)]
|
||||
(recur (retracted-entities db next-tx) seen-ids' (into cleanup-tx next-tx)))
|
||||
(distinct cleanup-tx))))
|
||||
|
||||
(defn update-refs-history
|
||||
"When an entity is deleted, related property history, views and reactions
|
||||
are deleted"
|
||||
[db txs _opts]
|
||||
(let [retracted-ids (keep (fn [tx]
|
||||
(when (and (vector? tx)
|
||||
(contains? #{:db.fn/retractEntity :db/retractEntity} (first tx)))
|
||||
(second tx))) txs)
|
||||
retracted-entities (map #(d/entity db %) retracted-ids)]
|
||||
(when (seq retracted-ids)
|
||||
(let [retracted-blocks (remove entity-util/page? retracted-entities)
|
||||
reaction-entities (->> retracted-entities
|
||||
(mapcat :logseq.property.reaction/_target)
|
||||
(common-util/distinct-by :db/id))
|
||||
retract-reactions-tx (map (fn [reaction] [:db/retractEntity (:db/id reaction)])
|
||||
reaction-entities)
|
||||
retracted-tx (build-retracted-tx retracted-blocks)
|
||||
history-entities (->> retracted-entities
|
||||
(mapcat (fn [e]
|
||||
(concat (:logseq.property.history/_block e)
|
||||
(:logseq.property.history/_ref-value e))))
|
||||
(common-util/distinct-by :db/id))
|
||||
retract-history-tx (map (fn [history] [:db/retractEntity (:db/id history)])
|
||||
history-entities)
|
||||
delete-views (->>
|
||||
(mapcat
|
||||
(fn [item]
|
||||
(let [block (d/entity db (:db/id item))]
|
||||
(:logseq.property/_view-for block)))
|
||||
retracted-entities)
|
||||
(map (fn [b] [:db/retractEntity (:db/id b)])))]
|
||||
(concat retracted-tx delete-views retract-history-tx retract-reactions-tx)))))
|
||||
(seq (build-cleanup-tx db txs)))
|
||||
|
||||
@@ -69,3 +69,38 @@
|
||||
history-entity (d/entity @conn [:block/uuid history-uuid])]
|
||||
(ldb/transact! conn [[:db/retractEntity (:db/id page)]])
|
||||
(is (nil? (d/entity @conn (:db/id history-entity)))))))
|
||||
|
||||
(deftest delete-blocks-removes-history-for-corresponding-views
|
||||
(testing "property history entries attached to a deleted block's view are retracted"
|
||||
(let [conn (db-test/create-conn-with-blocks
|
||||
{:pages-and-blocks
|
||||
[{:page {:block/title "Page"}
|
||||
:blocks [{:block/title "Target block"}]}]})
|
||||
target-block (db-test/find-block-by-content @conn "Target block")
|
||||
view-uuid (random-uuid)
|
||||
target-history-uuid (random-uuid)
|
||||
view-history-uuid (random-uuid)
|
||||
now (common-util/time-ms)
|
||||
_ (d/transact! conn [{:block/uuid view-uuid
|
||||
:block/title "Target view"
|
||||
:block/created-at now
|
||||
:block/updated-at now
|
||||
:logseq.property/view-for (:db/id target-block)
|
||||
:logseq.property.view/type :logseq.property.view/type.table
|
||||
:logseq.property.view/feature-type :linked-references}
|
||||
{:block/uuid target-history-uuid
|
||||
:block/created-at now
|
||||
:block/updated-at now
|
||||
:logseq.property.history/block (:db/id target-block)
|
||||
:logseq.property.history/property (:db/id (d/entity @conn :logseq.property/status))
|
||||
:logseq.property.history/scalar-value "Todo"}
|
||||
{:block/uuid view-history-uuid
|
||||
:block/created-at now
|
||||
:block/updated-at now
|
||||
:logseq.property.history/block [:block/uuid view-uuid]
|
||||
:logseq.property.history/property (:db/id (d/entity @conn :logseq.property/status))
|
||||
:logseq.property.history/scalar-value "List"}])]
|
||||
(ldb/transact! conn [[:db/retractEntity (:db/id target-block)]])
|
||||
(is (nil? (d/entity @conn [:block/uuid view-uuid])))
|
||||
(is (nil? (d/entity @conn [:block/uuid target-history-uuid])))
|
||||
(is (nil? (d/entity @conn [:block/uuid view-history-uuid]))))))
|
||||
|
||||
29
deps/outliner/src/logseq/outliner/recycle.cljs
vendored
29
deps/outliner/src/logseq/outliner/recycle.cljs
vendored
@@ -4,6 +4,7 @@
|
||||
[logseq.common.util :as common-util]
|
||||
[logseq.common.uuid :as common-uuid]
|
||||
[logseq.db :as ldb]
|
||||
[logseq.db.common.delete-blocks :as delete-blocks]
|
||||
[logseq.db.common.initial-data :as common-initial-data]
|
||||
[logseq.db.common.order :as db-order]))
|
||||
|
||||
@@ -105,6 +106,10 @@
|
||||
(#(d/entity db [:block/uuid %]))
|
||||
:db/id))
|
||||
|
||||
(defn- with-delete-cleanup-tx
|
||||
[db tx-data]
|
||||
(distinct (concat tx-data (delete-blocks/update-refs-history db tx-data {}))))
|
||||
|
||||
(defn recycle-blocks-tx-data
|
||||
[db blocks {:keys [deleted-by-uuid now-ms]}]
|
||||
(let [{:keys [page page-id tx-data]} (ensure-recycle-page db)
|
||||
@@ -232,15 +237,17 @@
|
||||
(defn ^:api permanently-delete-tx-data
|
||||
[db root]
|
||||
(when (and root (recycled? root))
|
||||
(->> (if (ldb/page? root)
|
||||
(keep (fn [id]
|
||||
(some-> (d/entity db id) :block/uuid))
|
||||
(page-tree-ids db root))
|
||||
(keep :block/uuid (block-subtree db root)))
|
||||
(map (fn [block-uuid]
|
||||
[:db/retractEntity [:block/uuid block-uuid]]))
|
||||
distinct
|
||||
seq)))
|
||||
(some->> (if (ldb/page? root)
|
||||
(keep (fn [id]
|
||||
(some-> (d/entity db id) :block/uuid))
|
||||
(page-tree-ids db root))
|
||||
(keep :block/uuid (block-subtree db root)))
|
||||
(map (fn [block-uuid]
|
||||
[:db/retractEntity [:block/uuid block-uuid]]))
|
||||
distinct
|
||||
seq
|
||||
(with-delete-cleanup-tx db)
|
||||
seq)))
|
||||
|
||||
(defn ^:api permanently-delete!
|
||||
[conn root-uuid]
|
||||
@@ -265,7 +272,9 @@
|
||||
(if (ldb/page? entity)
|
||||
(map (fn [id] [:db/retractEntity id]) (page-tree-ids db entity))
|
||||
(map (fn [node] [:db/retractEntity (:db/id node)]) (block-subtree db entity)))))
|
||||
distinct)))
|
||||
distinct
|
||||
seq
|
||||
(with-delete-cleanup-tx db))))
|
||||
|
||||
(defn ^:api gc!
|
||||
[conn opts]
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
(ns logseq.outliner.recycle-test
|
||||
(:require [cljs.test :refer [deftest is]]
|
||||
[datascript.core :as d]
|
||||
[logseq.common.util :as common-util]
|
||||
[logseq.db :as ldb]
|
||||
[logseq.db.test.helper :as db-test]
|
||||
[logseq.outliner.recycle :as recycle]))
|
||||
@@ -48,3 +49,39 @@
|
||||
(is (some? (d/entity @conn [:block/uuid (:block/uuid page)])))
|
||||
(is (nil? (d/entity @conn [:block/uuid parent-uuid])))
|
||||
(is (nil? (d/entity @conn [:block/uuid child-uuid])))))
|
||||
|
||||
(deftest permanently-delete-recycled-block-removes-corresponding-view-history
|
||||
(let [conn (db-test/create-conn-with-blocks
|
||||
[{:page {:block/title "page1"}
|
||||
:blocks [{:block/title "target"}]}])
|
||||
target (db-test/find-block-by-content @conn "target")
|
||||
target-uuid (:block/uuid target)
|
||||
view-uuid (random-uuid)
|
||||
target-history-uuid (random-uuid)
|
||||
view-history-uuid (random-uuid)
|
||||
now (common-util/time-ms)
|
||||
_ (d/transact! conn [{:block/uuid view-uuid
|
||||
:block/title "target view"
|
||||
:block/created-at now
|
||||
:block/updated-at now
|
||||
:logseq.property/view-for (:db/id target)
|
||||
:logseq.property.view/type :logseq.property.view/type.table
|
||||
:logseq.property.view/feature-type :linked-references}
|
||||
{:block/uuid target-history-uuid
|
||||
:block/created-at now
|
||||
:block/updated-at now
|
||||
:logseq.property.history/block (:db/id target)
|
||||
:logseq.property.history/property (:db/id (d/entity @conn :logseq.property/status))
|
||||
:logseq.property.history/scalar-value "Todo"}
|
||||
{:block/uuid view-history-uuid
|
||||
:block/created-at now
|
||||
:block/updated-at now
|
||||
:logseq.property.history/block [:block/uuid view-uuid]
|
||||
:logseq.property.history/property (:db/id (d/entity @conn :logseq.property/status))
|
||||
:logseq.property.history/scalar-value "List"}])]
|
||||
(ldb/transact! conn (recycle/recycle-blocks-tx-data @conn [target] {}) {:outliner-op :delete-blocks})
|
||||
(is (true? (recycle/permanently-delete! conn target-uuid)))
|
||||
(is (nil? (d/entity @conn [:block/uuid target-uuid])))
|
||||
(is (nil? (d/entity @conn [:block/uuid view-uuid])))
|
||||
(is (nil? (d/entity @conn [:block/uuid target-history-uuid])))
|
||||
(is (nil? (d/entity @conn [:block/uuid view-history-uuid])))))
|
||||
|
||||
Reference in New Issue
Block a user