Coverage report: /development/source/library/org/datagraph/spocq-shard/src/store/rlmdb/bgp-streaming.lisp

KindCoveredAll%
expression7411798 41.2
branch89282 31.6
Key
Not instrumented
Conditionalized out
Executed
Not executed
 
Both branches taken
One branch taken
Neither branch taken
1
 ;;; -*- Mode: lisp; Syntax: ansi-common-lisp; Base: 10; Package: org.datagraph.spocq.implementation; -*-
2
 
3
 ;;; compile a bgp into a function which matches against an lmdb database and
4
 ;;; performs the nested interation on the results.
5
 
6
 
7
 (in-package :org.datagraph.spocq.implementation)
8
 #|
9
 bgp compilation for simple quad indices contributes solution fields to the algebraic reduction process.
10
 
11
     Field ::= [] | (Solution . Field)
12
     Solution ::= [] | (Var -> Term) x Solution
13
     BGP ::= [] | (Pattern x BGP)
14
     Pattern ::= (Var + Term)*
15
     BGPProcess ::= match(BGP x Quad*) -> Field
16
     match(BGP x Quad*) ::= (Pattern x Quad*) . match(BGP x Quad*)
17
     [] x Quad* ::= []
18
     Pattern x Quad* ::= (Pattern x Quad) + (Pattern x Quad*)
19
     Pattern x [] ::= []
20
     Pattern x Quad ::= Solution + []
21
 
22
 where a pushed filter is present
23
 
24
     FilteredBGPProcess ::= (BGP + Filter) x Quad* -> Field
25
     Filter x Solution ::= Solution + []
26
     Filter x [] ::= []
27
     (BGP + Filter) x Quad* ::= (BGP x Quad*) x Filter
28
     Field x Filter ::= (Filter x (Solution + [])) . (Field x Filter)
29
 
30
 where an event filter is present
31
 1. a statement pattern match yields a solution with an event identifer bound in addition to any other variables in the pattern
32
 2. the variable is specified with a special statement pattern { ?s dydra:event ?eventID }, for which a statement is 
33
    never present in the dataset. it indicates that the temporal index is to be used and the respective value bound in
34
    solutions which share the subject.
35
 3. the functions event-ordinal, event-timestamp, and event-uuid return the respective value when applied to an event identifier
36
 4. this predicate must be present in a bgp to cause it to use an event index.
37
 5. filters which include a single event variable are pushed to the bgp which binds it and used to control the scan.
38
 |#
39
 
40
 
41
   
42
 (defun revision-max-uuid () (rlmdb:revision-record-uuid (transaction-max-revision-record *transaction*)))
43
 (defun revision-min-uuid () (rlmdb:revision-record-uuid (transaction-min-revision-record *transaction*)))
44
 (defun revision-max-timestamp () (rlmdb:revision-record-uuid (transaction-max-revision-record *transaction*)))
45
 (defun revision-min-timestamp () (rlmdb:revision-record-uuid (transaction-min-revision-record *transaction*)))
46
 (defun revision-max-ordinal () (transaction-max-revision-ordinal *transaction*))
47
 (defun revision-min-ordinal () (transaction-min-revision-ordinal *transaction*))
48
 
