enhance: export-edn supports :build/properties in property values

no matter how many levels deep a property value has properties
This commit is contained in:
Gabriel Horner
2026-02-20 13:49:10 -05:00
parent 991e38419a
commit 5e43f89af2
3 changed files with 81 additions and 50 deletions

View File

@@ -126,6 +126,45 @@
:original-property-id k
:logseq.property/type prop-type}))))
(declare ->property-value-tx-m)
(defn- build-pvalue [properties-config all-idents closed-value-id v]
(let [pvalue-uuid (or (:block/uuid v) (random-uuid))
nested-pvalue-tx-m
(when (seq (:build/properties v))
(some-> (->property-value-tx-m {:block/uuid pvalue-uuid}
(:build/properties v)
properties-config
all-idents)
;; add :db/id to ensure datascript consistently creates this new tx
(update-vals (fn [prop-val]
(cond
(map? prop-val)
(assoc prop-val :db/id (new-db-id))
(set? prop-val)
(set (map #(if (map? %)
(assoc % :db/id (new-db-id))
%)
prop-val))
:else
prop-val)))))]
{:attributes
(when (:build/property-value v)
(merge (:build/properties v)
nested-pvalue-tx-m
{:block/tags (mapv #(hash-map :db/ident (get-ident all-idents %))
(:build/tags v))}
(select-keys v [:block/created-at :block/updated-at])
{:block/uuid pvalue-uuid}))
:value
(cond
closed-value-id
closed-value-id
(:build/property-value v)
(or (:logseq.property/value v) (:block/title v))
:else
v)}))
(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.
@@ -140,25 +179,8 @@
(when (= (:value item) v)
(:uuid item)))
(get property :build/closed-values)))
build-pvalue
(fn build-pvalue [v]
{:attributes
(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 :block/uuid])))
:value
(cond
closed-value-id
closed-value-id
(:build/property-value v)
(or (:logseq.property/value v) (:block/title v))
:else
v)})]
(if (set? v) (set (map build-pvalue v)) (build-pvalue v)))])))
build-pvalue' #(build-pvalue properties-config all-idents closed-value-id %)]
(if (set? v) (set (map build-pvalue' v)) (build-pvalue' v)))])))
((fn [x]
(db-property-build/build-property-values-tx-m new-block x {:pvalue-map? true})))))
@@ -394,15 +416,21 @@
(map #(-> (:blocks %) vec (conj (:page %))))
(mapcat (fn build-node-props-vec [nodes]
(mapcat (fn [m]
(if-let [pvalue-pages
(->> (vals (:build/properties m))
(mapcat #(if (set? %) % [%]))
(filter page-prop-value?)
(map second)
seq)]
(into (vec (:build/properties m))
(build-node-props-vec pvalue-pages))
(:build/properties m)))
(let [nested-pvalue-pages
(->> (vals (:build/properties m))
(mapcat #(if (set? %) % [%]))
(keep #(cond
(page-prop-value? %)
(second %)
(and (map? %) (:build/property-value %))
%
:else
nil))
seq)]
(if nested-pvalue-pages
(into (vec (:build/properties m))
(build-node-props-vec nested-pvalue-pages))
(:build/properties m))))
nodes)))
set)
property-properties (->> (vals properties)

View File

@@ -15,12 +15,10 @@
[logseq.db.frontend.db :as db-db]
[logseq.db.frontend.entity-util :as entity-util]
[logseq.db.frontend.property :as db-property]
[logseq.db.frontend.property.type :as db-property-type]
[logseq.db.frontend.schema :as db-schema]
[logseq.db.frontend.validate :as db-validate]
[logseq.db.sqlite.build :as sqlite-build]
[logseq.db.sqlite.create-graph :as sqlite-create-graph]
[medley.core :as medley]))
[logseq.db.sqlite.create-graph :as sqlite-create-graph]))
;; Export fns
;; ==========
@@ -60,27 +58,19 @@
(entity-util/journal? pvalue)
[:build/page {:build/journal (:block/journal-day pvalue)}]))
(defn- build-pvalue-entity-default [db ent-properties pvalue
(defn- build-pvalue-entity-default [ent-properties pvalue
{:keys [include-pvalue-uuid-fn]
:or {include-pvalue-uuid-fn (constantly false)}
:as options}]
(let [property-value-content' (property-value-content pvalue)
;; TODO: Add support for ref properties here and in sqlite.build
build-properties
(some->> ent-properties
(keep (fn [[k v]]
(let [prop-type (:logseq.property/type (d/entity db k))]
(when-not (contains? db-property-type/all-ref-property-types prop-type)
[k v]))))
(into {}))]
(let [property-value-content' (property-value-content pvalue)]
(if (or (seq ent-properties) (seq (:block/tags pvalue)) (include-pvalue-uuid-fn (:block/uuid pvalue)))
(cond-> {:build/property-value :block
:block/title property-value-content'}
(seq (:block/tags pvalue))
(assoc :build/tags (->build-tags (:block/tags pvalue)))
(seq build-properties)
(assoc :build/properties build-properties)
(seq ent-properties)
(assoc :build/properties ent-properties)
(include-pvalue-uuid-fn (:block/uuid pvalue))
(assoc :block/uuid (:block/uuid pvalue) :build/keep-uuid? true)
@@ -107,14 +97,12 @@
;; Use metadata to 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?))
(let [ent-properties* (apply dissoc (db-property/properties pvalue)
:logseq.property/value :logseq.property/created-from-property
db-property/public-db-attribute-properties)
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 db ent-properties pvalue options'))))))]
(build-pvalue-entity-default ent-properties pvalue options'))))))]
(->> (apply dissoc ent-properties ignored-properties)
(map (fn [[k v]]
[k
@@ -241,10 +229,22 @@
(defn- build-node-properties
[db entity ent-properties {:keys [properties] :as options}]
(let [new-user-property-ids (->> (keys ent-properties)
(let [collect-nested-property-ids
(fn collect-nested-property-ids [v]
(cond
(and (de/entity? v) (:logseq.property/created-from-property v))
(let [pvalue-properties (apply dissoc (db-property/properties v) db-property/public-db-attribute-properties)]
(concat (keys pvalue-properties)
(mapcat collect-nested-property-ids (vals pvalue-properties))))
(set? v)
(mapcat collect-nested-property-ids v)
:else
[]))
new-user-property-ids (->> (keys ent-properties)
(concat (->> (:block/tags entity)
(mapcat :logseq.property.class/properties)
(map :db/ident)))
(concat (mapcat collect-nested-property-ids (vals ent-properties)))
;; Built-in properties and any possible modifications are not exported
(remove db-property/logseq-property?)
(remove #(get properties %)))]

View File

@@ -285,6 +285,8 @@
:build/keep-uuid? true
:build/property-classes [:user.class/NodeClass]}
:user.property/p2
{:logseq.property/type :default}
:user.property/p3
{:logseq.property/type :default}}
:extract-content-refs? false
:pages-and-blocks
@@ -304,6 +306,7 @@
{:block/title "block with a pvalue that has a :block/uuid"
:build/properties {:user.property/p2 {:build/property-value :block
:block/title "property value block"
:build/properties {:user.property/p3 "woot"}
:block/uuid pvalue-block-uuid
:build/keep-uuid? true}}}]}
{:page {:block/title "page with block ref"}