querying-logseq-data

Expert in building Datalog queries for Logseq DB graphs. Auto-invokes when users need help writing Logseq queries, understanding Datalog syntax, optimizing query performance, or working with the Datas

SKILL.md

GitHub

Querying Logseq Data

When to Use This Skill

This skill auto-invokes when:

  • User wants to build a Datalog query for Logseq
  • Questions about :find, :where, :in clauses
  • Pull syntax questions (pull ?e [*])
  • Query optimization or performance issues
  • Aggregation queries (count, sum, avg, min, max)
  • Rule definitions or reusable query logic
  • Converting simple query syntax to full Datalog
  • User mentions "Datalog", "query", "datascript" with Logseq context

Reference Material: See {baseDir}/references/query-patterns.md for common query examples.

You are an expert in Datalog queries for Logseq's database-based graphs.

Datalog Query Fundamentals

Basic Query Structure

[:find ?variable          ; What to return
 :in $ ?input-var         ; Inputs ($ = database)
 :where                   ; Conditions
 [?entity :attribute ?value]]

Find Specifications

;; Return all matches as tuples
[:find ?title ?author ...]

;; Return as collection (single variable)
[:find [?title ...] ...]

;; Return single value
[:find ?title . ...]

;; Return single tuple
[:find [?title ?author] ...]

;; Pull entity data
[:find (pull ?e [*]) ...]
[:find (pull ?e [:block/title :block/tags]) ...]

Common Query Patterns

Find All Pages

[:find (pull ?p [*])
 :where
 [?p :block/tags ?t]
 [?t :db/ident :logseq.class/Page]]

Find Blocks with Specific Tag/Class

[:find (pull ?b [*])
 :where
 [?b :block/tags ?t]
 [?t :block/title "Book"]]

Find by Property Value

;; Exact match
[:find (pull ?b [*])
 :where
 [?b :user.property/author "Stephen King"]]

;; With variable binding
[:find ?title ?author
 :where
 [?b :block/title ?title]
 [?b :user.property/author ?author]
 [?b :block/tags ?t]
 [?t :block/title "Book"]]

Find Tasks by Status

[:find (pull ?t [*])
 :where
 [?t :block/tags ?tag]
 [?tag :db/ident :logseq.class/Task]
 [?t :logseq.property/status ?s]
 [?s :block/title "In Progress"]]

Find with Date Ranges

;; Tasks due this week
[:find (pull ?t [*])
 :in $ ?start ?end
 :where
 [?t :block/tags ?tag]
 [?tag :db/ident :logseq.class/Task]
 [?t :logseq.property/deadline ?d]
 [(>= ?d ?start)]
 [(<= ?d ?end)]]

Advanced Query Techniques

Aggregations

;; Count books by author
[:find ?author (count ?b)
 :where
 [?b :block/tags ?t]
 [?t :block/title "Book"]
 [?b :user.property/author ?author]]

;; Sum, min, max, avg
[:find (sum ?rating) (avg ?rating) (min ?rating) (max ?rating)
 :where
 [?b :block/tags ?t]
 [?t :block/title "Book"]
 [?b :user.property/rating ?rating]]

Rules (Reusable Query Logic)

;; Define rules
[[(has-tag ?b ?tag-name)
  [?b :block/tags ?t]
  [?t :block/title ?tag-name]]

 [(is-task ?b)
  [?b :block/tags ?t]
  [?t :db/ident :logseq.class/Task]]]

;; Use rules in query
[:find (pull ?b [*])
 :in $ %
 :where
 (has-tag ?b "Important")
 (is-task ?b)]

Negation

;; Find books without rating
[:find (pull ?b [*])
 :where
 [?b :block/tags ?t]
 [?t :block/title "Book"]
 (not [?b :user.property/rating _])]

Or Clauses

;; Find high priority or overdue tasks
[:find (pull ?t [*])
 :in $ ?today
 :where
 [?t :block/tags ?tag]
 [?tag :db/ident :logseq.class/Task]
 (or
   [?t :logseq.property/priority "High"]
   (and
     [?t :logseq.property/deadline ?d]
     [(< ?d ?today)]))]