49
 (unless (fboundp 'temporal-expression-p)
50
   (defun temporal-expression-p (form)
51
     (declare (ignore form))
52
     nil))
53
 
54
 #+(or)  ;; old version sorted
55
 (defgeneric sort-bgp-statements (repository iteration-patterns initial-bindings)
56
   (:documentation
57
    "order bgp patterns and filters to prefer constants early and paths late
58
     ensure that filters and functional predicates follow the respective bindings
59
     and the filters precede predicates")
60
   (:method ((repository lmdb-repository) iteration-patterns initial-bindings)
61
     (labels ((free-variable-p (term)
62
                (and (variable-p term) (not (member term initial-bindings))))
63
              (pattern-cost (stmt)
64
                (+ (if (free-variable-p (statement-subject stmt)) 1 0)
65
                   (if (free-variable-p (statement-object stmt)) 1 0)
66
                   (if (property-path-p (statement-predicate stmt)) 1 0)))
67
              (filters-pattern-p (filter stmt)
68
                "return true iff the filter depends on a variable from the statement"
69
                (let ((stmt-terms (subseq stmt 1 4))
70
                      (filter-vars (expression-variables filter)))
71
                  (intersection filter-vars stmt-terms)))
72
              (functional-predicate-p (stmt)
73
                "return true iff the pattern predicate is a function"
74
                (let ((predicate (third stmt)))
75
                  (and (symbolp predicate) (fboundp predicate))))
76
              (path-statement-p (stmt)
77
                (when (eq (first stmt) 'spocq.a:|triple|)
78
                  (property-path-p (statement-predicate stmt))))
79
              (statement-precedes-p (s1 s2)
80
                ;; temporal filters go first, then patterns by cost, then functional predicates
81
                (ecase (first s1)
82
                  (spocq.a:|triple|
83
                           (ecase (first s2)
84
                             (spocq.a:|triple|
85
                                      (or (and (functional-predicate-p s2) (not (functional-predicate-p s1)))
86
                                          (and (path-statement-p s2) (not (path-statement-p s1)))
87
                                          (< (pattern-cost s1) (pattern-cost s2))))
88
                             (spocq.a:|filter|
89
                                      (and (not (temporal-expression-p (rest s2)))
90
                                           (not (functional-predicate-p s1))
91
                                           (filters-pattern-p s2 s1)))))
92
                  (spocq.a:|filter|
93
                           (ecase (first s2)
94
                             (spocq.a:|triple|
95
                                      (or (temporal-expression-p (rest s1))
96
                                          (functional-predicate-p s2)
97
                                          (not (filters-pattern-p s1 s2))))
98
                             (spocq.a:|filter|
99
                                      (temporal-expression-p (rest s1))))))))
100
       (declare (dynamic-extent #'statement-precedes-p))
101
       (partial-order-sort iteration-patterns #'statement-precedes-p)))
102
    (:method ((repository bitemporal-repository) iteration-patterns initial-bindings)
103
     (let ((patterns (call-next-method))
104
           (bt-patterns ())
105
           (nbt-patterns ()))
106
       (loop for pattern in patterns
107
         do (if (repository-temporal-predicate-p repository (statement-predicate pattern))
108
                (push pattern bt-patterns)
109
                (push pattern nbt-patterns)))
110
       (append bt-patterns nbt-patterns))))
111
 
112
 (defgeneric sort-bgp-statements (repository iteration-patterns initial-bindings)
113
   (:documentation
114
    "order bgp patterns and filters to prefer constants early and paths late
115
     ensure that filters and functional predicates follow the respective bindings
116
     and the filters precede predicates")
117
   ;; first do a dependency-based sort, then reorder for other preferences
118
  (:method ((repository lmdb-repository) iteration-patterns initial-bindings)
119
    (when (null (rest iteration-patterns))
120
      (return-from sort-bgp-statements iteration-patterns))
121
    (let ((filters ())
122
          (triples  ())
123
          (unsortable ())
124
          (filter-triples ()))
125
      (loop for form in iteration-patterns
126
        do (case (first form)
127
             (spocq.a:|triple| (push form triples))
128
             (spocq.a:|filter| (push form filters))
129
             (t
130
              (log-warn "sort-bgp-statements: unsortable form: ~s" form)
131
              (push form unsortable))))
132
      (setf triples (reverse triples))
133
      (labels ((free-variable-p (term)
134
                 (and (variable-p term) (not (member term initial-bindings))))
135
               (pattern-cost (stmt)
136
                 (+ (if (free-variable-p (statement-subject stmt)) 1 0)
137
                    (if (free-variable-p (statement-object stmt)) 1 0)
138
                    (if (property-path-p (statement-predicate stmt)) 1 0)))
139
               (functional-predicate-p (stmt)
140
                 "return true iff the pattern predicate is a function"
141
                 (let ((predicate (third stmt)))
142
                   (and (symbolp predicate) (fboundp predicate))))
143
               (path-statement-p (stmt)
144
                 (when (eq (first stmt) 'spocq.a:|triple|)
145
                   (property-path-p (statement-predicate stmt))))
146
               (annotation (stmt)
147
                 (let ((cost (pattern-cost stmt))
148
                       (function-p (functional-predicate-p stmt))
149
                       (path-p (path-statement-p stmt)))
150
                   (append `(:cost ,cost)
151
                           (when function-p '(:functional t))
152
                           (when path-p '(:path t)))))
153
               (precedes-p (k1 k2)
154
                 (flet ((functional-p (annotation) (getf annotation :functional))
155
                        (path-p (annotation) (getf annotation :path))
156
                        (cost-of (annotation) (getf annotation :cost)))
157
                   (or  (and (functional-p k2) (not (functional-p k1)))
158
                        (and (path-p k2) (not (path-p k1)))
159
                        (< (cost-of k1) (cost-of k2)))))
160
               (filters-pattern-p (filter stmt)
161
                 "return true iff the filter depends on a variable from the statement"
162
                 (let ((stmt-terms (subseq stmt 1 4))
163
                       (filter-vars (expression-variables filter)))
164
                   (intersection filter-vars stmt-terms))))
165
        (setf triples (sort triples #'precedes-p :key #'annotation))
166
        (append
167
         (cond (filters
168
                (setf triples (reverse triples))
169
                (setf filter-triples
170
                      (loop for filter in filters
171
                        collect (cons filter (loop for triple in triples when (filters-pattern-p filter triple) collect triple))))
172
                (let ((independents ())
173
                      (dependents ()))
174
                  (loop for (filter . filtered-triples) in filter-triples
175
                    do (if (or (null filtered-triples) (temporal-expression-p (rest filter)))
176
                           (push filter independents)
177
                           (push (cons filter filtered-triples) dependents)))
178
                  ;; (print (list independents dependents))
179
                  (let* ((result (append
180
                                  ;; add each triple, preceded by the eventual dependents
181
                                  (loop for triple in triples
182
                                    append (loop for dependency in dependents
183
                                             for (filter . filtered-triples) = dependency
184
                                             when (find triple filtered-triples)
185
                                             do (setf (rest dependency) nil)
186
                                             and collect filter)
187
                                    collect triple)
188
                                  ;; add all independent filters
189
                                  independents)))
190
                    ;; check that nothing was lost
191
                    (let ((lost (loop for dependent in dependents if (rest dependent) collect dependent)))
192
                      (when lost (warn "lost filter: ~s" lost)))
193
                    ;; reorder to put dependents later
194
                    (reverse result))))
195
               (t
196
                triples))
197
         unsortable))))
198
 
199
    (:method ((repository bitemporal-repository) iteration-patterns initial-bindings)
200
     (let ((patterns (call-next-method))
201
           (bt-patterns ())
202
           (nbt-patterns ()))
203
       (loop for pattern in patterns
204
         do (if (repository-temporal-predicate-p repository (statement-predicate pattern))
205
                (push pattern bt-patterns)
206
                (push pattern nbt-patterns)))
207
       (append bt-patterns nbt-patterns))))
208
 
209
 
210
 (defmethod rlmdb:map-repository-statements (operator (repository lmdb-repository) quad-pattern &rest args)
211
   (declare (dynamic-extent args))
212
   (apply #'rlmdb:map-repository-statements operator (repository-lmdb-repository repository)
213
          quad-pattern
214
          args))
215
 
216
 (defmethod rlmdb:map-repository-statements (operator (transaction lmdb-transaction) (quad-pattern t)
217
                                                      &rest args)
218
   (apply #'rlmdb:map-repository-statements operator (transaction-revision transaction)
219
          quad-pattern
220
          args))
221
 
222
 (defmethod rlmdb:map-repository-statements (operator (revision lmdb-revision) (quad-pattern t)
223
                                                      &rest args
224
                                                      &key
225
                                                      revision-predicate
226
                                                      &allow-other-keys)
227
   (apply #'rlmdb:map-repository-statements operator (repository-lmdb-repository revision)
228
          quad-pattern
229
          :revision-predicate (compute-revision-predicate (list :first (revision-min-revision-ordinal revision)
230
                                                                :last (revision-max-revision-ordinal revision)))
231
          args))
232
 
233
 
234
 ;;(defparameter *revision-relation* nil)
235
                             
236
 (defgeneric resolve-version-constraint (repository expression)
237
   (:method ((repository lmdb-repository) (expression list))
238
     (flet ((resolve-revision-designator (designator)
239
              (typecase designator
240
                (integer designator)
241
                (string (or (rlmdb:get-revision-ordinal repository designator)
242
                            (spocq.e:revision-not-found-error :identifier designator)))
243
                ((satisfies variable-p) `(rlmdb:term-value ,designator))
244
                (t designator))))
245
       (declare (dynamic-extent #'resolve-revision-designator))
246
       (map-tree #'resolve-revision-designator expression))))
247
 
248
 (defun extend-triple (triple graph)
249
   (destructuring-bind (tag s p o . rest) triple
250
     (declare (ignore tag rest))
251
     `(spocq.a:|quad| ,s ,p ,o ,graph)))
252
 
253
 (defun compute-term-precedence (triple-pattern sort-order graph-variable event-variable)
254
   (destructuring-bind (tag s p o c . rest) triple-pattern
255
     (declare (ignore tag rest))
256
     (let ((precedence ()))
257
       (unless (variable-p s) (push :subject precedence))
258
       (unless (variable-p p) (push :predicate precedence))
259
       (unless (variable-p o) (push :object precedence))
260
       (unless (variable-p c) (push :graph precedence))
261
       (loop for variable in sort-order
262
         if (eq variable s)
263
         do (pushnew :subject precedence)
264
         else if (eq variable p)
265
         do (pushnew :predicate precedence)
266
         else if (eq variable o)
267
         do (pushnew :object precedence)
268
         else if (eq variable graph-variable)
269
         do (pushnew :graph precedence)
270
         else if (eq variable event-variable)
271
         do (push :event precedence))
272
       (reverse precedence))))
273
 ;;; (compute-term-precedence '(spocq.a:|quad| ?::s <http://example.org/p> ?::value ?::g) '(?::s ?::e) '?::g '?::e)
274
 ;;; (compute-term-precedence '(spocq.a:|quad| ?::s <http://example.org/p> ?::value <urn:dydra:all>) '(?::e ?::s) '?::g '?::e)
275
 
276
 (defgeneric pattern-scan-order (pattern sort-order)
277
   (:documentation "Given a wild pattern and a sort order, return a list of statement roles
278
    order as per the sort order")
279
   (:method ((pattern t) (sort-order list))
280
     (loop for dimension in sort-order
281
       for position = (statement-variable-position pattern dimension)
282
       when position
283
       collect position)))
284
 
285
 (defgeneric statement-variable-position (pattern variable)
286
   (:method ((pattern cons) variable)
287
     (let ((position (position variable (rest pattern))))
288
       (when position (aref #(:subject :predicate :object :graph) position))))
289
   (:method ((pattern spocq:triple) variable)
290
     (cond ((eq variable (spocq:triple-subject pattern)) :subject)
291
           ((eq variable (spocq:triple-predicate pattern)) :predicate)
292
           ((eq variable (spocq:triple-object pattern)) :object)))
293
   (:method ((pattern spocq:quad) variable)
294
     (cond ((eq variable (spocq:quad-subject pattern)) :subject)
295
           ((eq variable (spocq:quad-predicate pattern)) :predicate)
296
           ((eq variable (spocq:quad-object pattern)) :object)
297
           ((eq variable (spocq:quad-graph pattern)) :graph)))
298
   (:method ((pattern vector) variable)
299
     (cond ((eq variable (aref pattern 0)) :subject)
300
           ((eq variable (aref pattern 1)) :predicate)
301
           ((eq variable (aref pattern 2)) :object)
302
           ((and (= (length pattern) 4(eq variable (aref pattern 3))) :graph))))
303
 
304
 
305
 (defgeneric compute-lmdb-statement-lambda (repository body &key) )
306
 
307
 (defgeneric compute-bgp-star-lambda (repository body &key) )
308
 
309
 (defmethod compute-bgp-lambda ((repository lmdb-repository) body &rest args &key &allow-other-keys) ;;!!! temporaril
310
   (declare (dynamic-extent args))
311
   (when (assoc 'spocq.a:|quad| body)
312
     (error "invalid bpg pattern: ~s" body))
313
   (cond ((star-bgp-p body)
314
          (apply #'compute-bgp-star-lambda repository body args))
315
         (t
316
          (apply #'compute-lmdb-statement-lambda repository body args))))
317
 
318
 (defmethod compute-lmdb-statement-lambda ((repository lmdb-repository) (body list) &key
319
                                                       (base-dimensions ())          ; initial solution field variables
320
                                                       (projection-dimensions (bgp-projected-dimensions body))
321
                                                       (wildcard-term (repository-wildcard-term repository))
322
                                                       (default-context-term (repository-default-context-term repository))
323
                                                       (named-contexts-term (repository-named-contexts-term repository))
324
                                                       graph    ; if in a graph clause, then either a variable or a literal
325
                                                       (dataset-graphs nil)
326
                                                       (named-graphs (dataset-named-graphs dataset-graphs))
327
                                                       (default-graphs (dataset-default-graphs dataset-graphs))        
328
                                                       (graphs nil)
329
                                                       (variables (if (variable-p graph)
330
                                                                      (cons graph (expression-variables body))
331
                                                                      (expression-variables body)))
332
                                                       (dynamic-variables ())
333
                                                       (transaction *transaction*)
334
                                           (trace *compute-bgp-lambda.trace*))
335
   "Generate a matching operator from a BGP for direct LMBD access path.
336
 
337
   The base logic is the same as for rdfcache-based BGP evaluation:
338
   - graphs are recognized as for multigraph streaming
339
   - initial constraints are accepted
340
   "
341
 
342
   ;; ensure uniqness
343
   (setf variables (remove-duplicates variables :from-end t))
344
   (when graphs
345
     (warn "graphs ignored ~s." graphs)
346
     (log-warn "graphs ignored ~s." graphs))
347
   ;; use the dimensions provided in the call
348
   ;; (setf projection-dimensions (expression-dimensions body))
349
   (let* (;; if the query had a literal graph term, provide a variable
350
          (graph-variable (if (variable-p graph) graph '.graph))
351
          ;; note if the graph variable is among the initial solutions
352
          (initial-solution-graph-variable (find graph base-dimensions))
353
          (event-pattern (find-if #'(lambda (form) (triple-form-p form) (eq (third form) *predicate.event*)) body))
354
          (event-variable (if (and event-pattern (variable-p (fourth event-pattern)))
355
                              (fourth event-pattern)
356
                              (cons-variable "event-")))
357
          ;; indicate whether the graph needs to be set on first query
358
          ;; (set-graph-variable (and (variable-p graph) (not initial-solution-graph-variable)))
359
          ;; extract just the expressions to be included in the matching and bindings propagation code
360
          (iteration-patterns (remove-if-not #'bgp-pattern-form-p (remove event-pattern body)))
361
          (id (second (assoc 'spocq.a::|id| body)))
362
          ;; extract and consolidate anu declaration clauses
363
          (declarations (reduce #'append (mapcar #'rest (remove 'spocq.a::|declare| body :test-not #'eq :key #'first))))
364
          ;; get the a-list of variables which were inferred to be related through a sameTerm filter constraint
365
          ;; in order to mirror any binding/setting
366
          (equivalents (rest (assoc 'spocq.a::|equivalents| body)))
367
          (pattern-count 1)
368
          ;;!!! still builds state for triples with paths even though cursors and terms are handled out-of-line
369
          ;; adjust bounds to account for per-thread cursors
370
          (match-counters (loop for i from 0 below pattern-count collect (gensym "COUNTER")))
371
          (temp-match-counters match-counters)
372
          (slice (rest (assoc 'spocq.a:|slice| body)))
373
          (perform-slice (or (getf slice :start) (getf slice :offset)))
374
          (projection-variable-count (length projection-dimensions))
375
          (equivalent-variables (mapcar #'first equivalents))
376
          (collection-variables (difference-dimensions projection-dimensions equivalent-variables))
377
          (blank-nodes (expression-blank-nodes iteration-patterns))
378
          (blank-node-map (loop for node in blank-nodes
379
                            collect (cons node (if (spocq:blank-node-constant-p node)
380
                                                   (rlmdb:value-term-number node)
381
                                                   (cons-variable)))))
382
          (default-context-term-number (rlmdb:value-term-number default-context-term))
383
          (named-contexts-term-number (rlmdb:value-term-number named-contexts-term))
384
          ;; track the match nesting; the outer loop only must bind the graph
385
          (iteration-level 0)
386
          (base-bindings base-dimensions)
387
          (initial-bindings (union-dimensions base-bindings dynamic-variables))
388
          (term-id-map (make-hash-table :test 'eql))
389
          (id-term-map (make-hash-table :test 'eql))
390
          (event-filter (assoc 'spocq.a::|event-filter| body))
391
          (sort-order (assoc 'spocq.a::|sort-order| body))
392
          )
393
 
394
     (labels ((intern-if-constant (object)
395
                (cond ((variable-p object)
396
                       object)
397
                      ((spocq:blank-node-p object)
398
                       (or (rest (assoc object blank-node-map))
399
                           (error "lost a blank-node: ~a" object)))
400
                      ((property-path-p object)
401
                       (rlmdb:intern-property-path object))
402
                      (t  ;; for spo only
403
                       (let ((number (rlmdb:value-term-number object)))
404
                         (setf (gethash number id-term-map) object)
405
                         (setf (gethash object term-id-map) number)))))
406
              (intern-graph-if-constant (object)
407
                (cond ((variable-p object)
408
                       object)
409
                      (t
410
                       (rlmdb:graph-term-number object :allow-all t))))
411
              (intern-predicate (expression)
412
                (typecase expression
413
                  (cons (cons (first expression) (mapcar #'intern-predicate (rest expression))))
414
                  (t (rlmdb:value-term-number expression))))
415
              (index-statement-p (statement)
416
                (and (consp statement) (member :test statement)))
417
              (compute-filter-continuation (triples accumulated-variables)
418
                (let* ((form (first triples))
419
                       (cc (compute-next-continuation (rest triples) accumulated-variables))
420
                       (test-expression (second form))
421
                       (test-variables (expression-variables test-expression))
422
                       (test-expression `(handler-case (ebv ,test-expression) (error () nil))))
423
                  (if test-variables
424
                    (let ((test-aliases (loop for variable in test-variables collect (make-symbol (symbol-name variable)))))
425
                      `(if ((lambda ,test-aliases
426
                              (symbol-macrolet ,(loop for variable in test-variables
427
                                                  for alias in test-aliases
428
                                                  collect `(,variable (rlmdb:term-number-value ,alias)))
429
                                (trace-bgp bgp.filter ',test-expression ',test-variables (list ,@test-aliases) (list ,@test-variables))
430
                                ,test-expression))
431
                            ,@test-variables)
432
                         ,cc
433
                         ;; must provide a true result if the filter fails for the scan to continue
434
                         t))
435
                    `(if ,test-expression ,cc t))))
436
              (compute-statement-continuation (triples accumulated-variables)
437
                (let* ((form (first triples))
438
                       (dimensions (cons graph-variable (statement-dimensions form)))
439
                       ;; the first match must always use the graph term - either a constant from/from-named iri
440
                       ;; or one of the wild-card, designators default/named/all.
441
                       ;; if wild, then the result matters, but if constant, then the result does not.
442
                       ;; all subsequent iterations use the result from the first match and do not need to rebind
443
                       (is-first-iteration (= 1 (incf iteration-level)))
444
                       (last-triple-p (null (assoc 'spocq.a:|triple| (rest triples))))
445
                       ;; set the graph from each result for the outermost iteration only. that means:
446
                       ;; trying w/o this: nb. in contrast to the term-pointer version, which sets the pointer
447
                       ;; once only, the term-id based version must retrieve the id each iteration
448
                       (match-result-arguments (loop ;; for term in (cons graph-variable (statement-terms form))
449
                                                     for term in dimensions
450
                                                     collect (cond ((or (not (variable-p term)) (member term accumulated-variables))
451
                                                                    (gensym "constant"))
452
                                                                   ((and (eq term graph-variable)
453
                                                                         (not is-first-iteration))
454
                                                                    (gensym "constant-graph"))
455
                                                                   (t
456
                                                                    term))))
457
                       (uniqued-result-arguments (loop for vars on match-result-arguments
458
                                                       for v in match-result-arguments
459
                                                       collect (if (member v (rest vars))
460
                                                                 (gensym)
461
                                                                 v))))
462
                  (labels (#+(or) (local-variable-p (v) (member v match-result-arguments))
463
                           #+(or) (wild-variable-p (v) (and (local-variable-p v) (not (eq v graph)))))
464
                    (let* ((match-pattern (cons graph-variable 
465
                                                ;; replace a variable which was not yet returned from a match
466
                                                ;; (loop for term in (statement-terms form) collect (if (wild-variable-p term) wildcard-term term))
467
                                                (statement-terms form)
468
                                                ))
469
                           (concrete-match-pattern (let ((graph-term (first match-pattern)))
470
                                                     (cons graph-term (loop for term in (rest match-pattern)
471
                                                                            collect (if (eq term graph-term)
472
                                                                                      `(max 0 ,term) ; cannot pass non-concrete term
473
                                                                                      term)))))
474
                           ;; replaced with rlmdb call
475
                           ;; (match-form `(match-quad-cursor transaction-record ,@concrete-match-pattern *match-target-graph* nil nil))
476
                           (count-form `(count-quad-cursor transaction-record ,@concrete-match-pattern))
477
                           (accumulated-variables (append accumulated-variables (remove-if-not #'symbol-package match-result-arguments)))
478
                           (body (cond ((property-path-p (third match-pattern))
479
                                        ;; interpret a property path through an out-of-line call to the path matcher
480
                                        (let* ((path-continuation (gensym "PATH-CONTINUATION"))
481
                                               (ignored-variables (remove-if #'symbol-package uniqued-result-arguments))
482
                                               (match-aliases (loop for v in match-result-arguments
483
                                                                    for v-eq = (rassoc v equivalents)
484
                                                                    when v-eq
485
                                                                    collect v-eq
486
                                                                    and do (setf equivalents (remove v-eq equivalents))))
487
                                               (continuation-form (compute-next-continuation (rest triples) accumulated-variables)))
488
                                          
489
                                          ;; constrain aliased variables
490
                                          (unless (equal uniqued-result-arguments match-result-arguments)
491
                                            (setf continuation-form
492
                                                  `(if (and ,@(loop for v in match-result-arguments
493
                                                                      for u in uniqued-result-arguments
494
                                                                      unless (eq u v)
495
                                                                      collect `(= ,u ,v)))
496
                                                     ,continuation-form
497
                                                     t)))
498
                                          (when match-aliases
499
                                            #+(or)       ; eliminate alias-rebinding in favor of original dimensions
500
                                            (setf continuation-form
501
                                                  `(let ,(loop for (alias . v) in match-aliases
502
                                                               collect `(,alias ,v))
503
                                                     ,continuation-form)))
504
                                          `(flet ((,path-continuation (,@uniqued-result-arguments)
505
                                                    ,@(when ignored-variables `((declare (ignorable ,@ignored-variables))))
506
                                                    (trace-bgp bgp.pp-matched ,@uniqued-result-arguments)
507
                                                    (unless (task-active-p *query*)
508
                                                      (log-debug "bgp premature completion: ~a" *query*)
509
                                                      (complete-solutions))
510
                                                    ;; when coercing solution graph, restore the match to the initial graphs
511
                                                    ,@(when is-first-iteration
512
                                                        `((note-graph-reference ,(first uniqued-result-arguments))))
513
                                                    ,(if (and perform-slice last-triple-p)
514
                                                         ;;when here, to return nil to statement mapper
515
                                                         `(when (minusp (decf solution-offset))
516
                                                            ,continuation-form)
517
                                                         continuation-form)))
518
                                             (declare (dynamic-extent #',path-continuation))
519
                                             (trace-bgp bgp.pp-to-match ',concrete-match-pattern)
520
                                             (when *match-target-graph* (setf ,graph-variable effective-dataset-graphs))
521
                                             (match-property-path transaction ,@concrete-match-pattern #',path-continuation))))
522
                                       
523
                                       ((active-verb-p (third match-pattern))
524
                                        ;; interpret a property function through an out-of-line call to funcall-extension
525
                                        (let* ((pattern-predicate  (third match-pattern))
526
                                               (match-result-arguments (active-verb-results pattern-predicate))
527
                                               ;; no identical result variables (uniqued-result-arguments (unique-result-arguments match-result-arguments))
528
                                               (match-parameters (active-verb-parameters pattern-predicate))
529
                                               (accumulated-variables (append accumulated-variables (remove-if-not #'symbol-package match-result-arguments)))
530
                                               (extension-continuation (gensym "ACTIVE-VERB-CONTINUATION"))
531
                                               (ignored-variables (remove-if #'symbol-package match-result-arguments))
532
                                               (continuation-form (compute-next-continuation (rest triples) accumulated-variables)))
533
                                          
534
                                          `(flet ((,extension-continuation (,@match-result-arguments &rest extra-results)
535
                                                    (declare (ignore extra-results))
536
                                                    ,@(when ignored-variables `((declare (ignorable ,@ignored-variables))))
537
                                                    (trace-bgp bgp.active-verb-matched ,@match-result-arguments)
538
                                                    (unless (task-active-p *query*)
539
                                                      (log-debug "bgp premature completion: ~a" *query*)
540
                                                      (complete-solutions))
541
                                                    ,(if (and perform-slice last-triple-p)
542
                                                         ;;when here, to return nil to statement mapper
543
                                                         `(when (minusp (decf solution-offset))
544
                                                            ,continuation-form)
545
                                                         continuation-form)))
546
                                             (declare (dynamic-extent #',extension-continuation))
547
                                             (trace-bgp bgp.extension ',concrete-match-pattern)
548
                                             (when *match-target-graph* (setf ,graph-variable effective-dataset-graphs))
549
                                             (invoke-active-verb ,pattern-predicate
550
                                                                 transaction
551
                                                                 #',extension-continuation
552
                                                                 ,@match-parameters))))
553
 
554
                                       ((extension-operator-p (third match-pattern))
555
                                        ;; interpret a property function through an out-of-line call to funcall-extension
556
                                        (let* ((extension-continuation (gensym "EXTENSION-CONTINUATION"))
557
                                               (ignored-variables (remove-if #'symbol-package uniqued-result-arguments))
558
                                               (match-aliases (loop for v in match-result-arguments
559
                                                                    for v-eq = (rassoc v equivalents)
560
                                                                    when v-eq
561
                                                                    collect v-eq
562
                                                                    and do (setf equivalents (remove v-eq equivalents))))
563
                                               (continuation-form (compute-next-continuation (rest triples) accumulated-variables)))
564
                                          
565
                                          ;; constrain aliased variables
566
                                          (unless (equal uniqued-result-arguments match-result-arguments)
567
                                            (setf continuation-form
568
                                                  `(if (and ,@(loop for v in match-result-arguments
569
                                                                      for u in uniqued-result-arguments
570
                                                                      unless (eq u v)
571
                                                                      collect `(= ,u ,v)))
572
                                                     ,continuation-form
573
                                                     t)))
574
                                          (when match-aliases
575
                                            #+(or)       ; eliminate alias-rebinding in favor of original dimensions
576
                                            (setf continuation-form
577
                                                  `(let ,(loop for (alias . v) in match-aliases
578
                                                               collect `(,alias ,v))
579
                                                     ,continuation-form)))
580
                                          `(flet ((,extension-continuation (,@uniqued-result-arguments)
581
                                                    ,@(when ignored-variables `((declare (ignorable ,@ignored-variables))))
582
                                                    (trace-bgp bgp.extension-matched ,@uniqued-result-arguments)
583
                                                    (unless (task-active-p *query*)
584
                                                      (log-debug "bgp premature completion: ~a" *query*)
585
                                                      (complete-solutions))
586
                                                    ;; when coercing solution graph, restore the match to the initial graphs
587
                                                    ,@(when is-first-iteration
588
                                                        `((note-graph-reference ,(first uniqued-result-arguments))))
589
                                                    ,(if (and perform-slice last-triple-p)
590
                                                         ;;when here, to return nil to statement mapper
591
                                                         `(when (minusp (decf solution-offset))
592
                                                            ,continuation-form)
593
                                                         continuation-form)))
594
                                             (declare (dynamic-extent #',extension-continuation))
595
                                             (trace-bgp bgp.extension ',concrete-match-pattern)
596
                                             (when *match-target-graph* (setf ,graph-variable effective-dataset-graphs))
597
                                             (funcall-extension transaction ,@concrete-match-pattern #',extension-continuation))))
598
 
599
                                       ((index-statement-p form)
600
                                        ;; this is here, but meaningless as the work in dydra-ndk was never done
601
                                        (let* ((options (member-if #'keywordp form))
602
                                               (test (getf options :test))
603
                                               (index (getf options :index)))
604
                                          (declare (ignore index))       ; held in case one wants to reintroduce the uri
605
                                          (let ((match-index-form `(match-quad-cursor transaction-record ,@concrete-match-pattern target-graph nil
606
                                                                                      ',(intern-predicate test))))
607
                                            ;; indexed pattern match
608
                                            `(progn (trace-bgp bgp.cspo-to-match-index ',(nthcdr 3 match-index-form))
609
                                               (incf match-requests)
610
                                               (let ((%match-cursor nil))
611
                                                 (when *match-target-graph* (setf ,graph-variable effective-dataset-graphs))
612
                                                 (unwind-protect
613
                                                     (if (setf %match-cursor  ,match-index-form)
614
                                                       (block :cursor-loop
615
                                                         ;; the innermost loop skips past the solution offset
616
                                                         ,@(when (and perform-slice last-triple-p)
617
                                                             `((when (and (plusp solution-offset)
618
                                                                          (plusp (decf solution-offset (dydra-ndk::quad-cursor-skip %match-cursor solution-offset))))
619
                                                                 (return-from :cursor-loop))))
620
                                                         (loop
621
                                                           (unless (dydra-ndk::quad-cursor-next %match-cursor) (return))
622
                                                           (incf match-results)
623
                                                           (incf ,(pop temp-match-counters))
624
                                                           (let* (,@(loop for term in match-result-arguments
625
                                                                      for uniqued-term in uniqued-result-arguments
626
                                                                      for accessor in '(dydra-ndk::quad-cursor-graph-id
627
                                                                                         dydra-ndk::quad-cursor-subject-id
628
                                                                                         dydra-ndk::quad-cursor-predicate-id
629
                                                                                         dydra-ndk::quad-cursor-object-id)
630
                                                                      if (symbol-package term) collect `(,uniqued-term (,accessor %match-cursor))))
631
                                                             (declare (ignorable ,@(remove-if-not #'symbol-package uniqued-result-arguments)))
632
                                                             ;; !once! the new bindings are established, compute the next continuation
633
                                                             ,@(when is-first-iteration
634
                                                                  `((note-graph-reference (dydra-ndk::quad-cursor-graph-id %match-cursor))))
635
                                                             ,(let ((form (compute-next-continuation (rest triples) accumulated-variables)))
636
                                                                (if (equal uniqued-result-arguments match-result-arguments)
637
                                                                    form
638
                                                                    `(if (and ,@(loop for v in match-result-arguments
639
                                                                                    for u in uniqued-result-arguments
640
                                                                                    unless (eq u v)
641
                                                                                    collect `(= ,u ,v)))
642
                                                                       ,form
643
                                                                       t))))))
644
                                                       t)
645
                                                   (when %match-cursor
646
                                                     (dydra-ndk::free-quad-cursor %match-cursor))))))))
647
 
648
                                       ((or (find-if #'variable-p match-pattern)
649
                                            graph
650
                                            (not (= default-context-term-number rlmdb:*default-context-number*)))
651
                                        (destructuring-bind (g s p o) concrete-match-pattern
652
                                          (let* ((lmdb-continuation (gensym "LMDB-CONTINUATION"))
653
                                                 (ignored-variables (remove-if #'symbol-package uniqued-result-arguments))
654
                                                 (continuation-form (compute-next-continuation (rest triples) accumulated-variables))
655
                                                 (quad-pattern `(vector ,g ,s ,p ,o))
656
                                                 (filter (getf (statement-properties form) :filter))
657
                                                 (temporal-predicate (when filter
658
                                                                       (compute-domain-predicate-form (cons 'spocq.a:|&&| filter) o)))
659
                                                 (apply-sort-order? (and is-first-iteration (wild-quad-pattern-p form) sort-order)))
660
                                            ;; constrain aliased variables
661
                                          (unless (equal uniqued-result-arguments match-result-arguments)
662
                                            (setf continuation-form
663
                                                  `(if (and ,@(loop for v in match-result-arguments
664
                                                                for u in uniqued-result-arguments
665
                                                                unless (eq u v)
666
                                                                collect `(= ,u ,v)))
667
                                                       ,continuation-form
668
                                                       t)))
669
                                            ;; scan over matched triples
670
                                            ;; the implementation resolves to the pattern-specific index
671
                                            ;; which constitutes adjacency appropriate for the pattern and
672
                                            ;; structure appropriate for the predicate.
673
                                            ;; anyconstraining properties are passed to the implementation to be interpreted
674
                                            `(flet ((,lmdb-continuation (%quad)
675
                                                      (let ,(loop for var in uniqued-result-arguments
676
                                                              for index from 0
677
                                                              collect `(,var (cffi:mem-aref %quad 'rlmdb:term-id ,index)))
678
                                                        ,@(when ignored-variables `((declare (ignorable ,@ignored-variables))))
679
                                                        (trace-bgp bgp.lmdb-matched ,@uniqued-result-arguments)
680
                                                        (unless (task-active-p *query*)
681
                                                          (log-debug "bgp premature completion: ~a" *query*)
682
                                                          (complete-solutions))
683
                                                        ;; when coercing solution graph, restore the match to the initial graphs
684
                                                        ,@(when is-first-iteration
685
                                                            `((note-graph-reference ,(first uniqued-result-arguments))))
686
                                                        ,continuation-form)))
687
                                               (declare (dynamic-extent #',lmdb-continuation))
688
                                               ;; if the first iteration and the intended sort order can be satisfied
689
                                               ;; indicate that in the result channel
690
                                               ,@(when (and apply-sort-order?
691
                                                            (rlmdb.i::repository-scan-database repository sort-order))
692
                                                   `((when (channel-p bgp-destination)
693
                                                       (setf (channel-sort-dimensions bgp-destination)
694
                                                             ',(pattern-scan-order form sort-order)))))
695
                                               (trace-bgp bgp.lmdb-to-match ',concrete-match-pattern)
696
                                               (when *match-target-graph* (setf ,graph-variable effective-dataset-graphs))
697
                                               (rlmdb:map-repository-statements #',lmdb-continuation transaction
698
                                                                                ,quad-pattern
699
                                                                                :revision-predicate (compute-revision-predicate transaction)
700
                                                                                ,@(when temporal-predicate
701
                                                                                    `(:temporal-predicate ,temporal-predicate))
702
                                                                                ,@(when apply-sort-order?
703
                                                                                    `(:scan-order ',(pattern-scan-order form sort-order))))))))
704
                                       (t
705
                                        ;; if all are constant, just count
706
                                        (let ((continuation-form (compute-next-continuation (rest triples) accumulated-variables)))
707
                                          (unless (equal uniqued-result-arguments match-result-arguments)
708
                                            (setf continuation-form
709
                                                  `(if (and ,@(loop for v in match-result-arguments
710
                                                                      for u in uniqued-result-arguments
711
                                                                      unless (eq u v)
712
                                                                      collect `(= ,u ,v)))
713
                                                     ,continuation-form
714
                                                     t)))
715
                                          (if (and perform-slice last-triple-p)
716
                                            ;; constant slice
717
                                            `(progn (incf match-requests)
718
                                                    (let ((count ,count-form))
719
                                                      (cond ((and (plusp count)
720
                                                                  (not (plusp (decf solution-offset count))))
721
                                                             (incf match-results)
722
                                                             (incf ,(pop temp-match-counters))
723
                                                             ,continuation-form)
724
                                                            (t t))))
725
                                            ;; constant match
726
                                            `(progn (incf match-requests)
727
                                               (cond ((plusp ,count-form)
728
                                                      (incf match-results)
729
                                                      (incf ,(pop temp-match-counters))
730
                                                      ,continuation-form)
731
                                                     (t t)))))))))
732
                      body))))
733
              
734
              (compute-base-continuation ()
735
                ;; set the next solution row
736
                (let ((collect-form `(collect-solution ,@collection-variables)))
737
                  (if perform-slice
738
                      `(if (minusp (decf solution-offset))
739
                           (if (and solution-count (minusp (decf solution-count)))
740
                               (complete-solutions)
741
                               ,collect-form)
742
                           solution-offset)
743
                      collect-form)))
744
              
745
              (compute-next-continuation (triples accumulated-variables)
746
                (if triples
747
                  (let* ((form (first triples)))         ; (triple ?s ?p ?o)
748
                    (ecase (first form)
749
                      (spocq.a:|triple|
750
                       ;; compule a triple/quad into a query/next continuation
751
                       (compute-statement-continuation triples accumulated-variables))
752
                      (spocq.a:|filter|
753
                       ;; compile a filter into a constraint on the solutions which pass through it
754
                       (compute-filter-continuation triples accumulated-variables))))
755
                  (compute-base-continuation)))
756
              (compute-collated-continuation (ts-triples accumulated-variables)
757
                ;; the general intent is to collate "entities" - events or resources.
758
                ;; the match/scan process for given patterns varies according to the their specificity,
759
                ;; any event filter, and any sorting.
760
                ;; this compilation step prepares the arguments.
761
                ;; the implementation is associated with the respective index.
762
                ;; at this level, the patterns for (g.s.p.*)+filter combinations are collated.
763
                ;; predicate sets with wild subject and graph are the intended case.
764
                ;; the compilation result dictates the arguments to the mapping operator and the
765
                ;; continuation signature
766
                ;;
767
                ;; the purpose of the indices is not to provide a quad pattern solution stream, as
768
                ;; from a general hex index respective pattern specificity, but to arrange for
769
                ;; an optimally ordered stream for certain pattern/event-filter/sort-clause
770
                ;; combinations, such as the following alternatives
771
                ;; - egsp : temporal complex event streaming ce/se/subject/predicate (eg a monitored object
772
                ;; - esgp : temporal subject streaming (eq. a cluster of multi-valued sensors
773
                ;; - sgep : subject temporal streaming (eg. a single multi-valued sensor
774
                ;; - psge : property streaming
775
                ;; - sgpe
776
                ;; whereby the relation is g-s*, that is, for events, a given event comprises multiple subject,
777
                ;; but the subject is present in just one event graph per revision
778
                ;;
779
                ;; eg. dimensions . filter       . sort
780
                ;;
781
                ;;      p*        . event bounds? . event identifier
782
                ;; given either event predicates or an event id filter,
783
                ;; - if no predicate is given, use all ts predicates
784
                ;; - a sort context indicates which order, by default: egsp
785
                ;;
786
                ;;      s.p*      . event bounds . event identifier
787
                ;; given a specific subject with an event bounds filter or sort by event id
788
                ;; - if the sort context indicates a predicate : spge
789
                ;; - by default esgp
790
                ;;
791
                ;;      e*        . event bounds . -
792
                ;; for a specific complex event or all of them over a range
793
                ;; - the sort context indicates precedence : egsp, epgs
794
                ;;
795
                ;;      g.s*.p*   . -            . -                    either s specific simple event or all of a class
796
                ;; a specific simple event or event class over time
797
                ;;
798
                ;; - sort and group the patterns by subject.
799
                ;; -- create a continuation with variables to bind the
800
                ;;    event(ce) identifier
801
                ;;    the graph(se) identifier
802
                ;;    the collected subject variables
803
                ;;    all respectively collected object variables
804
                ;; -- translate the event filter in to a predicate which closes over any variables
805
                ;;    which are free wrt the patterns
806
                ;;
807
                ;; in the simpler case of subject-star resources
808
                ;;
809
                ;;      s.p*      . star pattern scans all predicates
810
                ;;
811
                ;; arrange for the continuation to accept a vector of keyed term numbers.
812
                ;; this applies when the store does not declare the predicates t be scanned
813
                ;;
814
                ;; invoke the repository map operator, capture the results and continue with them
815
                ;; as for other patterns.
816
                ;; any event predicate must be generated here in order to close over matching state,
817
                ;;
818
                ;; as the last step, if the event term is bound or used in a
819
                ;; filter, it must be mapped to the respective object's term
820
                ;; number in order to be in the correct domain. the actual value
821
                ;; is not
822
 
823
                (multiple-value-bind (ts-subject ts-predicates ts-variables)
824
                                     (loop for (tag s p o) in ts-triples
825
                                       for subject = s
826
                                       collect p into predicates
827
                                       collect o into variables
828
                                       finally (return (values subject predicates variables)))
829
                  (when *compute-bgp-lambda.debug*
830
                    (print (list :time-series-continuation ts-subject ts-predicates ts-variables)))
831
                  (let* ((lmdb-continuation (gensym "LMDB-TS-CONTINUATION"))
832
                         (is-first-iteration (= 1 (incf iteration-level)))
833
                         (accumulated-variables (append accumulated-variables ts-variables))
834
                         (match-result-arguments ts-variables)
835
                         (uniqued-result-arguments (loop for vars on match-result-arguments
836
                                                     for v in match-result-arguments
837
                                                     collect (if (member v (rest vars))
838
                                                                 (gensym)
839
                                                                 v)))
840
                         (ignored-variables (remove-if #'symbol-package uniqued-result-arguments))
841
                         (continuation-form (compute-next-continuation nil accumulated-variables))
842
                         (event-predicate-form (when event-filter
843
                                                 (compute-domain-predicate-form (cons 'spocq.a:|&&| event-filter)
844
                                                                                event-variable)))
845
                         (repository-predicate-ids (repository-time-series-predicate-ids repository))
846
                         (result-indexes (loop for predicate in ts-predicates
847
                                           for position = (or (position predicate repository-predicate-ids)
848
                                                              (error "Invalid collection predicate: ~s: ~s"
849
                                                                     predicate body))
850
                                           collect position))
851
                         (quad-patterns (loop for (tag s p o) in ts-triples
852
                                          when (find p repository-predicate-ids)
853
                                          collect `(vector ,graph-variable ,s ,p ,o)))
854
                         )
855
                    (unless (equal uniqued-result-arguments match-result-arguments)
856
                      (setf continuation-form
857
                            `(if (and ,@(loop for v in match-result-arguments
858
                                          for u in uniqued-result-arguments
859
                                          unless (eq u v)
860
                                          collect `(= ,u ,v)))
861
                                 ,continuation-form
862
                                 t)))
863
                    `(flet ((,lmdb-continuation (result-vector)
864
                                (let ((,event-variable (aref result-vector 0))
865
                                      ,@(when (variable-p graph-variable) `((,graph-variable (aref result-vector 1))))
866
                                      ,@(when (variable-p ts-subject) `((,ts-subject (aref result-vector 2))))
867
                                      ;;;!!! this should ust the repositry predicate list as the arrangement
868
                                      ;;;!!! in order to leave some empty
869
                                      ,@(loop for var in uniqued-result-arguments
870
                                          for index in result-indexes
871
                                          collect `(,var (aref result-vector ,(+ 3 index)))))
872
                                  (declare (ignorable ,event-variable))
873
                                  ,@(when ignored-variables `((declare (ignorable ,@ignored-variables))))
874
                                  (trace-bgp bgp.lmdb-ts-matched ,@uniqued-result-arguments)
875
                                  (unless (task-active-p *query*)
876
                                    (log-debug "bgp premature completion: ~a" *query*)
877
                                    (complete-solutions))
878
                                  ;; when coercing solution graph, restore the match to the initial graphs
879
                                  ,@(when is-first-iteration
880
                                      `((note-graph-reference ,(first uniqued-result-arguments))))
881
                                  ,continuation-form)))
882
                       (declare (dynamic-extent #',lmdb-continuation))
883
                       (trace-bgp bgp.lmdb-ts-to-match ',ts-predicates ',ts-variables)
884
                       ;; (when *match-target-graph* (setf ,graph-variable effective-dataset-graphs))
885
                       (rlmdb:map-repository-events #',lmdb-continuation transaction
886
                                                    (list ,@quad-patterns)
887
                                                    :revision-predicate (compute-revision-predicate transaction)
888
                                                    ,@(when event-predicate-form
889
                                                        `(:domain-predicate ,event-predicate-form))
890
                                                    ;; all triples in the set should follow the same pattern
891
                                                    :term-precedence ',(compute-term-precedence (extend-triple (first ts-triples)
892
                                                                                                               graph-variable)
893
                                                                                                sort-order
894
                                                                                                graph-variable
895
                                                                                                event-variable))))))
896
              (event-pattern-p (form)
897
                (and (triple-form-p form)
898
                     (eq (third form) *predicate.event*)))
899
              (compute-first-continuation (triples accumulated-variables)
900
                (if (find 'collated declarations)
901
                    (compute-collated-continuation triples accumulated-variables)
902
                    (compute-next-continuation triples accumulated-variables)))
903
              )
904
       
905
       (when event-filter
906
         (setf body (remove event-filter body))
907
         (setf event-filter (rest event-filter)))
908
       (when sort-order
909
         (setf body (remove sort-order body))
910
         (setf sort-order (rest sort-order)))
911
 
912
       ;;; if some pattern requires a term which is not found, the bgp has to yield no result.
913
       (setf iteration-patterns (loop for (tag . spo) in iteration-patterns
914
                                      collect (ecase tag
915
                                                (spocq.a:|triple|
916
                                                 (destructuring-bind (s p o &rest args) spo
917
                                                   `(,tag ,(intern-if-constant s)
918
                                                          ,(cond ((extension-operator-p p)
919
                                                                  p)
920
                                                                 ((active-verb-p p)
921
                                                                  p)
922
                                                                 (t
923
                                                                  (intern-if-constant p)))
924
                                                          ,(intern-if-constant o)
925
                                                          ,@args)))
926
                                                (spocq.a:|filter|
927
                                                 (cons tag spo)))))
928
       ;; prefer constants early and paths late
929
       (setf iteration-patterns (sort-bgp-statements repository iteration-patterns initial-bindings))
930
       (setf default-graphs (mapcar #'intern-graph-if-constant default-graphs))
931
       (setf named-graphs (mapcar #'intern-graph-if-constant named-graphs))
932
       (when (and (null default-graphs) (null named-graphs))
933
         ;; compose the "default dataset"
934
         (setf default-graphs (list default-context-term-number)
935
               named-graphs (list named-contexts-term-number)))
936
       (when (some (complement #'plusp) named-graphs)
937
         ;; permit a singleton abstract graph only
938
         (assert (null (rest named-graphs)) ()
939
                 "Invalid wildcard dataset named graphs specification: ~a." named-graphs)
940
         (setf named-graphs (first named-graphs)))
941
       (when (some (complement #'plusp) default-graphs)
942
         (assert (null (rest default-graphs)) ()
943
                 "Invalid wildcard dataset default graph specification: ~a." default-graphs)
944
         (setf default-graphs (first default-graphs)))
945
       (setf equivalents (loop for (var . value-or-var) in equivalents collect (cons var (intern-if-constant value-or-var))))
946
       
947
       ;; allow for just the graph as a variable for literal bgp patterns?
948
       (when (variable-p graph)
949
         (setf projection-dimensions (union-dimensions (list graph) projection-dimensions)))
950
 
951
       (let* (;; the interface operator accepts the repository, and optionally an initial spo solution field, and/or a graph sequence
952
              ;; this wraps an operator which iterates outer over the spo field to bind the starting values for those variables 
953
              ;; for each pass and then iterates secondarily over the graph field to perform the pattern matches.
954
              ;;
955
              ;; the top-level match form starts with all patterns, _no_ bound variables, and the cursor lists
956
              ;; corresponding to the patterns. not even the solution variables are bound initially, as they may themselves
957
              ;; start out as wild cards.
958
              #+(or) ;; the initial call no longer includes constant terms
959
              (base-bindings (if (or graphs (not graph))
960
                               (union-dimensions (list graph-variable) base-dimensions)
961
                               base-dimensions))
962
              ;; this cannot work in this way, as it causes all variables to
963
              ;; be handled as if they are pre-bound, with the consequence, that
964
              ;; they are matched, but never extractedd from the cursor...
965
              ;; (initial-bindings (union-dimensions base-bindings variables))
966
              (match-invocation-form
967
               ;; the effective process depends on a combination of :
968
               ;;
969
               ;; - the query graph term: null, variable, constant, abstract
970
               ;; - the dataset default graphs: null, constant, singleton abstract
971
               ;;   normalized above to reflect the default v/s declare composition
972
               ;; - the dataset named graphs: null, constant, singleton abstract
973
               ;;   normalized above to reflect the default v/s declare composition
974
               ;; - a propagated solution graph: null, variable
975
               ;;
976
               ;; if graph is bound, the bgp is in a graph clause
977
               ;; - if dataset.named-graphs are supplied : they provide the argument for the initial
978
               ;;   match and the result serves for successive, nested calls
979
               ;;   - allow for propagation to be constrained by the dataset
980
               ;;   - allow for - and constrain, a constant graph
981
               ;; - no dataset.named-graphs
982
               ;;   no match is possible
983
               ;; no graph indicates the bgp is outside the scope of a graph clause
984
               ;; - if dataset.default-graphs are supplied : match them the first time _and_
985
               ;;   also for subsequent matches, but substitute the default graph in the result field
986
               ;; - no dataset.default-graphs
987
               ;;   no match is possible
988
               ;;
989
               ;; the dataset definition can be a singleton set of a designator for default, named, or all
990
               ;; graphs, in which case the role when matching is the same os that of a concrete graph
991
               ;;
992
               (if graph
993
                   ;; in the scope of a graph clause
994
                   (cond (named-graphs
995
                          ;; if the dataset definition includes named graphs, permit propgation, constants, or a dynamic binding
996
                          (cond (initial-solution-graph-variable
997
                                 ;; propagate form outside the bgp
998
                                 `(if (eql ,wildcard-term ,initial-solution-graph-variable)
999
                                      (match-patterns ',named-graphs)
1000
                                      (when ,(or (eql named-contexts-term-number named-graphs)
1001
                                                 (eql wildcard-term named-graphs)
1002
                                                 (if (consp named-graphs)
1003
                                                     `(member ,initial-solution-graph-variable ',named-graphs)
1004
                                                     `(eql ,initial-solution-graph-variable ,named-graphs)))
1005
                                          (match-patterns ,initial-solution-graph-variable))))
1006
                                ((variable-p graph)
1007
                                 `(if (not (eql ,graph ,wildcard-term))
1008
                                      (when ,(or (eql named-contexts-term-number named-graphs)
1009
                                                 (eql wildcard-term named-graphs)
1010
                                                 (if (consp named-graphs)
1011
                                                     `(member ,graph ',named-graphs)
1012
                                                     `(eql ,graph ,named-graphs)))
1013
                                        (match-patterns ,graph))
1014
                                      (match-patterns ',named-graphs))
1015
                                 ;; bind aa request argument or match as wild
1016
                                 #+(or)
1017
                                 `(let ((argument (query-binding-value ',graph)))
1018
                                    (if (integerp argument)
1019
                                        ;; execute with a request parameter if the dataset graphs permit
1020
                                        (when ,(or (eql named-contexts-term-number named-graphs)
1021
                                                   (eql wildcard-term named-graphs)
1022
                                                   (if (consp named-graphs)
1023
                                                       `(member argument ',named-graphs)
1024
                                                       `(eql argument ,named-graphs)))
1025
                                          (match-patterns argument))
1026
                                        ;; otherwise, start the match unbound
1027
                                        (match-patterns ',named-graphs))))
1028
                                ((iri-p graph)
1029
                                 (setf graph (intern-graph-if-constant graph))
1030
                                 ;; match concrete literal or abstract (named, all)
1031
                                 (when (or (eql named-contexts-term-number named-graphs)
1032
                                           (eql wildcard-term named-graphs)
1033
                                           (eql graph named-graphs)
1034
                                           (and (consp named-graphs) (member graph named-graphs)))
1035
                                   `(match-patterns ,graph)))
1036
                                (t
1037
                                 (error "Invalid graph term: ~s." graph))))
1038
                         (t
1039
                          ;; if the dataset includes no named graph, then a graph term cannot match
1040
                          ;; see 13.2.1: the dataset includes no named graphs
1041
                          nil))
1042
                   (cond (default-graphs
1043
                             ;; iff merging into the default graph, the match result must be coerced
1044
                          `(match-patterns ',default-graphs ,default-context-term-number))
1045
                         (t
1046
                          nil))))
1047
              (filtered-p (when (assoc 'spocq.a:|filter| iteration-patterns) t))
1048
              (triple-matching-form (compute-first-continuation iteration-patterns initial-bindings)))
1049
         (when filtered-p
1050
           ;;(print :added-stringdb-context)
1051
           (setf triple-matching-form
1052
                 `(rlmdb:with-string-database (bgp-db) ,triple-matching-form)))
1053
         
1054
         ;; (print (list :match-invocation-form match-invocation-form :graph graph :named-graphs named-graphs))
1055
         (when trace
1056
           (format *trace-output* "~&*compute-bgp-lambda.trace*~%body ~s~%graph ~s~%named ~s~%default ~s~%initial-solution-graph-variable ~s~%graph-variable ~s"
1057
                   body graph named-graphs default-graphs initial-solution-graph-variable graph-variable))
1058
         ;; the actual query operator takes one of two forms. iff base dimensions are included, it is
1059
         ;; generated to expect a solution field source, and to iterate the entire operator over the field's solutions.
1060
         ;; without base dimensions, it runs autonomously.
1061
         ;; the combination makes it possible to construct query processing both as reduction through bottom-up combination
1062
         ;; and as solution combination, and even as some combination of the two
1063
         ;; where evaluation materialized all intermediate solutions, it did not matter that the reduction order agree
1064
         ;; with the matching order. the intermediate matching results were cached for delayed use as combination arguments.
1065
         ;; where the solution data flows form one operator tot he next, with the intent to limit the space required for
1066
         ;; intermediate fields by streaming results, the contradictory evaluation order would still require buffered
1067
         ;; materialization. for example an expression of the form
1068
         ;;
1069
         ;;  (join (?a ?b ?c) (join (?b ?c) (?c))) 
1070
         ;;
1071
         ;; would reduce with data flow
1072
         ;;
1073
         ;;  ((?b ?c) . (?c) . (?a ?b ?c))
1074
         ;;
1075
         ;; but match with data flow
1076
         ;;  (?c) -> (?b ?c) -> (?a ?b ?c)
1077
         ;;
1078
         ;; which entail inverted match orders. the question is, how to enable both?
1079
         
1080
         (let* ((all-bgp-variables
1081
                 ;; look for a dynamic binding for all variables in the bgp
1082
                 ;; this allows to specify dynamic variables retrospectively, but it may introduce
1083
                 ;; an issue related to extracting them from the cursor
1084
                  (set-difference variables base-bindings))
1085
                (query-lambda
1086
                `(lambda (bgp-destination ,@(when base-dimensions '(source)))
1087
                   (declare (optimize ,@*field-optimization*))
1088
                   (assert-argument-types bgp-match
1089
                                          (bgp-destination (or channel function))
1090
                                          ,@(when base-dimensions '((source (or channel function)))))
1091
                   (let* ((repository-id (repository-id *repository*))
1092
                          (revision-id (repository-revision-id *task*))
1093
                          (transaction *transaction*)
1094
                          (transaction-record (transaction-record transaction))
1095
                          (*thread-operations* (cons (list 'spocq.a:|bgp| ',body) *thread-operations*))
1096
                          (match-requests 0)
1097
                          (match-results 0)
1098
                          (result-page nil)
1099
                          (result-page-length (channel-page-length bgp-destination))
1100
                          (result-index *field-page-length*)
1101
                          (result-count 0)
1102
                          (last-graph-id 0)
1103
                          (graph-ids-read (make-term-id-cache :single-thread t))
1104
                          (*wildcard-identifier* ,wildcard-term)
1105
                          (*default-context-identifier* ,default-context-term-number)
1106
                          ,@(loop for var in match-counters collect `(,var 0))
1107
                          ;; the original formulation looked for dynamic bindings for all variables
1108
                          ;; which were specified as dynamic in the first request but provided to
1109
                          ;; be accepted from a propagation source
1110
                          #+(or)
1111
                          ,@(loop for var in (intersection (set-difference dynamic-variables base-bindings) variables)
1112
                                  collect `(,var (query-binding-term-number ',var)))
1113
                          ,@(loop for var in all-bgp-variables
1114
                                  collect `(,var (query-binding-term-number ',var)))
1115
                          ,@(loop for (nil . var) in blank-node-map
1116
                              when (variable-p var)
1117
                              collect `(,var ,wildcard-term))
1118
                          ,@(when perform-slice
1119
                              (destructuring-bind (&key (start 0) (offset start) end (count (when end (- end offset))))
1120
                                                  slice
1121
                                `((solution-offset ,offset)
1122
                                  (solution-count ,count))))
1123
                          )
1124
                     (declare (ignorable repository-id revision-id transaction-record)         ; if just paths
1125
                              )
1126
                     ,@(when all-bgp-variables ; a variable may have been folded into a constant
1127
                         `((declare (ignorable ,@all-bgp-variables))))
1128
                     (block :bgp-match
1129
                       (labels ((coerce-to-term-id (term-number)
1130
                                  (typecase term-number
1131
                                    (fixnum term-number)
1132
                                    (t (log-warn "bgp match result not a term number: ~s." term-number)
1133
                                        +null-term-id+)))
1134
                                (collect-solution ,collection-variables
1135
                                  (trace-bgp bgp.match.collect-solution ,@collection-variables)
1136
                                  (case *match-target-graph*
1137
                                    ((nil) )
1138
                                    (-1 )
1139
                                    (t (log-warn "anomalous match target graph: ~s." *match-target-graph*)))
1140
                                  (incf result-count)
1141
                                  ,(if projection-dimensions
1142
                                     `(let ,(loop for (alias . equivalent) in equivalents         ; bind left-over constants
1143
                                                  collect (list alias equivalent))
1144
                                        (next-solution-location)
1145
                                        (locally (declare (type (simple-array fixnum (* ,projection-variable-count)) result-page)
1146
                                                          (type fixnum result-index)
1147
                                                          (ftype (function (t) fixnum) coerce-to-term-id)
1148
                                                          (optimize ,@*field-optimization*))
1149
                                          (setf ,@(loop for var-index from 0
1150
                                                        for variable in projection-dimensions
1151
                                                        nconc `((aref result-page result-index ,var-index)
1152
                                                                (coerce-to-term-id ,variable))))))
1153
                                     '(next-solution-location))
1154
                                  result-count)
1155
                                (next-solution-location ()
1156
                                  ;; return a page (possible newly created) and the next free location in that page
1157
                                  (when (>= (incf result-index) result-page-length)
1158
                                    (unless (check-query-status)
1159
                                      (complete-solutions))
1160
                                    (when result-page (put-result result-page))
1161
                                    (setf result-page (new-field-page bgp-destination result-page-length ,projection-variable-count)
1162
                                          result-index 0))
1163
                                  (values result-page result-index))
1164
                                (complete-solutions ()
1165
                                  (trace-bgp bgp.match.complete-solutions result-count)
1166
                                  (incf *match-requests* match-requests)
1167
                                  (incf *match-responses* match-results)
1168
                                  (when (plusp (hash-table-count graph-ids-read))
1169
                                    (with-locked-cache ((transaction-read-graph-ids transaction))
1170
                                      (loop for id being each hash-key of graph-ids-read
1171
                                            do (setf (transaction-graph-id-read transaction id) t))))
1172
                                  (log-debug "bgp matches+counts: ~s: requests: ~s, matches: (~s ~s), solutions: ~s"
1173
                                             repository-id match-requests match-results (list ,@match-counters) result-count)
1174
                                  (when result-page
1175
                                    (let ((page-result-count (1+ result-index)))
1176
                                      (when (< page-result-count result-page-length)
1177
                                        (setf result-page
1178
                                              (adjust-page result-page (list page-result-count ,projection-variable-count)))))
1179
                                    (put-result result-page))
1180
                                  (complete-field bgp-destination)
1181
                                  (incf-stat *solutions-constructed* result-count)
1182
                                  (return-from :bgp-match result-count))
1183
                                (put-result (page)
1184
                                  (trace-bgp bgp.put bgp-destination ',projection-dimensions page)
1185
                                  (put-field-page bgp-destination page)
1186
                                  (unless (task-active-p *query*)
1187
                                    (complete-field bgp-destination)
1188
                                    (return-from :bgp-match result-count)))
1189
                                (note-graph-reference (graph)
1190
                                  (unless (eql graph last-graph-id)
1191
                                    (setf last-graph-id graph)
1192
                                    (setf (gethash graph graph-ids-read) t))))
1193
                         (trace-bgp bgp.start ',id (task-id *query*)
1194
                                    repository-id revision-id ',projection-dimensions)
1195
                         ;; (print (list :bgp.start ',id (task-id *query*) repository-id revision-id ',projection-dimensions))
1196
                         (incf-stat *algebra-operations*)
1197
                         (trace-bgp bgp.repository *repository* repository-id revision-id :dataset ',dataset-graphs)
1198
                         (if ,(if (find-if #'property-path-p body :key #'third) t 'revision-id)
1199
                             ;; unless there are paths present (actually zero-length paths is the issue)
1200
                             ;; require an id to perform the query. otherwise there can be no result - skip it
1201
                             (progn 
1202
                               ; push this logic to the outer-most match call to pass
1203
                               ; the entire graph set as the context argument
1204
                               ,(let ((iterate-over-graphs `(flet ((match-patterns (effective-dataset-graphs &optional (*match-target-graph* nil)
1205
                                                                                                             ,@(when (eq graph-variable '.graph)
1206
                                                                                                                 '(&aux (.graph *wildcard-identifier*)))
1207
                                                                                                             )
1208
                                                                     ;; effective-dataset-graphs is the initial match set
1209
                                                                     ;; *match-target-graph*, if bound indicates to coerce the graph term to that
1210
                                                                     ;; if the graph is coerced, subsequent matches re-use the original set
1211
                                                                     ;; this manages, eg. the case where a set of graphs is 'merged' into the
1212
                                                                     ;; default graph, but the result must indicate the default graph only
1213
                                                                     (trace-bgp bgp.next-graph effective-dataset-graphs *match-target-graph*)
1214
                                                                     (let ((*match-property-path-context* effective-dataset-graphs))
1215
                                                                       (when (= *wildcard-identifier* ,graph-variable)
1216
                                                                         (setf ,graph-variable effective-dataset-graphs))
1217
                                                                       ,triple-matching-form)))
1218
                                                              ,match-invocation-form)))
1219
                                  (when *compute-bgp-lambda.debug*
1220
                                    (pprint iterate-over-graphs))
1221
                                  (if base-dimensions
1222
                                      `(do-pages (page source)
1223
                                                 ,(let ((macros (loop for variable in base-dimensions
1224
                                                                  for i from 0
1225
                                                                  collect `(,variable (aref page page-index ,i)))))
1226
                                                    `(locally (declare (type (simple-array fixnum (* ,(length base-dimensions))) page)
1227
                                                                       (optimize ,@*field-optimization*))
1228
                                                       (assert (and (typep page '(simple-array fixnum))
1229
                                                                    (= (array-dimension page 1) ,(length base-dimensions)))
1230
                                                               ()
1231
                                                               "Invalid propagated page: ~s" page)
1232
                                                       (trace-data bgp-match.dequeue bgp-destination ',base-dimensions page (term-value-field page))
1233
                                                       (loop for page-index from 0 below (array-dimension page 0)
1234
                                                         do (symbol-macrolet ,macros
1235
                                                                             ,iterate-over-graphs)))))
1236
                                      iterate-over-graphs))
1237
                               (trace-bgp bgp.complete-after-graph-iteration)
1238
                               (complete-solutions))
1239
                             (progn (trace-bgp bgp.suppress  repository-id)
1240
                               (log-debug "suppress query for empty repository: ~a" repository-id)
1241
                               (complete-solutions))))
1242
                       (log-warn "incomplete bgp: ~s." ',id)
1243
                       result-count)))))
1244
           (log-trace "query [~a] bgp [~a] lambda: ~s"
1245
                      (task-id *query*) id query-lambda)
1246
           (values query-lambda
1247
                   projection-dimensions))))))
1248
 
1249
 (defmethod compute-repository-pattern-partitions
1250
            ((repository time-series-repository) patterns)
1251
   "A time-series repository collates results from graph patterns which pertain to
1252
   its declared predecates. Statements which involve those predicates are isolated
1253
   into paritions which share subject."
1254
   (let ((triples (remove-if-not #'triple-form-p patterns))
1255
         (non-triples (remove-if #'triple-form-p patterns)))
1256
     ;; partition based on three interacting indicators
1257
     ;; -- a statement pattern with a |urn:dydra|:|event| predicate
1258
     ;; -- statements with declared event predicates
1259
     ;; -- an event filter
1260
     ;;
1261
     ;; separate those triples which involve a constant time-series predicate and a variable object.
1262
     ;; the ts index keys omit the object values and substitute a event designator term
1263
     ;; (ordinal, uuid, or timestamp) which is understood as the complex event id.
1264
     ;; when matching, the event id may be constant or variable depending on the term
1265
     ;; which appears in the event statement pattern and/or the boounds expressed in any event filter
1266
     ;; the graph and the subject may be constant or variable.
1267
     ;; consistent with rsp, the graph identifes the "simple event object", or sample,
1268
     ;; while the event designator identifies the complex event
1269
     ;; - http://ontologydesignpatterns.org/wiki/Submissions:EventProcessing
1270
     ;; - https://www.w3.org/community/rsp/wiki/RDF_Stream_Models
1271
     ;; different patterns yield different match processes and different binding streams.
1272
     ;; in any case, the basic partition is those which are present in the time-series indices.
1273
     ;;
1274
     ;; the partition process expects at most one set of patterns which pertains to a given subject
1275
     ;; and produces one of
1276
     ;; - no partition, if no event indicator is present
1277
     ;; - a two-statement partition with the event statement pattern and a wild pattern
1278
     ;; - a multi-statement partition with the event pattern and those which include egent predicates.
1279
     (loop with collections = (make-hash-table)
1280
       for triple in triples
1281
       for (tag . spo) = triple
1282
       if (or (and (repository-time-series-predicate-p repository (second spo))
1283
                   (variable-p (third spo)))
1284
              (eq (second spo) *predicate.event*))
1285
       do (push triple (gethash (first spo) collections))
1286
       else collect triple into other-triples
1287
       finally (return (append (loop for partition-triples being each hash-value of collections
1288
                                 collect (cons '(spocq.a::|declare| collated) (append non-triples partition-triples)))
1289
                               (call-next-method repository other-triples))))))
1290
 
1291
 ;;; domain index support
1292
 
1293
 (defun statement-mapping-properties (statement)
1294
   (loop for (keyword value) on (statement-properties statement)
1295
     append (statement-mapping-property keyword value)))
1296
 
1297
 (defgeneric statement-mapping-property (keyword value)
1298
   (:method ((keyword (eql :filter)) (value t))
1299
     `(:filter ',value))
1300
   (:method ((keyword t) (value t))
1301
     nil))
1302
 
1303
 (defgeneric annotate-bgp-statements (repository body)
1304
   (:documentation "Annotate the bgp statement to reflect the repository indices.
1305
    Where the repository implements domain indices pertinent to present filter constraints,
1306
    extract them merge them into statements as annotations to permit the respective map
1307
    operator to tailor its match/scan process to them.")
1308
   (:method ((repository t) (body list))
1309
     "The base method returns the body unchanged."
1310
     body)
1311
 
1312
   (:method ((repository lmdb-temporal-repository) (body list))
1313
     "If the target is a temporal repository, migrate any filter in which one
1314
     variable is constraind by constants to that statement pattern which binds
1315
     the variable.
1316
     Other variables may be present, but not in relation to constants.
1317
     If the filter is an expression list or a conjunction, attempt to deconstruct it."
1318
     ;; that property is present in a statement, migrate the expression
1319
     (setf body (copy-tree body))
1320
     (let ((temporal-statements ; index temporal statement patterns by variable object
1321
            (loop for form in body
1322
              when (and (triple-form-p form)
1323
                        (variable-p (statement-object form))
1324
                        (repository-temporal-predicate-p repository (statement-predicate form)))
1325
              collect (cons (statement-object form) form)))
1326
           (unrelated-filters ()))
1327
       (labels ((migrate-form (variable form)
1328
                  ;; push any components possible onto the respective triple
1329
                  ;; return the remaining filter or nil if no clause remains
1330
                  (let ((statement (rest (assoc variable temporal-statements))))
1331
                    (when statement
1332
                      (push form (getf (statement-properties statement) :filter)))))
1333
                (associate-filter (filter &aux (expression (second filter)))
1334
                  (case (first expression)
1335
                    (spocq.a:|exprlist|
1336
                     (loop for form in (rest expression) do (associate-filter form)))
1337
                    ((spocq.a:! spocq.a:!=)
1338
                     (push expression unrelated-filters))
1339
                    ((spocq.a:< spocq.a:> spocq.a:=)
1340
                     (destructuring-bind (a1 a2) (rest expression)
1341
                       (cond ((migrate-form a1 expression))
1342
                             ((migrate-form a2 expression))
1343
                             (t
1344
                              (push expression unrelated-filters)))))
1345
                    (spocq.a:&&
1346
                     (loop for form in (rest expression) do (associate-filter form)))
1347
                    (spocq.a:|\|\||
1348
                     (push expression unrelated-filters)))))
1349
       (setf body 
1350
             (append (loop for statement in body
1351
                       if (triple-form-p statement)
1352
                       collect statement
1353
                       else if (filter-form-p statement)
1354
                       do (associate-filter statement)
1355
                       else collect statement)
1356
                     (loop for form in unrelated-filters
1357
                       collect `(spocq.a:|filter| ,form))))
1358
       (call-next-method repository body)))))
1359
 
1360
 
1361
 (:documentation "domain predicates"
1362
   "Indices which include a domain value in the index record are intended
1363
  to support interval retrieval by scanning within bounds.
1364
  This require predicate combine tests for term-id->term bindings and
1365
  variables bound directly to domain values.
1366
  In order to permit variant values, they also use local variables which
1367
  convert between temporal object and timeline locations as well as
1368
  various uuid forms.")
1369
 
1370
 (defun compute-domain-predicate-form (expression domain-variable)
1371
   "given an expression and the respective domain variable, return a form
1372
  which constructs a predicate function object which captures the min/max bounds
1373
  for the variable as well as a test function which accepts the variable as
1374
  its single argument."
1375
   (let* ((min nil)
1376
          (op nil)
1377
          (variables (expression-variables expression))
1378
          (other-variables (remove domain-variable variables))
1379
          (macros ()))
1380
     (assert (member domain-variable variables) ()
1381
             "Domain expression must involve variable: ~a: ~a"
1382
             domain-variable expression)
1383
     (labels ((extract-bounds (form)
1384
                (when (consp form)
1385
                  (case (first form)
1386
                    (spocq.a:|exprlist|
1387
                      (loop for form in (rest form) do (extract-bounds form)))
1388
                    (spocq.a:! )
1389
                    (spocq.a:!=
1390
                     (unless (assoc 'spocq.a:!= macros)
1391
                       (push '(spocq.a:!= (v1 v2) (list 'not (list 'domain-= v1 v2))) macros)))
1392
                    (spocq.a:<
1393
                     (unless (assoc 'spocq.a:< macros)
1394
                       (push '(spocq.a:< (v1 v2) (list 'domain-< v1 v2)) macros)))
1395
                    (spocq.a:<=
1396
                     (unless (assoc 'spocq.a:<= macros)
1397
                       (push '(spocq.a:<= (v1 v2) (list 'domain-<= v1 v2)) macros)))
1398
                    (spocq.a:>
1399
                      (let ((constraint (relation-constraint form)))
1400
                        (when constraint (push constraint min) (push 'spocq.a:> op)))
1401
                      (unless (assoc 'spocq.a:> macros)
1402
                       (push '(spocq.a:> (v1 v2) (list 'domain-> v1 v2)) macros)))
1403
                    (spocq.a:>=
1404
                      (let ((constraint (relation-constraint form)))
1405
                        (when constraint (push constraint min) (push 'spocq.a:>= op)))
1406
                      (unless (assoc 'spocq.a:>= macros)
1407
                       (push '(spocq.a:>= (v1 v2) (list 'domain->= v1 v2)) macros)))
1408
                    (spocq.a:=
1409
                     (let ((constraint (relation-constraint form)))
1410
                        (when constraint (push constraint min) (push 'spocq.a:= op)))
1411
                     (unless (assoc 'spocq.a:= macros)
1412
                       (push '(spocq.a:= (v1 v2) (list 'domain-= v1 v2)) macros)))
1413
                    ((spocq.a:&& spocq.a:|\|\||)
1414
                     (loop for form in (rest form) do (extract-bounds form)))))
1415
                form)
1416
              (relation-constraint (form)
1417
                (cond ((eq (second form) domain-variable) (third form))
1418
                      ((eq (third form) domain-variable) (second form)))))
1419
       (setf expression (extract-bounds expression))
1420
       `(let ,(loop for variable in other-variables
1421
                collect `(,variable (rlmdb:term-number-value ,variable)))
1422
          (multiple-value-bind (min op)
1423
                               (domain-lower-bound (list ,@min) ',op)
1424
            (make-domain-predicate
1425
             :min min :min-op op
1426
             :test (lambda (,domain-variable)
1427
                     (macrolet ,macros
1428
                       (handler-case (ebv ,expression) (error () nil))))))))))
1429
 
1430
 (defgeneric domain-< (v1 v2)
1431
   (:method ((v1 integer) (v2 spocq:temporal-location))
1432
     (< v1 (temporal-timeline-location v2)))
1433
   (:method ((v1 spocq:temporal-location) (v2 integer))
1434
     (< (temporal-timeline-location v1) v2))
1435
   (:method ((v1 t) (v2 t))
1436
     (spocq.e:< v1 v2)))
1437
 (defgeneric domain-<= (v1 v2)
1438
   (:method ((v1 integer) (v2 spocq:temporal-location))
1439
     (<= v1 (temporal-timeline-location v2)))
1440
   (:method ((v1 spocq:temporal-location) (v2 integer))
1441
     (<= (temporal-timeline-location v1) v2))
1442
   (:method ((v1 t) (v2 t))
1443
     (spocq.e:<= v1 v2)))
1444
 
1445
 (defun domain-> (v1 v2)
1446
   (domain-< v2 v1))
1447
 (defun domain->= (v1 v2)
1448
   (domain-<= v2 v1))
1449
 
1450
 (defgeneric domain-= (v1 v2)
1451
   (:method ((v1 integer) (v2 spocq:temporal-location))
1452
     (= v1 (temporal-timeline-location v2)))
1453
   (:method ((v1 spocq:temporal-location) (v2 integer))
1454
     (= (temporal-timeline-location v1) v2))
1455
   (:method ((v1 t) (v2 t))
1456
     (spocq.e:= v1 v2)))
1457
 
1458
 (defun domain-lower-bound (values operators)
1459
   (loop with value = (pop values)
1460
     with operator = (pop operators)
1461
     for next-value in values
1462
     for next-operator in operators
1463
     when (or (domain-< next-value value)
1464
              (and (eq next-operator 'spocq.a:>)
1465
                   (domain-= next-value value)))
1466
     do (setf operator next-operator
1467
              value next-value)
1468
     finally (return (values value operator))))
1469
 
1470
 (defclass domain-predicate (c2mop:funcallable-standard-object)
1471
   ((min :initarg :min :initform nil
1472
         :reader domain-predicate-min)
1473
    (min-op :initarg :min-op :initform nil
1474
            :reader domain-predicate-min-op)
1475
    (test :initarg :test :initform nil
1476
         :reader domain-predicate-test))
1477
   (:metaclass c2mop:funcallable-standard-class))
1478
 
1479
 (defun make-domain-predicate (&rest args)
1480
   (declare (dynamic-extent args))
1481
   (apply #'make-instance 'domain-predicate args))
1482
 
1483
 (defmethod initialize-instance ((instance domain-predicate) &rest args
1484
                                 &key test min max)
1485
   (declare (ignore max))
1486
   (assert (typep test '(or function null)) ()
1487
           "domain-predicate: invalid test function: ~s" test)
1488
   (apply #'call-next-method instance
1489
          :min (etypecase min
1490
                 (null nil)
1491
                 (integer min)
1492
                 (spocq:temporal-location (temporal-timeline-location min)))
1493
          args)
1494
   (c2mop:set-funcallable-instance-function instance (or test (constantly t))))
1495
 
1496
 (defun domain-predicate-p (data)
1497
   (typep data 'domain-predicate))
1498
 
1499
 #+(or)
1500
 (
1501
  (compute-domain-predicate-form '(spocq.a:|\|\|| (spocq.a:> ?::|y| 2) (spocq.a:< ?::|y| 3))
1502
                                   '?::|y|)
1503
  (let ((tp (make-instance 'domain-predicate :test (lambda (v) (= v 3))))) (funcall tp 2))
1504
 (let ((tp (make-instance 'domain-predicate :test nil))) (funcall tp 2))
1505
  )
1506
 
1507
 #+(or) ;; obsolete
1508
 (defun compute-temporal-predicate-lambda (expression)
1509
   "walk the expression to extract all variables other then ?::timeSeries.
1510
  return a predicate which accepts a time series identifier
1511
  - rebind variables to resolved values
1512
  - bind constants to resolved values
1513
  - evaluate the expression"
1514
 
1515
   (let ((constants ())
1516
         (variables ())
1517
         (first 0))
1518
     (labels ((replace-expressions (form)
1519
                (typecase form
1520
                  (cons
1521
                   (destructuring-bind (op &optional arg1 arg2) form
1522
                     (case op
1523
                       ((> >=) (when (eq arg1 '?::|timeSeries|) (setf first arg2)))
1524
                       ((<= <) (when (eq arg2 '?::|timeSeries|) (setf first arg1)))))
1525
                   form)
1526
                  (symbol
1527
                   (cond ((eq form '?::|timeSeries|)
1528
                          'time-series-identifier)
1529
                         ((eq (symbol-package form) *variable-package*)
1530
                          (push `(,form (resolve-time-series-identifier ,form))
1531
                                variables)
1532
                          form)
1533
                         (t
1534
                          form)))
1535
                  (t
1536
                   (let ((variable (gensym "tsid-value-")))
1537
                     (push `(,variable (resolve-time-series-identifier ,form))
1538
                           constants)
1539
                     variable)))))
1540
       (setf expression (map-tree #'replace-expressions expression))
1541
       (values `(let (,@constants ,@variables)
1542
                  (lambda (time-series-identifier)
1543
                    ,expression))
1544
               first))))
1545
 
1546
 ;;; (compute-temporal-predicate-lambda '(and (< ?::|timeSeries| #@"2020-01-01T00:00:00") (>= ?::|timeSeries| ?::tsStart)))
1547
 
1548
 (defclass revision-predicate (c2mop:funcallable-standard-object)
1549
   ((min :initarg :min :initform nil
1550
         :reader revision-predicate-min)
1551
    (min-op :initarg :min-op :initform nil
1552
            :reader revision-predicate-min-op)
1553
    (test :initarg :test :initform nil
1554
         :reader revision-predicate-test))
1555
   (:metaclass c2mop:funcallable-standard-class))
1556
 
1557
 (defmethod initialize-instance ((instance revision-predicate) &rest args
1558
                                 &key test min max)
1559
   (declare (ignore max))
1560
   (assert (typep test '(or function null)) ()
1561
           "revision-predicate: invalid test function: ~s" test)
1562
   (apply #'call-next-method instance
1563
          :min (etypecase min
1564
                 (null nil)
1565
                 (integer min))
1566
          args)
1567
   (c2mop:set-funcallable-instance-function instance (or test (constantly t))))
1568
 
1569
 (defgeneric compute-revision-predicate (transaction)
1570
   
1571
   (:method ((transaction lmdb-transaction))
1572
     (let ((first (transaction-min-revision-ordinal transaction))
1573
           (last (transaction-max-revision-ordinal transaction))
1574
           (highest (transaction-highest-revision-ordinal transaction)))
1575
       (compute-revision-predicate (list :first first :last last :highest highest))))
1576
   (:method ((bounds list))
1577
     (destructuring-bind (&key first (last first) highest
1578
                               (element-size (load-time-value (cffi:foreign-type-size 'revision-ordinal))))
1579
                         bounds
1580
       (flet ((test-visibility (%vector byte-count)
1581
              ;; (print (list byte-count element-size highest))
1582
              (or (null byte-count) (= byte-count 0)
1583
                  (let ((visibility-count (/ byte-count element-size)))
1584
                    (or (and (oddp visibility-count) highest (>= first highest)) ; head, just the last revision matters
1585
                        (rlmdb.i::%test-visibility-range first last %vector visibility-count))))))
1586
         (make-instance 'revision-predicate :min first :test #'test-visibility)))))
1587
 
1588
 (defun revision-predicate-p (data)
1589
   (typep data 'revision-predicate))
1590
 
1591
 
1592
 ;;; path support
1593
 
1594
 (defmethod repository-query-by-verb ((continuation t) (transaction lmdb-transaction) context subject (predicate integer) object)
1595
   "if the predicate is an integer, it as a term number - perform the actual match and upon success
1596
   invoke the continuation with the concrete terms under cycle constraints"
1597
   (cond ((null (transaction-parent-p transaction))
1598
          (trace-paths "pp.rqbv.suppress    : query-by-verb for empty repository: ~a" transaction)
1599
          nil)
1600
         (t
1601
          (trace-paths "pp.rqbv.lmdb        : ~a(~a) ~a(~a) ~a(~a) : ~a,~a~%"
1602
                       (format-term-number-object subject) subject
1603
                       (format-term-number-object predicate) predicate
1604
                       (format-term-number-object object) object
1605
                       *match-target-graph*
1606
                       *match-property-path-context*)
1607
          (when *match-target-graph* (setf context *match-target-graph*))
1608
          (when *match-property-path-context* (setf context *match-property-path-context*))
1609
          (flet ((continue-with-quad (%quad)
1610
                   (call-path-continuation continuation
1611
                                           (or *match-target-graph* (%quad-context %quad))
1612
                                           (%quad-subject %quad)
1613
                                           (%quad-predicate %quad)
1614
                                           (%quad-object %quad))
1615
                   t))
1616
            (declare (dynamic-extent #'continue-with-quad))
1617
            (rlmdb:map-repository-statements #'continue-with-quad transaction
1618
                                             (vector context
1619
                                                     subject
1620
                                                     predicate
1621
                                                     object))))))
1622
 
1623
 ;;; matrix support
1624
 
1625
 (defmethod repository-match-matrix ((transaction lmdb-transaction) matrix context subject predicate object
1626
                                     &key (start 0) end)
1627
   "generate a new solution matrix for the statements which match the argument pattern.
1628
  If the context is not yet interned, intern all terms and recurse. should some term not be present, then return
1629
  a null matrix. Associate a (possible sparse) dimension list with the result to indicate which columns
1630
  were variables."
1631
   
1632
   (assert (typep start '(integer 0)) () "repository-match-matrix: invalid start: ~s" start)
1633
   (assert (or (null end) (and (typep end '(integer 0)) (>= end start))) () "repository-match-matrix: invalid end: ~s" end)
1634
   (let* ((contexts (if (consp context) context (list context)))
1635
          (results ())
1636
          (result-count 0)
1637
          (limit (when end (- end start))))
1638
     (flet ((match-context (context)
1639
                (flet ((continue-with-quad (%quad)
1640
                         (cond ((and limit (zerop limit))
1641
                                nil)
1642
                               ((zerop start)
1643
                                (push %quad results)
1644
                                (incf result-count))
1645
                               (t
1646
                                (decf start)))))
1647
                  (declare (dynamic-extent #'continue-with-quad))
1648
                  (rlmdb:map-repository-statements #'continue-with-quad transaction
1649
                                                   (vector context subject predicate object)))))
1650
       (loop for context in contexts
1651
         until (and limit (zerop limit))
1652
         do (match-context context))
1653
       (let ((%matrix (rdfcache:make-matrix result-count 4)))
1654
         (declare (type cffi-sys:foreign-pointer %matrix))
1655
         (loop for %quad in results
1656
           for offset from (* (1- result-count) 4) downto 0
1657
           do (rlmdb.i::copy-quad-record %quad %matrix offset))
1658
         (values %matrix result-count)))))
1659
 
1660
 (defmethod repository-matrix-field ((transaction lmdb-transaction) context subject predicate object
1661
                                      &key (start 0) end dimensions (wild-blank-nodes-p t))
1662
   (declare (ignore context subject predicate object start end dimensions wild-blank-nodes-p))
1663
   ;; nyi
1664
   (call-next-method))
1665
 
1666
 (defmethod map-repository-statements (function (transaction lmdb-transaction) subject predicate object context &key
1667
                                                (wildcard-term (repository-wildcard-term transaction)) (offset 0) count)
1668
   (flet ((continue-with-quad (%quad)
1669
            (funcall function
1670
                     (%quad-context %quad)
1671
                     (%quad-subject %quad)
1672
                     (%quad-predicate %quad)
1673
                     (%quad-object %quad)))
1674
          (continue-with-sliced-quad (%quad)
1675
            (cond ((<= (decf offset) 0)
1676
                   ;; continue until any limit is reached
1677
                   (when (or (null count) (>= (decf count) 0))
1678
                     (funcall function
1679
                              (%quad-context %quad)
1680
                              (%quad-subject %quad)
1681
                              (%quad-predicate %quad)
1682
                              (%quad-object %quad))))
1683
                  (t t))))
1684
     (declare (dynamic-extent #'continue-with-quad #'continue-with-sliced-quad))
1685
     (rlmdb:map-repository-statements #'continue-with-quad transaction
1686
                                      (vector (or context (repository-default-context-term-number (transaction-repository transaction)))
1687
                                              (or subject wildcard-term)
1688
                                              (or predicate wildcard-term)
1689
                                              (or object wildcard-term)))))
1690
 
1691
 (defmethod map-repository-statements ((operator t) (index rlmdb:index-database) subject predicate object context &rest args)
1692
   (declare (dynamic-extent args))
1693
   (flet ((continue-with-quad (%quad)
1694
            (funcall operator
1695
                     (%quad-context %quad)
1696
                     (%quad-subject %quad)
1697
                     (%quad-predicate %quad)
1698
                     (%quad-object %quad))))
1699
     (declare (dynamic-extent #'continue-with-quad))
1700
     ;; no nil==wildcard support . this is intended to be used from describe, where everything is known
1701
     (apply #'rlmdb::map-index-statements #'continue-with-quad index
1702
            (vector context subject predicate object)
1703
            args)))
1704
 
1705
 (defmethod transaction-call-with-matched-terms (op (transaction lmdb-transaction) subject predicate object &key
1706
                                                    (wildcard-term (repository-wildcard-term *transaction*))
1707
                                                    (context wildcard-term)
1708
                                                    (target-graph nil) (ordered-p nil))
1709
   (declare (ignore ordered-p))
1710
   (flet ((continue-with-quad (%quad)
1711
            (funcall op
1712
                     (or target-graph (%quad-context %quad))
1713
                     (%quad-subject %quad)
1714
                     (%quad-predicate %quad)
1715
                     (%quad-object %quad))))
1716
     (declare (dynamic-extent #'continue-with-quad))
1717
     (rlmdb:map-repository-statements #'continue-with-quad *transaction*
1718
                                      (vector (or context (repository-default-context-term-number (transaction-repository *transaction*)))
1719
                                              (or subject wildcard-term)
1720
                                              (or predicate wildcard-term)
1721
                                              (or object wildcard-term)))))
1722
 
1723