Переглянути джерело

chore(libs): add development notes

charlie 2 тижнів тому
батько
коміт
d1bbb5ad16
1 змінених файлів з 352 додано та 0 видалено
  1. 352 0
      libs/development-notes/db_tag_property_idents_notes.md

+ 352 - 0
libs/development-notes/db_tag_property_idents_notes.md

@@ -0,0 +1,352 @@
+# Property & Tag Idents
+
+## What is an Ident?
+
+An **ident** is a unique keyword identifier for a property or tag in the database. It's distinct from:
+- **name**: Human-readable display name (e.g., "Created At")
+- **uuid**: Universal unique identifier for the entity
+- **ident**: Internal database identifier (e.g., `:logseq.property/created-at`)
+
+**Key characteristics:**
+- Idents use kebab-case with namespace prefixes
+- System properties use `:logseq.property/` namespace
+- System class properties use `:logseq.class/` namespace
+- Plugin created class use `:plugin.class.xxx-plugin-id/` namespace
+- Plugin created property use `:plugin.property.xxx-plugin-id/` namespace
+- Custom properties typically don't have explicit idents (auto-generated)
+
+## When to Use Idents
+
+Use idents when:
+1. Working with **built-in system properties**
+2. Setting **special behaviors** for properties
+3. Querying the database directly with datascript
+4. Creating **stable references** that survive renames
+
+**Example:**
+```typescript
+// Using ident for system property
+await logseq.Editor. upsertBlockProperty(
+  blockId, 
+  ':logseq. property/created-at',  // ident
+  new Date().getTime()
+)
+
+// Using regular property name
+await logseq.Editor. upsertBlockProperty(
+  blockId, 
+  'author',  // regular name
+  'John Doe'
+)
+```
+
+---
+
+## Built-in System Properties
+
+### Common System Properties
+
+#### `:logseq.property/created-at`
+Timestamp when the block/page was created.
+
+```typescript
+await logseq.Editor.upsertBlockProperty(
+  page.uuid,
+  ':logseq.property/created-at',
+  Date.now()
+)
+```
+
+#### `:logseq.property/updated-at`
+Timestamp when the block/page was last updated.
+
+```typescript
+await logseq.Editor.upsertBlockProperty(
+  block.uuid,
+  ':logseq.property/updated-at',
+  Date.now()
+)
+```
+
+#### `:logseq.property/icon`
+Icon for a page or block (set via `setBlockIcon` API).
+
+```typescript
+// Don't set directly, use API: 
+await logseq.Editor. setBlockIcon(page.uuid, 'emoji', 'book')
+
+// Behind the scenes, this sets:
+// :logseq.property/icon {: type "emoji", :id "book"}
+```
+
+#### `:logseq.property/hide-empty-value`
+Hide property when value is empty.
+
+```typescript
+const prop = await logseq.Editor.upsertProperty('optional-field', { 
+  type: 'default' 
+})
+
+await logseq.Editor.upsertBlockProperty(
+  prop. id,  // Apply to property entity itself
+  ':logseq.property/hide-empty-value',
+  true
+)
+```
+
+#### `:logseq.property/closed-value-mode`
+Property only accepts predefined values (enum-like).
+
+```typescript
+const prop = await logseq.Editor. upsertProperty('status', { type: 'default' })
+
+await logseq.Editor.upsertBlockProperty(
+  prop.id,
+  ':logseq.property/closed-value-mode',
+  true
+)
+
+// Define allowed values
+await logseq.Editor.upsertBlockProperty(
+  prop. id,
+  ':logseq.property/closed-values',
+  ['todo', 'in-progress', 'done']
+)
+```
+
+#### `:logseq.property/schema`
+Property schema definition (type, cardinality, etc.).
+
+```typescript
+// Automatically set by upsertProperty, don't set manually
+await logseq.Editor. upsertProperty('tags', { 
+  type: 'node', 
+  cardinality: 'many' 
+})
+
+// Behind the scenes sets:
+// :logseq. property/schema {:type "node", :cardinality "many"}
+```
+
+---
+
+## Built-in Class (Tag) Properties
+
+### Tag Inheritance & Schema
+
+#### `:logseq.property. class/extends`
+Define parent classes (tag inheritance).
+
+```typescript
+const mediaTag = await logseq.Editor.createTag('Media')
+const bookTag = await logseq. Editor.createTag('book')
+
+// Use addTagExtends API (recommended)
+await logseq.Editor. addTagExtends(bookTag. uuid, mediaTag.uuid)
+
+// Or set directly: 
+await logseq.Editor. upsertBlockProperty(
+  bookTag.uuid,
+  ': logseq.property.class/extends',
+  [mediaTag.id]  // Array of parent tag IDs
+)
+```
+
+#### `:logseq.property.class/properties`
+Properties available for this class.
+
+```typescript
+// Use addTagProperty API (recommended)
+await logseq.Editor.addTagProperty(bookTag.uuid, 'author')
+
+// Or set directly:
+const authorProp = await logseq.Editor.getProperty('author')
+await logseq.Editor.upsertBlockProperty(
+  bookTag.uuid,
+  ':logseq.property. class/properties',
+  [authorProp.id]
+)
+```
+
+---
+
+## Working with Idents
+
+### Pattern 1: Query by Ident
+
+```typescript
+// Get property entity by ident
+const createdAtProp = await logseq. DB.datascriptQuery(`
+  [: find (pull ?p [*])
+   :where
+   [?p :block/type "property"]
+   [?p :block/ident : logseq.property/created-at]]
+`)
+
+console.log(createdAtProp)
+```
+
+### Pattern 2: Check if Property Has Ident
+
+```typescript
+async function getPropertyIdent(propertyName: string) {
+  const prop = await logseq.Editor. getProperty(propertyName)
+  return prop?.ident || null
+}
+
+const ident = await getPropertyIdent('created-at')
+// Returns:  ": logseq.property/created-at" or null
+```
+
+### Pattern 3: Create Custom Namespaced Property
+
+```typescript
+// For plugin-specific properties, use your own namespace
+const pluginNamespace = 'myplugin'
+
+async function createPluginProperty(name: string, schema: any) {
+  // Note: Custom idents are not directly settable via API
+  // They're auto-generated, but you can query by name
+  
+  const prop = await logseq.Editor.upsertProperty(name, schema)
+  
+  // Custom properties won't have idents unless they're system properties
+  return prop
+}
+```
+
+### Pattern 4: Set Multiple System Properties
+
+```typescript
+async function setupPageMetadata(pageUUID: string, metadata: any) {
+  const now = Date.now()
+  
+  // Set creation timestamp
+  await logseq. Editor.upsertBlockProperty(
+    pageUUID,
+    ':logseq.property/created-at',
+    metadata.createdAt || now
+  )
+  
+  // Set update timestamp
+  await logseq. Editor.upsertBlockProperty(
+    pageUUID,
+    ':logseq.property/updated-at',
+    now
+  )
+  
+  // Set custom properties
+  await logseq. Editor.upsertBlockProperty(pageUUID, 'author', metadata.author)
+  await logseq.Editor.upsertBlockProperty(pageUUID, 'source', metadata.source)
+}
+```
+
+### Pattern 5: Configure Property Behavior
+
+```typescript
+async function createEnumProperty(name: string, allowedValues: string[]) {
+  // 1. Create property
+  const prop = await logseq.Editor.upsertProperty(name, { type: 'default' })
+  
+  // 2. Enable closed-value mode
+  await logseq.Editor.upsertBlockProperty(
+    prop.id,
+    ':logseq.property/closed-value-mode',
+    true
+  )
+  
+  // 3. Set allowed values
+  await logseq.Editor. upsertBlockProperty(
+    prop.id,
+    ':logseq.property/closed-values',
+    allowedValues
+  )
+  
+  // 4.  Optionally hide when empty
+  await logseq. Editor.upsertBlockProperty(
+    prop.id,
+    ':logseq.property/hide-empty-value',
+    true
+  )
+  
+  return prop
+}
+
+// Usage
+await createEnumProperty('priority', ['high', 'medium', 'low'])
+```
+
+---
+
+## System Property Reference Table
+
+| Ident | Purpose | Value Type | Applied To |
+|-------|---------|------------|------------|
+| `:logseq.property/created-at` | Creation timestamp | number (ms) | Block/Page |
+| `:logseq.property/updated-at` | Update timestamp | number (ms) | Block/Page |
+| `:logseq.property/icon` | Icon definition | `{type, id}` object | Block/Page |
+| `:logseq.property/hide-empty-value` | Hide if empty | boolean | Property entity |
+| `:logseq.property/closed-value-mode` | Enum mode | boolean | Property entity |
+| `:logseq.property/closed-values` | Allowed values | array | Property entity |
+| `:logseq.property/schema` | Schema definition | object | Property entity |
+| `:logseq.property. class/extends` | Parent classes | array of IDs | Tag entity |
+| `:logseq.property.class/properties` | Class properties | array of IDs | Tag entity |
+
+---
+
+## Best Practices for Idents
+
+### 1. Prefer High-Level APIs Over Direct Ident Manipulation
+Use specialized APIs instead of setting idents directly:
+
+```typescript
+// ✅ Better: Use API
+await logseq.Editor.addTagExtends(childTag.uuid, parentTag.uuid)
+
+// ❌ Avoid: Direct ident manipulation
+await logseq. Editor.upsertBlockProperty(
+  childTag.uuid,
+  ': logseq.property. class/extends',
+  [parentTag.id]
+)
+```
+
+### 2. Only Use Idents for System Properties
+Custom properties should use regular names:
+
+```typescript
+// ✅ Correct: System property with ident
+await logseq. Editor.upsertBlockProperty(
+  page.uuid,
+  ': logseq.property/created-at',
+  Date.now()
+)
+
+// ✅ Correct: Custom property with name
+await logseq.Editor. upsertBlockProperty(
+  page.uuid,
+  'author',
+  'John Doe'
+)
+
+// ❌ Wrong: Don't create custom idents
+await logseq.Editor. upsertBlockProperty(
+  page.uuid,
+  ':myplugin/custom-prop',  // Not supported
+  'value'
+)
+```
+
+### 3. Query System Properties by Ident in Datascript
+When using advanced queries, idents provide stable references:
+
+```typescript
+// Query all pages created in the last 7 days
+const recentPages = await logseq. DB.datascriptQuery(`
+  [:find (pull ?p [*])
+   :where
+   [?p :block/type "page"]
+   [?p : logseq.property/created-at ? created]
+   [(> ?created ${Date.now() - 7 * 24 * 60 * 60 * 1000})]]
+`)
+```