mirror of
https://github.com/logseq/logseq.git
synced 2026-05-01 17:36:33 +00:00
Merge branch 'feat/db' into perf/app-start
This commit is contained in:
28
deps/db/src/logseq/db/frontend/property.cljs
vendored
28
deps/db/src/logseq/db/frontend/property.cljs
vendored
@@ -430,6 +430,9 @@
|
||||
{:type :coll
|
||||
:hide? true
|
||||
:public? false}
|
||||
;; ignore this property when rtc,
|
||||
;; since users frequently click the sort button to view table content temporarily,
|
||||
;; but this action does not need to be synchronized with other clients.
|
||||
:rtc {:rtc/ignore-attr-when-init-upload true
|
||||
:rtc/ignore-attr-when-init-download true
|
||||
:rtc/ignore-attr-when-syncing true}}
|
||||
@@ -438,47 +441,32 @@
|
||||
:schema
|
||||
{:type :map
|
||||
:hide? true
|
||||
:public? false}
|
||||
:rtc {:rtc/ignore-attr-when-init-upload true
|
||||
:rtc/ignore-attr-when-init-download true
|
||||
:rtc/ignore-attr-when-syncing true}}
|
||||
:public? false}}
|
||||
|
||||
:logseq.property.table/hidden-columns {:title "View hidden columns"
|
||||
:schema
|
||||
{:type :keyword
|
||||
:cardinality :many
|
||||
:hide? true
|
||||
:public? false}
|
||||
:rtc {:rtc/ignore-attr-when-init-upload true
|
||||
:rtc/ignore-attr-when-init-download true
|
||||
:rtc/ignore-attr-when-syncing true}}
|
||||
:public? false}}
|
||||
|
||||
:logseq.property.table/ordered-columns {:title "View ordered columns"
|
||||
:schema
|
||||
{:type :coll
|
||||
:hide? true
|
||||
:public? false}
|
||||
:rtc {:rtc/ignore-attr-when-init-upload true
|
||||
:rtc/ignore-attr-when-init-download true
|
||||
:rtc/ignore-attr-when-syncing true}}
|
||||
:public? false}}
|
||||
|
||||
:logseq.property.table/sized-columns {:title "View columns settings"
|
||||
:schema
|
||||
{:type :map
|
||||
:hide? true
|
||||
:public? false}
|
||||
:rtc {:rtc/ignore-attr-when-init-upload true
|
||||
:rtc/ignore-attr-when-init-download true
|
||||
:rtc/ignore-attr-when-syncing true}}
|
||||
:public? false}}
|
||||
:logseq.property.table/pinned-columns {:title "Table view pinned columns"
|
||||
:schema
|
||||
{:type :property
|
||||
:cardinality :many
|
||||
:hide? true
|
||||
:public? false}
|
||||
:rtc {:rtc/ignore-attr-when-init-upload true
|
||||
:rtc/ignore-attr-when-init-download true
|
||||
:rtc/ignore-attr-when-syncing true}}
|
||||
:public? false}}
|
||||
:logseq.property/view-for {:title "This view belongs to"
|
||||
:schema
|
||||
{:type :node
|
||||
|
||||
@@ -65,26 +65,32 @@
|
||||
(into [property-tx]
|
||||
(closed-values->blocks property))))
|
||||
|
||||
(defn- build-property-value-block
|
||||
(defn build-property-value-block
|
||||
"Builds a property value entity given a block map/entity, a property entity or
|
||||
ident and its property value"
|
||||
[block property value & {:keys [block-uuid]}]
|
||||
ident and its property value. Takes the following options:
|
||||
* :block-uuid - :block/uuid for property value entity
|
||||
* :properties - Additional properties and attributes to add to entity"
|
||||
[block property value & {:keys [block-uuid properties]}]
|
||||
(let [block-id (or (:db/id block) (:db/ident block))]
|
||||
(-> (merge
|
||||
{:block/uuid (or block-uuid (common-uuid/gen-uuid))
|
||||
:block/page (if (:block/page block)
|
||||
(:db/id (:block/page block))
|
||||
(cond->
|
||||
(merge
|
||||
{:block/uuid (or block-uuid (common-uuid/gen-uuid))
|
||||
:block/page (if (:block/page block)
|
||||
(:db/id (:block/page block))
|
||||
;; page block
|
||||
block-id)
|
||||
:block/parent block-id
|
||||
:logseq.property/created-from-property (if (= (:db/ident property) :logseq.property/default-value)
|
||||
block-id
|
||||
(or (:db/id property) {:db/ident (:db/ident property)}))
|
||||
:block/order (db-order/gen-key)}
|
||||
(if (db-property-type/property-value-content? (:logseq.property/type property) property)
|
||||
{:logseq.property/value value}
|
||||
{:block/title value}))
|
||||
common-util/block-with-timestamps)))
|
||||
block-id)
|
||||
:block/parent block-id
|
||||
:logseq.property/created-from-property (if (= (:db/ident property) :logseq.property/default-value)
|
||||
block-id
|
||||
(or (:db/id property) {:db/ident (:db/ident property)}))
|
||||
:block/order (db-order/gen-key)}
|
||||
(if (db-property-type/property-value-content? (:logseq.property/type property) property)
|
||||
{:logseq.property/value value}
|
||||
{:block/title value}))
|
||||
true
|
||||
common-util/block-with-timestamps
|
||||
properties
|
||||
(merge properties))))
|
||||
|
||||
(defn build-property-values-tx-m
|
||||
"Builds a map of property names to their property value blocks to be
|
||||
@@ -99,7 +105,7 @@
|
||||
(let [block' (if (:db/id block) block (assoc block :db/id [:block/uuid (:block/uuid block)]))]
|
||||
(->> properties
|
||||
(map (fn [[k v]]
|
||||
(let [property-map (if (map? k) k {:db/ident k})
|
||||
(let [{:keys [property-value-properties] :as property-map} (if (map? k) k {:db/ident k})
|
||||
gen-uuid-value-prefix (when pure?
|
||||
(or (:db/ident block) (:block/uuid block)))]
|
||||
(assert (:db/ident property-map) "Key in map must have a :db/ident")
|
||||
@@ -108,15 +114,20 @@
|
||||
(if (set? v)
|
||||
(set (map #(build-property-value-block
|
||||
block' property-map %
|
||||
(when pure?
|
||||
{:block-uuid
|
||||
(common-uuid/gen-uuid :builtin-block-uuid (str gen-uuid-value-prefix "-" %))}))
|
||||
(cond-> {}
|
||||
property-value-properties
|
||||
(assoc :properties property-value-properties)
|
||||
pure?
|
||||
(assoc :block-uuid
|
||||
(common-uuid/gen-uuid :builtin-block-uuid (str gen-uuid-value-prefix "-" %)))))
|
||||
v))
|
||||
(build-property-value-block block' property-map v
|
||||
(when pure?
|
||||
{:block-uuid
|
||||
(common-uuid/gen-uuid
|
||||
:builtin-block-uuid (str gen-uuid-value-prefix "-" v))})))])))
|
||||
(cond-> {}
|
||||
property-value-properties
|
||||
(assoc :properties property-value-properties)
|
||||
pure?
|
||||
(assoc :block-uuid
|
||||
(common-uuid/gen-uuid :builtin-block-uuid (str gen-uuid-value-prefix "-" v))))))])))
|
||||
(into {}))))
|
||||
|
||||
(defn build-properties-with-ref-values
|
||||
|
||||
58
deps/db/src/logseq/db/sqlite/build.cljs
vendored
58
deps/db/src/logseq/db/sqlite/build.cljs
vendored
@@ -99,6 +99,30 @@
|
||||
"Provides the next temp :db/id to use in a create-graph transact!"
|
||||
#(swap! current-db-id dec))
|
||||
|
||||
(defn- build-property-map-for-pvalue-tx
|
||||
"Returns a property map if the given property pair should have a property value entity constructured
|
||||
or nil if it should not. Property maps must at least contain the :db/ident and :logseq.property/type keys"
|
||||
[k v new-block properties-config all-idents]
|
||||
(if-let [built-in-type (get-in db-property/built-in-properties [k :schema :type])]
|
||||
(if (and (db-property-type/value-ref-property-types built-in-type)
|
||||
;; closed values are referenced by their :db/ident so no need to create values
|
||||
(not (get-in db-property/built-in-properties [k :closed-values])))
|
||||
{:db/ident k
|
||||
:logseq.property/type built-in-type}
|
||||
(when-let [built-in-type' (get (or (:build/properties-ref-types new-block)
|
||||
;; Reasonable default for properties like logseq.property/default-value
|
||||
{:entity :number})
|
||||
built-in-type)]
|
||||
{:db/ident k
|
||||
:logseq.property/type built-in-type'}))
|
||||
(when (and (db-property-type/value-ref-property-types (get-in properties-config [k :logseq.property/type]))
|
||||
;; Don't build property value entity if values are :block/uuid refs
|
||||
(if (set? v) (not (vector? (first v))) (not (vector? v))))
|
||||
(let [prop-type (get-in properties-config [k :logseq.property/type])]
|
||||
{:db/ident (get-ident all-idents k)
|
||||
:original-property-id k
|
||||
:logseq.property/type prop-type}))))
|
||||
|
||||
(defn- ->property-value-tx-m
|
||||
"Given a new block and its properties, creates a map of properties which have values of property value tx.
|
||||
This map is used for both creating the new property values and then adding them to a block.
|
||||
@@ -106,28 +130,18 @@
|
||||
[new-block properties properties-config all-idents]
|
||||
(->> properties
|
||||
(keep (fn [[k v]]
|
||||
(if-let [built-in-type (get-in db-property/built-in-properties [k :schema :type])]
|
||||
(if (and (db-property-type/value-ref-property-types built-in-type)
|
||||
;; closed values are referenced by their :db/ident so no need to create values
|
||||
(not (get-in db-property/built-in-properties [k :closed-values])))
|
||||
(let [property-map {:db/ident k
|
||||
:logseq.property/type built-in-type}]
|
||||
[property-map v])
|
||||
(when-let [built-in-type' (get (or (:build/properties-ref-types new-block)
|
||||
;; Reasonable default for properties like logseq.property/default-value
|
||||
{:entity :number})
|
||||
built-in-type)]
|
||||
(let [property-map {:db/ident k
|
||||
:logseq.property/type built-in-type'}]
|
||||
[property-map v])))
|
||||
(when (and (db-property-type/value-ref-property-types (get-in properties-config [k :logseq.property/type]))
|
||||
;; TODO: Support translate-property-value without this hack
|
||||
(not (vector? v)))
|
||||
(let [prop-type (get-in properties-config [k :logseq.property/type])
|
||||
property-map {:db/ident (get-ident all-idents k)
|
||||
:original-property-id k
|
||||
:logseq.property/type prop-type}]
|
||||
[property-map v])))))
|
||||
(when-let [property-map (build-property-map-for-pvalue-tx k v new-block properties-config all-idents)]
|
||||
[(let [pvalue-attrs (when (:build/property-value v)
|
||||
(merge (:build/properties v)
|
||||
{:block/tags (mapv #(hash-map :db/ident (get-ident all-idents %))
|
||||
(:build/tags v))}
|
||||
(select-keys v [:block/created-at :block/updated-at])))]
|
||||
(cond-> property-map
|
||||
(and (:build/property-value v) (seq pvalue-attrs))
|
||||
(assoc :property-value-properties pvalue-attrs)))
|
||||
(if (:build/property-value v)
|
||||
(or (:logseq.property/value v) (:block/title v))
|
||||
v)])))
|
||||
(db-property-build/build-property-values-tx-m new-block)))
|
||||
|
||||
(defn- extract-basic-content-refs
|
||||
|
||||
191
deps/db/src/logseq/db/sqlite/export.cljs
vendored
191
deps/db/src/logseq/db/sqlite/export.cljs
vendored
@@ -12,7 +12,8 @@
|
||||
[logseq.db.frontend.entity-plus :as entity-plus]
|
||||
[logseq.db.frontend.entity-util :as entity-util]
|
||||
[logseq.db.frontend.property :as db-property]
|
||||
[logseq.db.sqlite.build :as sqlite-build]))
|
||||
[logseq.db.sqlite.build :as sqlite-build]
|
||||
[medley.core :as medley]))
|
||||
|
||||
;; Export fns
|
||||
;; ==========
|
||||
@@ -37,54 +38,84 @@
|
||||
{:build/journal (:block/journal-day page-entity)}
|
||||
{:block/title (block-title page-entity)}))
|
||||
|
||||
(defn- buildable-property-value-entity
|
||||
"Converts property value to a buildable version"
|
||||
[property-ent pvalue {:keys [property-value-uuids?]}]
|
||||
(cond (and (not property-value-uuids?) (ldb/internal-page? pvalue))
|
||||
(defn- build-pvalue-entity-for-build-page
|
||||
[pvalue]
|
||||
(cond (ldb/internal-page? pvalue)
|
||||
;; Should page properties be pulled here?
|
||||
[:build/page (cond-> (shallow-copy-page pvalue)
|
||||
(seq (:block/tags pvalue))
|
||||
(assoc :build/tags (->build-tags (:block/tags pvalue))))]
|
||||
(and (not property-value-uuids?) (entity-util/journal? pvalue))
|
||||
[:build/page {:build/journal (:block/journal-day pvalue)}]
|
||||
:else
|
||||
(if (contains? #{:node :date} (:logseq.property/type property-ent))
|
||||
;; Idents take precedence over uuid because they are keep data graph-agnostic
|
||||
(if (:db/ident pvalue)
|
||||
(:db/ident pvalue)
|
||||
;; Use metadata distinguish from block references that don't exist like closed values
|
||||
^::existing-property-value? [:block/uuid (:block/uuid pvalue)])
|
||||
(or (:db/ident pvalue)
|
||||
;; nbb-compatible version of db-property/property-value-content
|
||||
(or (block-title pvalue)
|
||||
(:logseq.property/value pvalue))))))
|
||||
(entity-util/journal? pvalue)
|
||||
[:build/page {:build/journal (:block/journal-day pvalue)}]))
|
||||
|
||||
(defn- build-pvalue-entity-default [ent-properties pvalue options]
|
||||
(if (or (seq ent-properties) (seq (:block/tags pvalue)))
|
||||
(cond-> {:build/property-value :block
|
||||
:block/title (or (block-title pvalue)
|
||||
(:logseq.property/value pvalue))}
|
||||
(seq (:block/tags pvalue))
|
||||
(assoc :build/tags (->build-tags (:block/tags pvalue)))
|
||||
|
||||
(seq ent-properties)
|
||||
(assoc :build/properties ent-properties)
|
||||
|
||||
(:include-timestamps? options)
|
||||
(merge (select-keys pvalue [:block/created-at :block/updated-at])))
|
||||
;; nbb-compatible version of db-property/property-value-content
|
||||
(or (block-title pvalue)
|
||||
(:logseq.property/value pvalue))))
|
||||
|
||||
(defn- buildable-properties
|
||||
"Originally copied from db-test/readable-properties. Modified so that property values are
|
||||
valid sqlite.build EDN"
|
||||
[db ent-properties properties-config options]
|
||||
(->> ent-properties
|
||||
(map (fn [[k v]]
|
||||
[k
|
||||
(if (and (:block/closed-value-property v) (not (db-property/logseq-property? k)))
|
||||
(if-let [closed-uuid (some #(when (= (:value %) (db-property/property-value-content v))
|
||||
(:uuid %))
|
||||
(get-in properties-config [k :build/closed-values]))]
|
||||
[:block/uuid closed-uuid]
|
||||
(throw (ex-info (str "No closed value found for content: " (pr-str (db-property/property-value-content v))) {:properties properties-config})))
|
||||
(cond
|
||||
(de/entity? v)
|
||||
(buildable-property-value-entity (d/entity db k) v options)
|
||||
(and (set? v) (every? de/entity? v))
|
||||
(let [property-ent (d/entity db k)]
|
||||
(set (map #(buildable-property-value-entity property-ent % options) v)))
|
||||
:else
|
||||
v))]))
|
||||
(into {})))
|
||||
(letfn [(build-pvalue-entity
|
||||
[db' property-ent pvalue properties-config' {:keys [property-value-uuids?] :as options'}]
|
||||
(if-let [build-page (and (not property-value-uuids?) (build-pvalue-entity-for-build-page pvalue))]
|
||||
build-page
|
||||
(if (contains? #{:node :date} (:logseq.property/type property-ent))
|
||||
;; Idents take precedence over uuid because they are keep data graph-agnostic
|
||||
(if (:db/ident pvalue)
|
||||
(:db/ident pvalue)
|
||||
;; Use metadata distinguish from block references that don't exist like closed values
|
||||
^::existing-property-value? [:block/uuid (:block/uuid pvalue)])
|
||||
(or (:db/ident pvalue)
|
||||
(let [ent-properties* (->> (apply dissoc (db-property/properties pvalue)
|
||||
:logseq.property/value :logseq.property/created-from-property
|
||||
db-property/public-db-attribute-properties)
|
||||
;; TODO: Allow user properties when sqlite.build supports it
|
||||
(medley/filter-keys db-property/internal-property?))
|
||||
ent-properties (when (and (not (:block/closed-value-property pvalue)) (seq ent-properties*))
|
||||
(buildable-properties db' ent-properties* properties-config' options'))]
|
||||
(build-pvalue-entity-default ent-properties pvalue options'))))))]
|
||||
(->> ent-properties
|
||||
(map (fn [[k v]]
|
||||
[k
|
||||
;; handle user closed value properties. built-ins have idents and shouldn't be handled here
|
||||
(if (and (not (db-property/logseq-property? k))
|
||||
(or (:block/closed-value-property v)
|
||||
(and (set? v) (:block/closed-value-property (first v)))))
|
||||
(let [find-closed-uuid (fn [val]
|
||||
(or (some #(when (= (:value %) (db-property/property-value-content val))
|
||||
(:uuid %))
|
||||
(get-in properties-config [k :build/closed-values]))
|
||||
(throw (ex-info (str "No closed value found for content: " (pr-str (db-property/property-value-content val))) {:properties properties-config}))))]
|
||||
(if (set? v)
|
||||
(set (map #(vector :block/uuid (find-closed-uuid %)) v))
|
||||
[:block/uuid (find-closed-uuid v)]))
|
||||
(cond
|
||||
(de/entity? v)
|
||||
(build-pvalue-entity db (d/entity db k) v properties-config options)
|
||||
(and (set? v) (every? de/entity? v))
|
||||
(let [property-ent (d/entity db k)]
|
||||
(set (map #(build-pvalue-entity db property-ent % properties-config options) v)))
|
||||
:else
|
||||
v))]))
|
||||
(into {}))))
|
||||
|
||||
(defn- build-export-properties
|
||||
"The caller of this fn is responsible for building :build/:property-classes unless shallow-copy?"
|
||||
[db user-property-idents {:keys [include-properties? include-timestamps? include-uuid? shallow-copy?] :as options}]
|
||||
[db user-property-idents {:keys [include-properties? include-timestamps? include-uuid? shallow-copy? include-alias?] :as options}]
|
||||
(let [properties-config-by-ent
|
||||
(->> user-property-idents
|
||||
(map (fn [ident]
|
||||
@@ -98,6 +129,8 @@
|
||||
(assoc :block/uuid (:block/uuid property) :build/keep-uuid? true)
|
||||
include-timestamps?
|
||||
(merge (select-keys property [:block/created-at :block/updated-at]))
|
||||
(and (not shallow-copy?) include-alias? (:block/alias property))
|
||||
(assoc :block/alias (set (map #(vector :block/uuid (:block/uuid %)) (:block/alias property))))
|
||||
(and (not shallow-copy?) (:logseq.property/classes property))
|
||||
(assoc :build/property-classes (mapv :db/ident (:logseq.property/classes property)))
|
||||
(seq closed-values)
|
||||
@@ -129,8 +162,7 @@
|
||||
(defn- build-export-class
|
||||
"The caller of this fn is responsible for building any classes or properties from this fn
|
||||
unless shallow-copy?"
|
||||
[class-ent {:keys [include-parents? include-uuid? shallow-copy? include-timestamps?]
|
||||
:or {include-parents? true}}]
|
||||
[class-ent {:keys [include-uuid? shallow-copy? include-timestamps? include-alias?]}]
|
||||
(cond-> (select-keys class-ent [:block/title :block/collapsed?])
|
||||
include-uuid?
|
||||
(assoc :block/uuid (:block/uuid class-ent) :build/keep-uuid? true)
|
||||
@@ -139,11 +171,10 @@
|
||||
(and (:logseq.property.class/properties class-ent) (not shallow-copy?))
|
||||
(assoc :build/class-properties
|
||||
(mapv :db/ident (:logseq.property.class/properties class-ent)))
|
||||
(and (not shallow-copy?) (:block/alias class-ent))
|
||||
(and (not shallow-copy?) include-alias? (:block/alias class-ent))
|
||||
(assoc :block/alias (set (map #(vector :block/uuid (:block/uuid %)) (:block/alias class-ent))))
|
||||
;; It's caller's responsibility to ensure parent is included in final export
|
||||
(and include-parents?
|
||||
(not shallow-copy?)
|
||||
(and (not shallow-copy?)
|
||||
(:logseq.property/parent class-ent)
|
||||
(not= :logseq.class/Root (:db/ident (:logseq.property/parent class-ent))))
|
||||
(assoc :build/class-parent
|
||||
@@ -364,12 +395,26 @@
|
||||
:classes (apply merge (map :classes uuid-block-pages))
|
||||
:pages-and-blocks (mapv #(select-keys % [:page :blocks]) uuid-block-pages)}))
|
||||
|
||||
(defn sort-pages-and-blocks
|
||||
"Provide a reliable sort order since this tends to be large. Helps with diffing
|
||||
and readability"
|
||||
[pages-and-blocks]
|
||||
(vec
|
||||
(sort-by #(or (get-in % [:page :block/title])
|
||||
(some-> (get-in % [:page :build/journal]) str)
|
||||
(str (get-in % [:page :block/uuid])))
|
||||
pages-and-blocks)))
|
||||
|
||||
(defn- finalize-export-maps
|
||||
"Given final export maps, merges them, adds any missing class parents and merges those in"
|
||||
"Given final export maps, merges them, adds any missing class parents and merges those in.
|
||||
If :pages-and-blocks exist, sorts them in order to have reliable sort order"
|
||||
[db & export-maps]
|
||||
(let [final-export* (apply merge-export-maps export-maps)
|
||||
class-parents-export (some->> (:classes final-export*) (build-class-parents-export db))]
|
||||
(merge-export-maps final-export* class-parents-export)))
|
||||
class-parents-export (some->> (:classes final-export*) (build-class-parents-export db))
|
||||
merged-map (merge-export-maps final-export* class-parents-export)]
|
||||
(cond-> merged-map
|
||||
(:pages-and-blocks merged-map)
|
||||
(update :pages-and-blocks sort-pages-and-blocks))))
|
||||
|
||||
(defn- build-block-export
|
||||
"Exports block for given block eid"
|
||||
@@ -387,7 +432,7 @@
|
||||
(merge {::block (:node node-export)}
|
||||
block-export)))
|
||||
|
||||
(defn- build-page-blocks-export [db page-entity {:keys [properties classes blocks ontology-page?] :as options}]
|
||||
(defn- build-page-blocks-export [db page-entity {:keys [properties classes blocks ontology-page? include-alias?] :as options}]
|
||||
(let [options' (cond-> (dissoc options :classes :blocks :graph-ontology)
|
||||
(:exclude-ontology? options)
|
||||
(assoc :properties (get-in options [:graph-ontology :properties])))
|
||||
@@ -400,7 +445,7 @@
|
||||
(:node page-ent-export)
|
||||
(merge (dissoc (:node page-ent-export) :block/title)
|
||||
(shallow-copy-page page-entity)
|
||||
(when (:block/alias page-entity)
|
||||
(when (and include-alias? (:block/alias page-entity))
|
||||
{:block/alias (set (map #(vector :block/uuid (:block/uuid %)) (:block/alias page-entity)))})))
|
||||
page-blocks-export {:pages-and-blocks [{:page page :blocks blocks}]
|
||||
:properties properties
|
||||
@@ -439,7 +484,9 @@
|
||||
page-export (finalize-export-maps db page-export* uuid-block-export content-ref-export)]
|
||||
page-export))
|
||||
|
||||
(defn build-view-nodes-export* [db nodes opts]
|
||||
(defn- build-nodes-export
|
||||
"Export a mix of pages and blocks"
|
||||
[db nodes opts]
|
||||
(let [node-pages (filter entity-util/page? nodes)
|
||||
pages-export
|
||||
(merge
|
||||
@@ -452,9 +499,7 @@
|
||||
(->> node-blocks
|
||||
(group-by :block/page)
|
||||
(map (fn [[parent-page-ent blocks]]
|
||||
(merge (build-blocks-export db
|
||||
(sort-by :block/order blocks)
|
||||
(merge opts {:include-children? false}))
|
||||
(merge (build-blocks-export db (sort-by :block/order blocks) opts)
|
||||
{:page (shallow-copy-page parent-page-ent)}))))
|
||||
pages-to-blocks-export
|
||||
{:properties (apply merge (map :properties pages-to-blocks))
|
||||
@@ -474,7 +519,29 @@
|
||||
{:keys [content-ref-uuids content-ref-ents] :as content-ref-export}
|
||||
(build-content-ref-export db (into nodes property-value-ents))
|
||||
{:keys [pvalue-uuids] :as nodes-export}
|
||||
(build-view-nodes-export* db nodes {:include-uuid-fn content-ref-uuids})
|
||||
(build-nodes-export db nodes {:include-uuid-fn content-ref-uuids :include-children? false})
|
||||
uuid-block-export (build-uuid-block-export db pvalue-uuids content-ref-ents {})
|
||||
view-nodes-export (finalize-export-maps db nodes-export uuid-block-export content-ref-export)]
|
||||
view-nodes-export))
|
||||
|
||||
(defn- build-selected-nodes-export
|
||||
"Exports given nodes selected by a user. Nodes can be a mix of blocks and pages"
|
||||
[db eids]
|
||||
(let [top-level-nodes (map #(d/entity db %) eids)
|
||||
children-nodes (->> top-level-nodes
|
||||
;; Remove pages b/c when selected their children are not highlighted
|
||||
(remove entity-util/page?)
|
||||
(mapcat #(rest (ldb/get-block-and-children db (:block/uuid %))))
|
||||
(remove :logseq.property/created-from-property))
|
||||
nodes (concat top-level-nodes children-nodes)
|
||||
property-value-ents (mapcat #(->> (apply dissoc (db-property/properties %) db-property/public-db-attribute-properties)
|
||||
vals
|
||||
(filter de/entity?))
|
||||
nodes)
|
||||
{:keys [content-ref-uuids content-ref-ents] :as content-ref-export}
|
||||
(build-content-ref-export db (into nodes property-value-ents))
|
||||
{:keys [pvalue-uuids] :as nodes-export}
|
||||
(build-nodes-export db nodes {:include-uuid-fn content-ref-uuids :include-children? true})
|
||||
uuid-block-export (build-uuid-block-export db pvalue-uuids content-ref-ents {})
|
||||
view-nodes-export (finalize-export-maps db nodes-export uuid-block-export content-ref-export)]
|
||||
view-nodes-export))
|
||||
@@ -607,16 +674,6 @@
|
||||
:blocks (sqlite-build/update-each-block blocks remove-uuid-if-not-ref)})
|
||||
pages-and-blocks))))))
|
||||
|
||||
(defn sort-pages-and-blocks
|
||||
"Provide a reliable sort order since this tends to be large. Helps with diffing
|
||||
and readability"
|
||||
[pages-and-blocks]
|
||||
(vec
|
||||
(sort-by #(or (get-in % [:page :block/title])
|
||||
(some-> (get-in % [:page :build/journal]) str)
|
||||
(str (get-in % [:page :block/uuid])))
|
||||
pages-and-blocks)))
|
||||
|
||||
(defn- add-ontology-for-include-namespaces
|
||||
"Adds :properties to export for given namespace parents. Current use case is for :exclude-namespaces
|
||||
so no need to add :classes yet"
|
||||
@@ -644,7 +701,8 @@
|
||||
* :exclude-built-in-pages? - When set, built-in pages are excluded from export
|
||||
* :exclude-files? - When set, files are excluded from export"
|
||||
[db {:keys [exclude-files?] :as options*}]
|
||||
(let [options (merge options* {:property-value-uuids? true})
|
||||
(let [options (merge options* {:property-value-uuids? true
|
||||
:include-alias? true})
|
||||
content-ref-uuids (get-graph-content-ref-uuids db options)
|
||||
ontology-options (merge options {:include-uuid? true})
|
||||
ontology-export (build-graph-ontology-export db ontology-options)
|
||||
@@ -702,7 +760,10 @@
|
||||
;; Only looks one-level deep in properties e.g. not inside :build/page
|
||||
;; Doesn't find :block/link refs
|
||||
ref-uuids
|
||||
(->> (concat (mapcat get-pvalue-uuids (vals classes))
|
||||
(->> (concat (mapcat #(map second (:block/alias %)) (vals classes))
|
||||
(mapcat #(map second (:block/alias %)) (vals properties))
|
||||
(mapcat #(map second (:block/alias (:page %))) pages-and-blocks)
|
||||
(mapcat get-pvalue-uuids (vals classes))
|
||||
(mapcat get-pvalue-uuids (vals properties))
|
||||
(mapcat (comp get-pvalue-uuids :page) pages-and-blocks)
|
||||
(mapcat #(sqlite-build/extract-from-blocks (:blocks %) get-pvalue-uuids) pages-and-blocks))
|
||||
@@ -736,6 +797,8 @@
|
||||
(build-page-export db (:page-id options))
|
||||
:view-nodes
|
||||
(build-view-nodes-export db (:node-ids options))
|
||||
:selected-nodes
|
||||
(build-selected-nodes-export db (:node-ids options))
|
||||
:graph-ontology
|
||||
(build-graph-ontology-export db {})
|
||||
:graph
|
||||
|
||||
41
deps/db/test/logseq/db/sqlite/build_test.cljs
vendored
41
deps/db/test/logseq/db/sqlite/build_test.cljs
vendored
@@ -205,4 +205,43 @@
|
||||
(->> (d/q '[:find [?b ...] :in $ ?page-id :where [?b :block/page ?page-id]]
|
||||
@conn [:block/uuid property-uuid])
|
||||
(map #(:block/title (d/entity @conn %)))))
|
||||
"Property page has correct blocks")))
|
||||
"Property page has correct blocks")))
|
||||
|
||||
(deftest property-value-with-properties-and-tags
|
||||
(let [conn (db-test/create-conn-with-blocks
|
||||
{:properties {:p1 {:logseq.property/type :default}}
|
||||
:classes {:C1 {}}
|
||||
:pages-and-blocks
|
||||
[{:page {:block/title "page1"}
|
||||
:blocks [{:block/title "block has pvalue with built-in tag"
|
||||
:build/properties
|
||||
{:p1 {:build/property-value :block
|
||||
:block/title "t1"
|
||||
:build/tags [:logseq.class/Task]}}}
|
||||
{:block/title "block has pvalue with user tag"
|
||||
:build/properties
|
||||
{:p1 {:build/property-value :block
|
||||
:block/title "u1"
|
||||
:build/tags [:C1]}}}
|
||||
{:block/title "Todo query",
|
||||
:build/tags [:logseq.class/Query],
|
||||
:build/properties
|
||||
{:logseq.property/query
|
||||
{:build/property-value :block
|
||||
:block/title "{:query (task Todo)}"
|
||||
:build/properties
|
||||
{:logseq.property.code/lang "clojure"
|
||||
:logseq.property.node/display-type :code}}}}]}]})]
|
||||
(is (= {:logseq.property.node/display-type :code
|
||||
:logseq.property.code/lang "clojure"}
|
||||
(-> (db-test/find-block-by-content @conn "{:query (task Todo)}")
|
||||
db-test/readable-properties
|
||||
(dissoc :logseq.property/created-from-property))))
|
||||
(is (= {:block/tags [:logseq.class/Task]}
|
||||
(-> (db-test/find-block-by-content @conn "t1")
|
||||
db-test/readable-properties
|
||||
(dissoc :logseq.property/created-from-property))))
|
||||
(is (= {:block/tags [:user.class/C1]}
|
||||
(-> (db-test/find-block-by-content @conn "u1")
|
||||
db-test/readable-properties
|
||||
(dissoc :logseq.property/created-from-property))))))
|
||||
77
deps/db/test/logseq/db/sqlite/export_test.cljs
vendored
77
deps/db/test/logseq/db/sqlite/export_test.cljs
vendored
@@ -57,17 +57,21 @@
|
||||
(sqlite-export/build-export @import-conn {:export-type :page :page-id (:db/id page2)})))
|
||||
|
||||
(defn- import-second-time-assertions [conn conn2 page-title original-data
|
||||
& {:keys [transform-expected-blocks]
|
||||
& {:keys [transform-expected-blocks build-journal]
|
||||
:or {transform-expected-blocks (fn [bs] (into bs bs))}}]
|
||||
(let [page (db-test/find-page-by-title @conn2 page-title)
|
||||
imported-page (export-page-and-import-to-another-graph conn conn2 page-title)
|
||||
updated-page (db-test/find-page-by-title @conn2 page-title)
|
||||
expected-page-and-blocks
|
||||
(update-in (:pages-and-blocks original-data) [0 :blocks] transform-expected-blocks)]
|
||||
(update-in (:pages-and-blocks original-data) [0 :blocks] transform-expected-blocks)
|
||||
filter-imported-page (if build-journal
|
||||
#(= build-journal (get-in % [:page :build/journal]))
|
||||
#(= (get-in % [:page :block/title]) page-title))]
|
||||
|
||||
(assert (first expected-page-and-blocks))
|
||||
;; Assume first page is one being imported for now
|
||||
(is (= (first expected-page-and-blocks)
|
||||
(first (:pages-and-blocks imported-page)))
|
||||
(first (filter filter-imported-page (:pages-and-blocks imported-page))))
|
||||
"Blocks are appended to existing page")
|
||||
(is (= (:block/created-at page) (:block/created-at updated-page))
|
||||
"Existing page didn't get re-created")
|
||||
@@ -323,7 +327,8 @@
|
||||
(is (= (-> (:pages-and-blocks original-data)
|
||||
(medley/dissoc-in [1 :blocks 0 :build/properties])
|
||||
;; shallow block means this page doesn't get included
|
||||
butlast)
|
||||
butlast
|
||||
sort-pages-and-blocks)
|
||||
(:pages-and-blocks imported-page))
|
||||
"Page's blocks are imported")
|
||||
|
||||
@@ -412,7 +417,7 @@
|
||||
(is (= (:pages-and-blocks original-data) (:pages-and-blocks imported-page))
|
||||
"Page's blocks are imported")
|
||||
|
||||
(import-second-time-assertions conn conn2 journal-title original-data)))
|
||||
(import-second-time-assertions conn conn2 journal-title original-data {:build-journal 20250210})))
|
||||
|
||||
(deftest import-page-with-different-property-types
|
||||
(let [block-object-uuid (random-uuid)
|
||||
@@ -458,7 +463,8 @@
|
||||
"Page's classes are imported")
|
||||
(is (= (-> (:pages-and-blocks original-data)
|
||||
;; adjust shallow block
|
||||
(medley/dissoc-in [1 :blocks 0 :build/tags]))
|
||||
(medley/dissoc-in [1 :blocks 0 :build/tags])
|
||||
sort-pages-and-blocks)
|
||||
(:pages-and-blocks imported-page))
|
||||
"Page's blocks are imported")
|
||||
|
||||
@@ -527,7 +533,44 @@
|
||||
imported-nodes (sqlite-export/build-export @conn2 {:export-type :view-nodes
|
||||
:node-ids (get-node-ids @conn2)})]
|
||||
|
||||
(is (= (:pages-and-blocks original-data) (:pages-and-blocks imported-nodes)))
|
||||
(is (= (sort-pages-and-blocks (:pages-and-blocks original-data)) (:pages-and-blocks imported-nodes)))
|
||||
(is (= (expand-properties (:properties original-data)) (:properties imported-nodes)))
|
||||
(is (= (expand-classes (:classes original-data)) (:classes imported-nodes)))))
|
||||
|
||||
(deftest import-selected-nodes
|
||||
(let [original-data
|
||||
;; Test a mix of pages and blocks
|
||||
{:properties {:user.property/p1 {:logseq.property/type :default}}
|
||||
:classes {:user.class/class1 {}}
|
||||
:pages-and-blocks [{:page {:block/title "page1"}
|
||||
:blocks [{:block/title "b1"
|
||||
:build/properties {:user.property/p1 "ok"}
|
||||
:build/children [{:block/title "b2"}]}
|
||||
{:block/title "b3"
|
||||
:build/tags [:user.class/class1]
|
||||
:build/children [{:block/title "b4"}]}]}
|
||||
{:page {:block/title "page2"}
|
||||
:blocks [{:block/title "dont export"}]}]}
|
||||
conn (db-test/create-conn-with-blocks original-data)
|
||||
get-node-ids (fn [db]
|
||||
(->> [(db-test/find-block-by-content db "b1")
|
||||
(db-test/find-page-by-title db "b3")
|
||||
(db-test/find-page-by-title db "page2")]
|
||||
(remove nil?)
|
||||
(mapv #(vector :block/uuid (:block/uuid %)))))
|
||||
conn2 (db-test/create-conn)
|
||||
{:keys [init-tx block-props-tx] :as _txs}
|
||||
(-> (sqlite-export/build-export @conn {:export-type :selected-nodes :node-ids (get-node-ids @conn)})
|
||||
(sqlite-export/build-import @conn2 {}))
|
||||
;; _ (cljs.pprint/pprint _txs)
|
||||
_ (d/transact! conn2 init-tx)
|
||||
_ (d/transact! conn2 block-props-tx)
|
||||
_ (validate-db @conn2)
|
||||
imported-nodes (sqlite-export/build-export @conn2 {:export-type :selected-nodes :node-ids (get-node-ids @conn2)})]
|
||||
|
||||
(is (= (->> (:pages-and-blocks original-data)
|
||||
(map #(if (= (get-in % [:page :block/title]) "page2") (dissoc % :blocks) %)))
|
||||
(:pages-and-blocks imported-nodes)))
|
||||
(is (= (expand-properties (:properties original-data)) (:properties imported-nodes)))
|
||||
(is (= (expand-classes (:classes original-data)) (:classes imported-nodes)))))
|
||||
|
||||
@@ -558,6 +601,7 @@
|
||||
:logseq.property/default-value 42})}
|
||||
:user.property/default-closed
|
||||
{:logseq.property/type :default
|
||||
:db/cardinality :db.cardinality/many
|
||||
:build/closed-values [{:value "joy" :uuid closed-value-uuid}
|
||||
{:value "sad" :uuid (random-uuid)}]}
|
||||
:user.property/checkbox {:logseq.property/type :checkbox}
|
||||
@@ -587,10 +631,25 @@
|
||||
:user.property/node #{[:block/uuid page-pvalue-uuid]}}}
|
||||
:blocks [{:block/title "b1"
|
||||
:build/properties {:user.property/num 1
|
||||
:user.property/default-closed [:block/uuid closed-value-uuid]
|
||||
:user.property/default-closed #{[:block/uuid closed-value-uuid]}
|
||||
:user.property/date [:block/uuid journal-uuid]}}
|
||||
{:block/title "b2" :build/properties {:user.property/node #{[:block/uuid page-object-uuid]}}}
|
||||
{:block/title "b3" :build/properties {:user.property/node #{[:block/uuid page-object-uuid]}}}]}
|
||||
{:block/title "b3" :build/properties {:user.property/node #{[:block/uuid page-object-uuid]}}}
|
||||
{:block/title "Example advanced query",
|
||||
:build/tags [:logseq.class/Query],
|
||||
:build/properties
|
||||
{:logseq.property/query
|
||||
{:build/property-value :block
|
||||
:block/title "{:query (task Todo)}"
|
||||
:build/properties
|
||||
{:logseq.property.code/lang "clojure"
|
||||
:logseq.property.node/display-type :code}}}}
|
||||
{:block/title "block has property value with tags and properties"
|
||||
:build/properties
|
||||
{:user.property/url
|
||||
{:build/property-value :block
|
||||
:block/title "https://example.com"
|
||||
:build/tags [:user.class/MyClass]}}}]}
|
||||
{:page {:block/title "page object"
|
||||
:block/uuid page-object-uuid
|
||||
:build/keep-uuid? true}
|
||||
|
||||
Reference in New Issue
Block a user