Recursive Queries

;; Find all descendants of a block
[[(descendant ?parent ?child)
  [?child :block/parent ?parent]]
 [(descendant ?parent ?child)
  [?child :block/parent ?p]
  (descendant ?parent ?p)]]

[:find (pull ?c [*])
 :in $ % ?root-id
 :where
 [?root :block/uuid ?root-id]
 (descendant ?root ?c)]

Pull Syntax

Selective Attributes

;; Specific attributes
(pull ?e [:block/title :block/tags])

;; Nested pulling for refs
(pull ?e [:block/title {:block/tags [:block/title]}])

;; All attributes
(pull ?e [*])

;; Limit nested results
(pull ?e [:block/title {:block/children [:block/title] :limit 5}])

Reverse References

;; Find all blocks referencing this entity
(pull ?e [:block/title {:block/_refs [:block/title]}])

DB-Specific Query Patterns

Working with Classes

;; Find all classes (tags that are themselves tagged as Tag)
[:find (pull ?c [*])
 :where
 [?c :block/tags ?t]
 [?t :db/ident :logseq.class/Tag]]

;; Find class hierarchy
[:find ?parent-name ?child-name
 :where
 [?child :logseq.property.class/extends ?parent]
 [?child :block/title ?child-name]
 [?parent :block/title ?parent-name]]

Working with Properties

;; Find all user-defined properties
[:find (pull ?p [*])
 :where
 [?p :block/tags ?t]
 [?t :db/ident :logseq.class/Property]
 [?p :db/ident ?ident]
 [(clojure.string/starts-with? (str ?ident) ":user.property")]]

;; Find property values with type
[:find ?prop-name ?type
 :where
 [?p :block/tags ?t]
 [?t :db/ident :logseq.class/Property]
 [?p :block/title ?prop-name]
 [?p :logseq.property/type ?type]]

Journal Queries

;; Find all journal pages
[:find (pull ?j [*])
 :where
 [?j :block/tags ?t]
 [?t :db/ident :logseq.class/Journal]]

;; Find journal for specific date
[:find (pull ?j [*])
 :in $ ?date-str
 :where
 [?j :block/tags ?t]
 [?t :db/ident :logseq.class/Journal]
 [?j :block/title ?date-str]]

Query Optimization Tips

  1. Put most selective clauses first - Narrow down results early
  2. Use indexed attributes - :db/ident, :block/uuid are indexed
  3. Avoid wildcards in pull - Specify needed attributes
  4. Use rules for complex logic - Better readability and potential caching
  5. Limit results when possible - Add limits for large datasets
;; Optimized query example
[:find (pull ?b [:block/title :user.property/rating])
 :in $ ?min-rating
 :where
 ;; Most selective first
 [?b :user.property/rating ?r]
 [(>= ?r ?min-rating)]
 ;; Then filter by tag
 [?b :block/tags ?t]
 [?t :block/title "Book"]]

Logseq Query UI vs Raw Datalog

Simple Query (UI)

{{query (and [[Book]] (property :rating 5))}}

Equivalent Datalog

[:find (pull ?b [*])
 :where
 [?b :block/tags ?t]
 [?t :block/title "Book"]
 [?b :user.property/rating 5]]

Advanced Query Block

#+BEGIN_QUERY
{:title "5-Star Books"
 :query [:find (pull ?b [*])
         :where
         [?b :block/tags ?t]
         [?t :block/title "Book"]
         [?b :user.property/rating 5]]
 :result-transform (fn [result] (sort-by :block/title result))
 :view (fn [rows] [:ul (for [r rows] [:li (:block/title r)])])}
#+END_QUERY

Common Gotchas

  1. MD vs DB attribute differences

    • MD: :block/content, :block/name
    • DB: :block/title, :block/tags
  2. Property namespacing

    • User properties: :user.property/name
    • System properties: :logseq.property/name
  3. Tag vs Class terminology

    • In UI: "Tags"
    • In schema: "Classes" (:logseq.class/*)
  4. Date handling

    • Dates link to journal pages
    • Compare using date functions, not strings