-
Notifications
You must be signed in to change notification settings - Fork 15
Expand file tree
/
Copy pathsupertag-core-scan.el
More file actions
202 lines (180 loc) · 8.46 KB
/
Copy pathsupertag-core-scan.el
File metadata and controls
202 lines (180 loc) · 8.46 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
;;; supertag-core-scan.el --- Scan-based query functions for Supertag -*- lexical-binding: t; -*-
;;; Commentary:
;; This file provides internal Document Projection queries. Tag membership
;; uses rebuildable indexes; residual text/date/file queries still scan the
;; Store below high-level services.
;;; Code:
(require 'cl-lib)
(require 'supertag-core-index)
(require 'supertag-core-store)
(require 'supertag-ops-node)
(require 'supertag-ops-tag)
;;; --- Document Projection Queries ---
(defun supertag-find-tag-descendants (tag-name)
"Return stored tag IDs that transitively extend TAG-NAME."
(let ((tag-id (or (and (supertag-tag-get tag-name) tag-name)
(supertag-tag-resolve-occurrence tag-name)
tag-name)))
(supertag-tag-descendants tag-id)))
(defun supertag-index-get-nodes-by-tag (tag-name &optional include-descendants)
"Return indexed node IDs for TAG-NAME.
When INCLUDE-DESCENDANTS is non-nil, include transitive descendants."
(let* ((resolved (or (and (supertag-tag-get tag-name) tag-name)
(supertag-tag-resolve-occurrence tag-name)
tag-name))
(matching-tags (cons resolved
(and include-descendants
(supertag-find-tag-descendants resolved)))))
(supertag-index-find-node-ids-by-tags matching-tags)))
(defun supertag-index-get-nodes-by-word (word)
"Find all nodes containing WORD by scanning the store.
This is an O(N) operation and performs a simple substring search."
(let ((nodes-ht (supertag-store-get-collection :nodes))
(results '())
(search-word (downcase word)))
(when (hash-table-p nodes-ht)
(maphash (lambda (node-id node-data)
(let ((title (plist-get node-data :title))
(content (plist-get node-data :content)))
(when (or (and title (string-match-p (regexp-quote search-word) (downcase title)))
(and content (string-match-p (regexp-quote search-word) (downcase content))))
(push node-id results))))
nodes-ht))
(nreverse (delete-dups results))))
(defun supertag-index-get-nodes-by-date-range (start-time end-time &optional date-field)
"Find all nodes created/modified within a date range by scanning.
This is an O(N) operation.
DATE-FIELD can be :created-at or :modified-at (default :created-at)."
(let* ((field (or date-field :created-at))
(nodes-ht (supertag-store-get-collection :nodes))
(matching-nodes '()))
(when (hash-table-p nodes-ht)
(maphash (lambda (node-id node-data)
(let ((node-time (plist-get node-data field)))
(when node-time
(let ((start-check (or (null start-time) (time-less-p start-time node-time)))
(end-check (or (null end-time) (time-less-p node-time end-time))))
(when (and start-check end-check)
(push node-id matching-nodes))))))
nodes-ht))
(nreverse matching-nodes)))
(defun supertag-find-nodes-by-tag (tag-name &optional include-descendants)
"Return indexed nodes with TAG-NAME.
TAG-NAME is the name of the tag to search for.
When INCLUDE-DESCENDANTS is non-nil, tags that transitively extend
TAG-NAME also match.
Returns a list of (node-id . node-data) pairs."
(let* ((resolved (or (and (supertag-tag-get tag-name) tag-name)
(supertag-tag-resolve-occurrence tag-name)
tag-name))
(matching-tags (cons resolved
(and include-descendants
(supertag-find-tag-descendants resolved))))
(nodes-ht (supertag-store-get-collection :nodes))
results)
(dolist (node-id (supertag-index-find-node-ids-by-tags matching-tags)
(nreverse results))
(when-let* ((node (gethash node-id nodes-ht)))
(push (cons node-id node) results)))))
(defun supertag-find-nodes-by-file (file-path)
"Find all nodes located in FILE-PATH.
Returns a list of (node-id . node-data) pairs."
(let ((nodes-collection (supertag-store-get-collection :nodes))
(found-nodes '()))
(when (hash-table-p nodes-collection)
(maphash
(lambda (id node-data)
;; Safely extract :file and ensure it's a string
(when-let* ((node-file (and node-data (plist-get node-data :file)))
((stringp node-file)))
;; Direct string comparison without path normalization
(when (equal node-file file-path)
(push (cons id node-data) found-nodes))))
nodes-collection))
(nreverse found-nodes)))
(defun supertag-find-nodes-by-title (title-pattern)
"Find all nodes whose title matches TITLE-PATTERN by scanning the store.
TITLE-PATTERN is a regular expression string.
Returns a list of (node-id . node-data) pairs."
(let ((nodes-ht (supertag-store-get-collection :nodes))
(results '()))
(when (hash-table-p nodes-ht)
(maphash (lambda (node-id node-data)
(when (and node-data
(plist-get node-data :title)
(string-match-p title-pattern (plist-get node-data :title)))
(push (cons node-id node-data) results)))
nodes-ht))
(nreverse results)))
(defun supertag-find-nodes (predicates)
"Find nodes satisfying PREDICATES by scanning the store.
PREDICATES is a list of predicate functions. Each predicate function receives (id . data) and returns t or nil.
Returns a list of (node-id . node-data) pairs that satisfy all predicates."
(let ((nodes-ht (supertag-store-get-collection :nodes))
(results '()))
(when (hash-table-p nodes-ht)
(maphash (lambda (node-id node-data)
(when (and node-data
(cl-every (lambda (pred) (funcall pred node-id node-data)) predicates))
(push (cons node-id node-data) results)))
nodes-ht))
(nreverse results)))
(defun supertag-index-node-has-tag-p (node-id tag-name)
"Check if a node has a specific tag by direct lookup.
This is an O(1) operation on the node data."
(when-let ((node-data (supertag-node-get node-id)))
(member tag-name (supertag-node-tag-query-keys node-data))))
(defun supertag-find-nodes-by-parent (parent-id)
"Find all nodes whose :parent-id is PARENT-ID.
Returns a list of (node-id . node-data) pairs."
(let ((nodes-collection (supertag-store-get-collection :nodes))
(found-nodes '()))
(when (hash-table-p nodes-collection)
(maphash
(lambda (id node-data)
(when (and node-data
(equal (plist-get node-data :parent-id) parent-id))
(push (cons id node-data) found-nodes)))
nodes-collection))
(nreverse found-nodes)))
(defun supertag-find-file-node (file-path)
"Find the file node (level 0) for FILE-PATH.
Returns (node-id . node-data) or nil."
(let ((nodes-collection (supertag-store-get-collection :nodes))
(found nil))
(when (hash-table-p nodes-collection)
(maphash
(lambda (id node-data)
(when (and node-data
(eq (plist-get node-data :level) 0)
(equal (plist-get node-data :file) file-path)
(not found))
(setq found (cons id node-data))))
nodes-collection))
found))
(defun supertag-get-file-node-for-node (node-id)
"Get the file node (parent) for NODE-ID.
Walks :parent-id chain. Returns (file-node-id . file-node-data) or nil."
(when-let ((node (supertag-node-get node-id)))
(if-let ((parent-id (plist-get node :parent-id)))
(let ((parent (supertag-node-get parent-id)))
(when parent
(cons parent-id parent)))
;; Fallback: try to find file node by :file path
(when-let ((file-path (plist-get node :file)))
(supertag-find-file-node file-path)))))
(defun supertag-find-all-file-nodes ()
"Find all file nodes (level 0) in the database.
Returns a list of (node-id . node-data) pairs."
(let ((nodes-collection (supertag-store-get-collection :nodes))
(found-nodes '()))
(when (hash-table-p nodes-collection)
(maphash
(lambda (id node-data)
(when (and node-data
(eq (plist-get node-data :level) 0))
(push (cons id node-data) found-nodes)))
nodes-collection))
(nreverse found-nodes)))
(provide 'supertag-core-scan)
;;; supertag-core-scan.el ends here