Coverage report: /development/source/library/org/datagraph/spocq-shard/src/store/rlmdb/bgp-star-lambda.lisp
| Kind | Covered | All | % |
| expression | 494 | 1030 | 48.0 |
| branch | 47 | 136 | 34.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; -*-
3
;;; compile a bgp into a function when i matches the statement pattern combination
8
;;; with possible addition of paths
13
;;; to serve as the anchor for a bounded (kleene) path,
14
;;; as well as values bindings and/or filters
16
;;; the intended order is
17
;;; - bindings : as initial bindings
18
;;; - collated constant object: as nested loop
19
;;; - collated variable object: as a single retrieval
21
;;; + filters interposed as soon as all variables are bound
23
(in-package :org.datagraph.spocq.implementation)
25
(defun collated-statement-pattern-p (statement)
26
"indicate a variable subject and iri predicate.
27
object can be constant or not - as a first-order distinction"
28
(and (triple-form-p statement)
29
(destructuring-bind (subject predicate object . attributes) (rest statement)
30
(declare (ignore attributes))
31
(declare (ignore object))
32
(and (or (variable-p subject) (spocq:blank-node-p subject))
35
(defun constant-statement-pattern-p (statement)
36
(and (triple-form-p statement)
37
(destructuring-bind (subject predicate object . attributes) (rest statement)
38
(declare (ignore attributes))
39
(and (spocq.e:constantp subject)
40
(spocq.e:constantp predicate)
41
(spocq.e:constantp object)))))
43
(defparameter *star-bgp-mode* :enabled)
44
;;; (setq *star-bgp-mode* nil)
45
(defun star-bgp-p (statements)
46
"examine the statement and determine whether
47
- the all share the same subject
48
- more than one is present with a constant predicate and variable object"
50
(when (symbolp (first statements)) (pop statements))
53
(some #'(lambda (statement)
54
(typep statement '(cons (eql declare) (cons (cons (eql spocq.e::processing-mode) (cons (eql :collated)))))))
59
(defun compute-bgp-binding-continuation (form accumulated-variables graph-variable compute-next-continuation)
60
;; compute the bgp continuation for a binding form
61
(declare (ignore graph-variable))
62
(destructuring-bind (op solutions variables) form
64
(let* ((bindings-op (gensym "bgp-values-"))
65
(extended-variables (union-dimensions accumulated-variables variables))
66
(next-continuation (funcall compute-next-continuation extended-variables)))
68
(trace-bgp-operator spocq.a:|bindings| ',variables)
69
(flet ((,bindings-op ,variables
70
(declare (ignorable ,@variables))
72
(declare (dynamic-extent #',bindings-op))
73
(loop for solution in ',solutions
74
do (apply #',bindings-op solution)))))))
76
(defun compute-bgp-filter-continuation (form accumulated-variables graph-variable compute-next-continuation)
77
;; compute the bgp continuation for a filter form
78
(declare (ignore graph-variable))
79
(destructuring-bind (op test-expression) form
81
(let ((test-variables (expression-variables test-expression))
82
(_test-expression `(handler-case (ebv ,test-expression) (error () nil)))
83
;; a filter adds no bindings to those accumulated
84
(next-continuation (funcall compute-next-continuation accumulated-variables)))
86
(trace-bgp-operator spocq.a:|filter| ',test-expression)
87
,(if test-variables ;; make shadow variables to handle number->value mapping
88
(let ((test-aliases (loop for variable in test-variables collect (make-symbol (symbol-name variable)))))
89
`(if ((lambda ,test-aliases
90
(symbol-macrolet ,(loop for variable in test-variables
91
for alias in test-aliases
92
collect `(,variable (repository-term-number-object transaction ,alias)))
93
(trace-bgp bgp.filter ',test-expression ',test-variables (list ,@test-aliases) (list ,@test-variables))
97
;; must provide a true result if the filter fails for the scan to continue
99
`(if ,_test-expression ,next-continuation t))))))
101
(defun compute-bgp-collated-continuation (collation-statements accumulated-variables graph-variable compute-next-continuation)
102
;; compute the bgp gontinuation for a set of collated statements
103
;; the general intent is to collate "entities" resources within an spg- index.
104
;; at this level, the patterns for (s.p.g.*)+filter combinations are collated.
105
;; predicate sets with wild subject are the intended case, but the collation
106
;; ordering entails scanning across the subject index
108
;; this is simpler than the general event case.
109
;; -- create a continuation with variables to bind the
110
;; subject identifier (w/ possible sip)
111
;; the graph(se) identifier
112
;; the collected object variables
114
;; arrange for the continuation to accept a vector of term numbers in the same
115
;; order as given, but with possible unbound variables
116
;; this applies when the store does not declare the predicates t be scanned
118
;; invoke the repository map operator, capture the results and continue with them
119
;; as for other patterns.
121
(multiple-value-bind (c-subject c-predicates c-variables)
122
(loop for (tag s p o) in collation-statements
124
collect p into predicates
125
collect o into variables
126
finally (return (values subject predicates variables)))
127
(when *compute-bgp-lambda.debug*
128
(print (list :collation-continuation c-subject c-predicates c-variables)))
129
(assert (every #'symbolp c-variables) ()
130
"compute-bgp-collated-continuation: non-variable collation object: ~s" collation-statements)
131
(let* ((collation-continuation (gensym "bgp-collation"))
132
(match-result-arguments c-variables)
133
(uniqued-result-arguments (loop for vars on match-result-arguments
134
for v in match-result-arguments
135
collect (if (member v (rest vars))
138
;; ignore, as any earlier binding will have been passed in
139
(ignored-variables (remove-if #'symbol-package uniqued-result-arguments))
140
(extended-variables (append accumulated-variables c-variables))
141
(next-continuation (funcall compute-next-continuation extended-variables))
142
(collation-arguments (list* graph-variable c-subject c-predicates))
144
(unless (equal uniqued-result-arguments match-result-arguments)
145
(setf next-continuation
146
`(if (and ,@(loop for v in match-result-arguments
147
for u in uniqued-result-arguments
152
`(flet ((,collation-continuation (result-vector)
153
(let (,@(when (variable-p graph-variable) `((,graph-variable (aref result-vector 0))))
154
(,c-subject (aref result-vector 1))
155
,@(loop for var in uniqued-result-arguments
156
for index from 0 below (length c-predicates)
157
collect `(,var (aref result-vector ,(+ 2 index)))))
158
,@(when ignored-variables `((declare (ignorable ,@ignored-variables))))
159
(trace-bgp bgp.lmdb-ts-matched ,@uniqued-result-arguments)
160
;; (print result-vector)
161
(note-graph-reference (aref result-vector 0))
162
(incf match-responses)
163
;; when coercing solution graph, restore the match to the initial graphs
164
,next-continuation)))
165
(declare (dynamic-extent #',collation-continuation))
166
(trace-bgp-operator rlmdb::collate-repository-statements ',c-subject ',c-predicates ',c-variables)
167
;; (when *match-target-graph* (setf ,graph-variable effective-dataset-graphs))
168
(incf match-requests)
169
(rlmdb::collate-repository-statements #',collation-continuation transaction
170
(vector ,@collation-arguments)
171
:revision-predicate (compute-revision-predicate (list :first transaction-min-ordinal
172
:last transaction-max-ordinal)))))))
174
(defun compute-bgp-path-continuation (form accumulated-variables graph-variable compute-next-continuation)
175
;; interpret a property path through an out-of-line call to the path matcher
176
(let* ((path-continuation (gensym "bgp-path"))
177
(dimensions (cons graph-variable (statement-dimensions form)))
178
(match-result-arguments (loop ;; for term in (cons graph-variable (statement-terms form))
179
for term in dimensions
180
collect (cond ((or (not (variable-p term)) (member term accumulated-variables))
184
(uniqued-result-arguments (loop for vars on match-result-arguments
185
for v in match-result-arguments
186
collect (if (member v (rest vars))
189
(match-pattern (cons graph-variable
190
;; replace a variable which was not yet returned from a match
191
;; (loop for term in (statement-terms form) collect (if (wild-variable-p term) wildcard-term term))
192
(statement-terms form)
194
(concrete-match-pattern (let ((graph-term (first match-pattern)))
195
(cons graph-term (loop for term in (rest match-pattern)
196
collect (if (eq term graph-term)
197
`(max 0 ,term) ; cannot pass non-concrete term
199
(ignored-variables (remove-if #'symbol-package uniqued-result-arguments))
200
(extended-variables (append accumulated-variables (remove-if-not #'symbol-package match-result-arguments)))
201
(next-continuation (funcall compute-next-continuation extended-variables)))
202
(unless (equal uniqued-result-arguments match-result-arguments)
203
(setf next-continuation
204
`(if (and ,@(loop for v in match-result-arguments
205
for u in uniqued-result-arguments
210
`(flet ((,path-continuation (,@uniqued-result-arguments)
211
,@(when ignored-variables `((declare (ignorable ,@ignored-variables))))
212
(trace-bgp bgp.pp-matched ,@uniqued-result-arguments)
213
(incf match-responses)
215
(declare (dynamic-extent #',path-continuation))
216
(trace-bgp-operator match-property-path ',form)
217
(when *match-target-graph* (setf ,graph-variable effective-dataset-graphs))
218
(incf match-requests)
219
(match-property-path transaction ,@concrete-match-pattern #',path-continuation))))
221
(defun compute-bgp-active-verb-continuation (form accumulated-variables graph-variable compute-next-continuation)
222
;; interpret a property function through an out-of-line call to invoke-active-verb
223
(let* ((pattern-predicate (statement-predicate form))
224
(match-result-arguments (active-verb-results pattern-predicate))
225
;; no identical result variables (uniqued-result-arguments (unique-result-arguments match-result-arguments))
226
(match-parameters (active-verb-parameters pattern-predicate))
227
(extended-variables (append accumulated-variables (remove-if-not #'symbol-package match-result-arguments)))
228
(verb-continuation (gensym "bgp-active-verb"))
229
(ignored-variables (remove-if #'symbol-package match-result-arguments))
230
(next-continuation (funcall compute-next-continuation extended-variables)))
231
`(flet ((,verb-continuation (,@match-result-arguments &rest extra-results)
232
(declare (ignore extra-results))
233
,@(when ignored-variables `((declare (ignorable ,@ignored-variables))))
234
(trace-bgp bgp.active-verb-matched ,@match-result-arguments)
236
(declare (dynamic-extent #',verb-continuation))
237
(trace-bgp-operator invoke-active-verb ',form)
238
(when *match-target-graph* (setf ,graph-variable effective-dataset-graphs))
239
(invoke-active-verb ,pattern-predicate
242
,@match-parameters))))
244
(defun compute-bgp-extension-continuation (form accumulated-variables graph-variable compute-next-continuation)
245
;; interpret a property function through an out-of-line call to funcall-extension
246
(let* ((verb-continuation (gensym "bgp-extension"))
247
(dimensions (cons graph-variable (statement-dimensions form)))
248
(match-result-arguments (loop ;; for term in (cons graph-variable (statement-terms form))
249
for term in dimensions
250
collect (cond ((or (not (variable-p term)) (member term accumulated-variables))
254
(uniqued-result-arguments (loop for vars on match-result-arguments
255
for v in match-result-arguments
256
collect (if (member v (rest vars))
259
(match-pattern (cons graph-variable
260
;; replace a variable which was not yet returned from a match
261
;; (loop for term in (statement-terms form) collect (if (wild-variable-p term) wildcard-term term))
262
(statement-terms form)
264
(concrete-match-pattern (let ((graph-term (first match-pattern)))
265
(cons graph-term (loop for term in (rest match-pattern)
266
collect (if (eq term graph-term)
267
`(max 0 ,term) ; cannot pass non-concrete term
269
(ignored-variables (remove-if #'symbol-package uniqued-result-arguments))
270
(extended-variables (append accumulated-variables (remove-if-not #'symbol-package match-result-arguments)))
271
(next-continuation (funcall compute-next-continuation extended-variables)))
273
;; constrain aliased variables
274
(unless (equal uniqued-result-arguments match-result-arguments)
275
(setf next-continuation
276
`(if (and ,@(loop for v in match-result-arguments
277
for u in uniqued-result-arguments
282
`(flet ((,verb-continuation (,@uniqued-result-arguments)
283
,@(when ignored-variables `((declare (ignorable ,@ignored-variables))))
284
(trace-bgp bgp.extension-matched ,@uniqued-result-arguments)
286
(declare (dynamic-extent #',verb-continuation))
287
(trace-bgp-operator funcall-extension ',form)
288
(when *match-target-graph* (setf ,graph-variable effective-dataset-graphs))
289
(funcall-extension transaction ,@concrete-match-pattern #',verb-continuation))))
291
(defun compute-bgp-index-continuation (form accumulated-variables graph-variable compute-next-continuation)
292
;; this is replaces what was an invocation of dydra-ndk, as that was never implemented
293
(let* ((options (member-if #'keywordp form))
294
(test (getf options :test)) ; !!! this must have been interned already
295
(verb-continuation (gensym "bgp-extension"))
296
(dimensions (cons graph-variable (statement-dimensions form)))
297
(match-result-arguments (loop ;; for term in (cons graph-variable (statement-terms form))
298
for term in dimensions
299
collect (cond ((or (not (variable-p term)) (member term accumulated-variables))
303
(uniqued-result-arguments (loop for vars on match-result-arguments
304
for v in match-result-arguments
305
collect (if (member v (rest vars))
308
(match-pattern (cons graph-variable
309
;; replace a variable which was not yet returned from a match
310
;; (loop for term in (statement-terms form) collect (if (wild-variable-p term) wildcard-term term))
311
(statement-terms form)
313
(concrete-match-pattern (let ((graph-term (first match-pattern)))
314
(cons graph-term (loop for term in (rest match-pattern)
315
collect (if (eq term graph-term)
316
`(max 0 ,term) ; cannot pass non-concrete term
318
(ignored-variables (remove-if #'symbol-package uniqued-result-arguments))
319
(extended-variables (append accumulated-variables (remove-if-not #'symbol-package match-result-arguments)))
320
(next-continuation (funcall compute-next-continuation extended-variables)))
322
;; constrain aliased variables
323
(unless (equal uniqued-result-arguments match-result-arguments)
324
(setf next-continuation
325
`(if (and ,@(loop for v in match-result-arguments
326
for u in uniqued-result-arguments
331
`(flet ((,verb-continuation (,@uniqued-result-arguments)
332
,@(when ignored-variables `((declare (ignorable ,@ignored-variables))))
333
(trace-bgp bgp.extension-matched ,@uniqued-result-arguments)
336
(declare (dynamic-extent #',verb-continuation))
337
(trace-bgp-operator match-index ',form)
338
(when *match-target-graph* (setf ,graph-variable effective-dataset-graphs))
339
(incf match-requests)
340
(match-index transaction ,@concrete-match-pattern #',verb-continuation :test ',test))))
342
(defun compute-bgp-pattern-continuation (form accumulated-variables graph-variable compute-next-continuation)
343
;; iterate over the bindings emitted from the LMDB index
344
(let* ((lmdb-continuation (gensym "bgp-pattern"))
345
(dimensions (cons graph-variable (statement-dimensions form)))
346
(match-result-arguments (loop ;; for term in (cons graph-variable (statement-terms form))
347
for term in dimensions
348
collect (cond ((or (not (variable-p term)) (member term accumulated-variables))
352
(uniqued-result-arguments (loop for vars on match-result-arguments
353
for v in match-result-arguments
354
collect (if (member v (rest vars))
357
(match-pattern (cons graph-variable
358
;; replace a variable which was not yet returned from a match
359
;; (loop for term in (statement-terms form) collect (if (wild-variable-p term) wildcard-term term))
360
(statement-terms form)
362
(concrete-match-pattern (let ((graph-term (first match-pattern)))
363
(cons graph-term (loop for term in (rest match-pattern)
364
collect (if (eq term graph-term)
365
`(max 0 ,term) ; cannot pass non-concrete term
367
(ignored-variables (remove-if #'symbol-package uniqued-result-arguments))
368
(extended-variables (append accumulated-variables (remove-if-not #'symbol-package match-result-arguments)))
369
(next-continuation (funcall compute-next-continuation extended-variables))
371
(destructuring-bind (g s p o) concrete-match-pattern
372
`(vector ,g ,s ,p ,o))))
373
;; constrain aliased variables
374
(unless (equal uniqued-result-arguments match-result-arguments)
375
(setf next-continuation
376
`(if (and ,@(loop for v in match-result-arguments
377
for u in uniqued-result-arguments
382
`(flet ((,lmdb-continuation (%quad)
383
(let ,(loop for var in uniqued-result-arguments
385
collect `(,var (cffi:mem-aref %quad 'rlmdb:term-id ,index)))
386
,@(when ignored-variables `((declare (ignorable ,@ignored-variables))))
387
(trace-bgp bgp.lmdb-matched ,@uniqued-result-arguments)
388
(note-graph-reference (%quad-context %quad))
389
(incf match-responses)
390
,next-continuation)))
391
(declare (dynamic-extent #',lmdb-continuation))
392
(trace-bgp-operator rlmdb:map-repository-statements ',concrete-match-pattern)
393
(when *match-target-graph* (setf ,graph-variable effective-dataset-graphs))
394
(incf match-requests)
395
(rlmdb:map-repository-statements #',lmdb-continuation transaction
397
:revision-predicate (compute-revision-predicate (list :first transaction-min-ordinal
398
:last transaction-max-ordinal))))))
401
(defun compute-bgp-constant-continuation (form accumulated-variables graph-variable compute-next-continuation)
402
;; if all are constant, just count
403
(let* ((concrete-match-pattern (cons graph-variable (statement-terms form)))
404
(next-continuation (funcall compute-next-continuation accumulated-variables))
405
(count-form `(read-rlmdb-pattern-count (transaction-repository transaction) ,@concrete-match-pattern
406
:revision-predicate (compute-revision-predicate (list :first transaction-min-ordinal
407
:last transaction-max-ordinal)))))
409
`(progn (incf match-requests)
410
(trace-bgp-operator read-rlmdb-pattern-count ',form)
411
(cond ((plusp ,count-form)
412
(incf match-responses)
417
(defun compute-bgp-statement-continuation (form accumulated-variables graph-variable compute-next-continuation)
418
;; compute the bgp continuation for each of the variant bgp statement patterns
419
;; this version resets a non-constant graph for each result
420
(let ((predicate-term (statement-predicate form))
421
(is-index-statement (and (consp form) (member :test form))))
422
(cond ((property-path-p predicate-term)
423
(compute-bgp-path-continuation form accumulated-variables graph-variable compute-next-continuation))
424
((active-verb-p predicate-term)
425
(compute-bgp-active-verb-continuation form accumulated-variables graph-variable compute-next-continuation))
426
((extension-operator-p predicate-term)
427
(compute-bgp-extension-continuation form accumulated-variables graph-variable compute-next-continuation))
429
(compute-bgp-index-continuation form accumulated-variables graph-variable compute-next-continuation))
430
((find-if #'variable-p (statement-terms form))
431
(compute-bgp-pattern-continuation form accumulated-variables graph-variable compute-next-continuation))
432
((constant-statement-pattern-p form)
433
(compute-bgp-constant-continuation form accumulated-variables graph-variable compute-next-continuation))
435
(log-warn "compute-bgp-statement-continuation: unknown form: ~s" form)
437
(log-warn "compute-bgp-statement-continuation: unknown form: ~s" ',form)
438
(funcall compute-next-continuation accumulated-variables))))))
441
(defmethod compute-bgp-star-lambda ((repository lmdb-repository) (body list) &key
442
(base-dimensions ()) ; initial solution field variables
443
(projection-dimensions (bgp-projected-dimensions body))
444
(wildcard-term (repository-wildcard-term repository))
445
(default-context-term (repository-default-context-term repository))
446
(named-contexts-term (repository-named-contexts-term repository))
447
graph ; if in a graph clause, then either a variable or a literal
449
(named-graphs (dataset-named-graphs dataset-graphs))
450
(default-graphs (dataset-default-graphs dataset-graphs))
452
(variables (if (variable-p graph)
453
(cons graph (expression-variables body))
454
(expression-variables body)))
455
(dynamic-variables ())
456
(transaction *transaction*)
457
(trace *compute-bgp-lambda.trace*))
458
"Generate a matching operator for a direct LMBD access path from a BGP which involves a collated subject pattern.
459
allow for variable subject with bindings clauses, constant and variable objects, property path patterns and filters
461
lmdb contexts can use the shard-based methods directly.
465
(setf variables (remove-duplicates variables :from-end t))
467
(warn "graphs ignored ~s." graphs)
468
(log-warn "graphs ignored ~s." graphs))
469
;; use the dimensions provided in the call
470
;; (setf projection-dimensions (expression-dimensions body))
471
(let* (;; if the query had a literal graph term, provide a variable
472
(graph-variable (if (variable-p graph) graph '.graph))
473
;; note if the graph variable is among the initial solutions
474
(initial-solution-graph-variable (find graph base-dimensions))
475
(bgp-id (second (assoc 'spocq.a::|id| body)))
476
;; get the a-list of variables which were inferred to be related through a sameTerm filter constraint
477
;; in order to mirror any binding/setting
478
(equivalents (rest (assoc 'spocq.a::|equivalents| body)))
479
;;!!! still builds state for triples with paths even though cursors and terms are handled out-of-line
480
;; adjust bounds to account for per-thread cursors
481
(slice (rest (assoc 'spocq.a:|slice| body)))
482
(perform-slice (or (getf slice :start) (getf slice :offset)
483
(getf slice :end) (getf slice :count)))
484
(projection-variable-count (length projection-dimensions))
485
(equivalent-variables (mapcar #'first equivalents))
486
;; remove equivalents forrm those which the matching function emits
487
(collection-variables (difference-dimensions projection-dimensions equivalent-variables))
488
(blank-nodes (expression-blank-nodes body))
489
(blank-node-map (loop for node in blank-nodes
490
collect (cons node (if (spocq:blank-node-constant-p node)
491
(rlmdb:value-term-number node)
493
(default-context-term-number (rlmdb:value-term-number default-context-term))
494
(named-contexts-term-number (rlmdb:value-term-number named-contexts-term))
495
(base-bindings base-dimensions)
496
(initial-bindings (union-dimensions base-bindings dynamic-variables))
497
(term-id-map (make-hash-table :test 'eql))
498
(id-term-map (make-hash-table :test 'eql))
500
(declarations (remove 'declare body :key #'first :test-not #'eq))
501
(bindings-forms (remove-if-not #'bindings-form-p body))
502
(collated-statements (remove-if-not #'collated-statement-pattern-p body))
503
(collated-constant-statements (remove-if-not #'(lambda (statement)
504
(or (spocq.e:constantp (statement-object statement))
505
(find (statement-object statement) dynamic-variables)))
506
collated-statements))
507
(collated-variable-statements (remove-if #'(lambda (statement)
508
(member statement collated-constant-statements))
509
collated-statements))
510
(constant-statements (remove-if-not #'constant-statement-pattern-p body))
511
;; this is here even though the logic in collate-subject-bgp-forms culls them
512
;; the respective cardinalaities are likely high enough the a nested loop either before or after the
513
;; collated segment is unlikely to perform well for large queries - eg nxp's 40da4ed939d965f771cc6c91b4c55381df55733e
514
(path-statements (remove-if-not #'(lambda (statement) (and (triple-form-p statement)
515
(verb-p (statement-predicate statement))))
517
(filter-statements (remove-if-not #'filter-form-p body))
520
(let ((recognized (append declarations bindings-forms constant-statements
521
collated-constant-statements collated-variable-statements
522
path-statements filter-statements)))
523
(unless (= (length body) (length recognized))
524
(log-warn "compute-bgp-star-lambda ignoring: ~s"
525
(set-difference body recognized))))
526
(setf declarations (reduce #'append declarations :key #'rest))
529
(print (list :declarations declarations))
530
(print (list :bindings bindings-forms))
531
(print (list :constant constant-statements))
532
(print (list :collated (list collated-constant-statements collated-variable-statements)))
533
(print (list :path path-statements))
534
(print (list :filters filter-statements)))
536
;; provide methods to bind the terms and compute the continuation respective the statement form
537
(labels ((intern-if-constant (object)
538
(cond ((variable-p object)
540
((spocq:blank-node-p object)
541
(or (rest (assoc object blank-node-map))
542
(error "lost a blank-node: ~a" object)))
543
((property-path-p object)
544
(rlmdb:intern-property-path object))
546
(let ((number (rlmdb:value-term-number object)))
547
(setf (gethash number id-term-map) object)
548
(setf (gethash object term-id-map) number)))))
549
(intern-graph-if-constant (object)
550
(cond ((variable-p object)
553
(rlmdb:graph-term-number object :allow-all t))))
554
(intern-predicate (expression)
556
(cons (cons (first expression) (mapcar #'intern-predicate (rest expression))))
557
(t (rlmdb:value-term-number expression))))
558
(index-statement-p (statement)
559
(and (consp statement) (member :test statement)))
561
(compute-base-continuation (accumulated-variables)
562
"the base solution transfers the accumulated bindings into a solution row"
563
(declare (ignore accumulated-variables))
564
(let ((collect-form `(collect-solution ,@collection-variables)))
566
`(if (minusp (decf solution-offset))
567
(if (and solution-count (minusp (decf solution-count)))
572
;;; if some pattern requires a term which is not found, the bgp has to yield no result.
573
(intern-statement (statement)
574
(destructuring-bind (tag . spo) statement
577
(destructuring-bind (s p o &rest args) spo
578
`(,tag ,(intern-if-constant s)
579
,(cond ((extension-operator-p p)
584
(intern-if-constant p)))
585
,(intern-if-constant o)
589
(find-bound-filter (filters variables)
590
(loop for filter-form in filters
591
for filter-variables = (expression-variables (second filter-form))
592
when (null (set-difference filter-variables variables))
594
(intern-bindings-form (form)
595
(destructuring-bind (op solutions variables) form
596
`(,op ,(loop for solution in solutions
597
collect (loop for term in solution collect (intern-if-constant term)))
599
(setf bindings-forms (mapcar #'intern-bindings-form bindings-forms))
600
(setf collated-constant-statements (mapcar #'intern-statement collated-constant-statements))
601
(setf collated-variable-statements (mapcar #'intern-statement collated-variable-statements))
602
(setf constant-statements (mapcar #'intern-statement constant-statements))
603
(setf path-statements (mapcar #'intern-statement path-statements))
605
(setf default-graphs (mapcar #'intern-graph-if-constant default-graphs))
606
(setf named-graphs (mapcar #'intern-graph-if-constant named-graphs))
607
(when (and (null default-graphs) (null named-graphs))
608
;; compose the "default dataset"
609
(setf default-graphs (list default-context-term-number)
610
named-graphs (list named-contexts-term-number)))
611
(when (some (complement #'plusp) named-graphs)
612
;; permit a singleton abstract graph only
613
(assert (null (rest named-graphs)) ()
614
"Invalid wildcard dataset named graphs specification: ~a." named-graphs)
615
(setf named-graphs (first named-graphs)))
616
(when (some (complement #'plusp) default-graphs)
617
(assert (null (rest default-graphs)) ()
618
"Invalid wildcard dataset default graph specification: ~a." default-graphs)
619
(setf default-graphs (first default-graphs)))
620
;; allow for just the graph as a variable for literal bgp patterns?
621
(when (variable-p graph)
622
(setf projection-dimensions (union-dimensions (list graph) projection-dimensions)))
624
(setf equivalents (loop for (var . value-or-var) in equivalents collect (cons var (intern-if-constant value-or-var))))
626
(let* (;; the interface operator accepts the repository, and optionally an initial spo solution field, and/or a graph sequence
627
;; this wraps an operator which iterates outer over the spo field to bind the starting values for those variables
628
;; for each pass and then iterates secondarily over the graph field to perform the pattern matches.
630
;; the top-level match form starts with all patterns, _no_ bound variables, and the cursor lists
631
;; corresponding to the patterns. not even the solution variables are bound initially, as they may themselves
632
;; start out as wild cards.
633
#+(or) ;; the initial call no longer includes constant terms
634
(base-bindings (if (or graphs (not graph))
635
(union-dimensions (list graph-variable) base-dimensions)
637
;; this cannot work in this way, as it causes all variables to
638
;; be handled as if they are pre-bound, with the consequence, that
639
;; they are matched, but never extractedd from the cursor...
640
;; (initial-bindings (union-dimensions base-bindings variables))
641
(match-invocation-form
642
;; the effective process depends on a combination of :
644
;; - the query graph term: null, variable, constant, abstract
645
;; - the dataset default graphs: null, constant, singleton abstract
646
;; normalized above to reflect the default v/s declare composition
647
;; - the dataset named graphs: null, constant, singleton abstract
648
;; normalized above to reflect the default v/s declare composition
649
;; - a propagated solution graph: null, variable
651
;; if graph is bound, the bgp is in a graph clause
652
;; - if dataset.named-graphs are supplied : they provide the argument for the initial
653
;; match and the result serves for successive, nested calls
654
;; - allow for propagation to be constrained by the dataset
655
;; - allow for - and constrain, a constant graph
656
;; - no dataset.named-graphs
657
;; no match is possible
658
;; no graph indicates the bgp is outside the scope of a graph clause
659
;; - if dataset.default-graphs are supplied : match them the first time _and_
660
;; also for subsequent matches, but substitute the default graph in the result field
661
;; - no dataset.default-graphs
662
;; no match is possible
664
;; the dataset definition can be a singleton set of a designator for default, named, or all
665
;; graphs, in which case the role when matching is the same os that of a concrete graph
668
;; in the scope of a graph clause
670
;; if the dataset definition includes named graphs, permit propgation, constants, or a dynamic binding
671
(cond (initial-solution-graph-variable
672
;; propagate form outside the bgp
673
`(if (eql ,wildcard-term ,initial-solution-graph-variable)
674
(match-patterns ',named-graphs)
675
(when ,(or (eql named-contexts-term-number named-graphs)
676
(eql wildcard-term named-graphs)
677
(if (consp named-graphs)
678
`(member ,initial-solution-graph-variable ',named-graphs)
679
`(eql ,initial-solution-graph-variable ,named-graphs)))
680
(match-patterns ,initial-solution-graph-variable))))
682
`(if (not (eql ,graph ,wildcard-term))
683
(when ,(or (eql named-contexts-term-number named-graphs)
684
(eql wildcard-term named-graphs)
685
(if (consp named-graphs)
686
`(member ,graph ',named-graphs)
687
`(eql ,graph ,named-graphs)))
688
(match-patterns ,graph))
689
(match-patterns ',named-graphs))
690
;; bind aa request argument or match as wild
692
`(let ((argument (query-binding-value ',graph)))
693
(if (integerp argument)
694
;; execute with a request parameter if the dataset graphs permit
695
(when ,(or (eql named-contexts-term-number named-graphs)
696
(eql wildcard-term named-graphs)
697
(if (consp named-graphs)
698
`(member argument ',named-graphs)
699
`(eql argument ,named-graphs)))
700
(match-patterns argument))
701
;; otherwise, start the match unbound
702
(match-patterns ',named-graphs))))
704
(setf graph (intern-graph-if-constant graph))
705
;; match concrete literal or abstract (named, all)
706
(when (or (eql named-contexts-term-number named-graphs)
707
(eql wildcard-term named-graphs)
708
(eql graph named-graphs)
709
(and (consp named-graphs) (member graph named-graphs)))
710
`(match-patterns ,graph)))
712
(error "Invalid graph term: ~s." graph))))
714
;; if the dataset includes no named graph, then a graph term cannot match
715
;; see 13.2.1: the dataset includes no named graphs
717
(cond (default-graphs
718
;; iff merging into the default graph, the match result must be coerced
719
`(match-patterns ',default-graphs ,default-context-term-number))
722
(transaction-match-form
723
`(let* ((revision (transaction-revision transaction))
724
(rlmdb:repository (repository-lmdb-repository revision)))
725
(lmdb:with-transaction ((transaction (lmdb:make-transaction rlmdb:repository :flags liblmdb:+rdonly+))
726
:initial-disposition :begin :normal-disposition :abort
727
:error-disposition :abort)
728
,match-invocation-form))))
730
(labels ((compute-next-continuation (accumulated-variables forms filter-forms)
731
;; the first match must always use the graph term - either a constant from/from-named iri
732
;; or one of the wild-card, designators default/named/all.
733
;; if wild, then the result matters, but if constant, then the result does not.
734
;; all subsequent iterations use the result from the first match and do not need to rebind
735
(let ((bound-filter (and filter-forms
736
(find-bound-filter filter-forms accumulated-variables))))
738
(setf filter-forms (remove bound-filter filter-forms))
739
(compute-bgp-filter-continuation bound-filter
740
accumulated-variables ;; for symmetry
742
#'(lambda (accumulated-variables)
743
(compute-next-continuation accumulated-variables
745
(remove bound-filter filter-forms)))))
747
(let ((form (first forms)))
750
(compute-bgp-statement-continuation form
751
accumulated-variables
753
#'(lambda (accumulated-variables)
754
(compute-next-continuation accumulated-variables
758
(compute-bgp-binding-continuation form
759
accumulated-variables
761
#'(lambda (accumulated-variables)
762
(compute-next-continuation accumulated-variables
766
(let ((statements (rest form)))
767
(if (rest statements)
768
(compute-bgp-collated-continuation (rest form)
769
accumulated-variables
771
#'(lambda (accumulated-variables)
772
(compute-next-continuation accumulated-variables
775
;; if it is somehow just one pattern
776
(compute-bgp-statement-continuation (first statements)
777
accumulated-variables
779
#'(lambda (accumulated-variables)
780
(compute-next-continuation accumulated-variables
782
filter-forms)))))))))
784
;; if any are lect generate the test as the last thing before collecting
785
(compute-bgp-filter-continuation (first filter-forms)
786
accumulated-variables
788
#'(lambda (accumulated-variables)
789
(compute-next-continuation accumulated-variables
791
(rest filter-forms)))))
793
(compute-base-continuation
794
accumulated-variables))))))
795
(let ((triple-matching-form
796
(compute-next-continuation initial-bindings
797
(append bindings-forms
799
collated-constant-statements
800
(when collated-variable-statements
801
(list `(collated-statements ,@collated-variable-statements)))
806
;; (print (list :match-form transaction-match-form :graph graph :named-graphs named-graphs))
808
(format *trace-output* "~&*compute-bgp-lambda.trace*~%body ~s~%graph ~s~%named ~s~%default ~s~%initial-solution-graph-variable ~s~%graph-variable ~s"
809
body graph named-graphs default-graphs initial-solution-graph-variable graph-variable))
810
;; the actual query operator takes one of two forms. iff base dimensions are included, it is
811
;; generated to expect a solution field source, and to iterate the entire operator over the field's solutions.
812
;; without base dimensions, it runs autonomously.
813
;; the combination makes it possible to construct query processing both as reduction through bottom-up combination
814
;; and as solution combination, and even as some combination of the two
815
;; where evaluation materialized all intermediate solutions, it did not matter that the reduction order agree
816
;; with the matching order. the intermediate matching results were cached for delayed use as combination arguments.
817
;; where the solution data flows form one operator tot he next, with the intent to limit the space required for
818
;; intermediate fields by streaming results, the contradictory evaluation order would still require buffered
819
;; materialization. for example an expression of the form
821
;; (join (?a ?b ?c) (join (?b ?c) (?c)))
823
;; would reduce with data flow
825
;; ((?b ?c) . (?c) . (?a ?b ?c))
827
;; but match with data flow
828
;; (?c) -> (?b ?c) -> (?a ?b ?c)
830
;; which entail inverted match orders. the question is, how to enable both?
832
(let* ((all-bgp-variables
833
;; look for a dynamic binding for all variables in the bgp
834
;; this allows to specify dynamic variables retrospectively, but it may introduce
835
;; an issue related to extracting them from the cursor
836
(set-difference variables base-bindings))
838
`(lambda (bgp-destination ,@(when base-dimensions '(source)))
839
(declare (optimize ,@*field-optimization*))
841
`((declare ,@declarations)))
842
(assert-argument-types bgp-match
843
(bgp-destination (or channel function))
844
,@(when base-dimensions '((source (or channel function)))))
845
(let* ((repository-id (repository-id *repository*))
846
(revision-id (repository-revision-id *task*))
847
(transaction *transaction*)
848
(transaction-record (transaction-record transaction))
849
(transaction-min-ordinal (transaction-min-revision-ordinal transaction))
850
(transaction-max-ordinal (transaction-max-revision-ordinal transaction))
851
(*thread-operations* (cons (list 'spocq.a:|bgp| ',body) *thread-operations*))
855
(result-page-length (channel-page-length bgp-destination))
856
(result-index *field-page-length*)
859
(graph-ids-read (make-term-id-cache :single-thread t))
860
(*wildcard-identifier* ,wildcard-term)
861
(*default-context-identifier* ,default-context-term-number)
862
;; the original formulation looked for dynamic bindings for all variables
863
;; which were specified as dynamic in the first request but provided to
864
;; be accepted from a propagation source
866
,@(loop for var in (intersection (set-difference dynamic-variables base-bindings) variables)
867
collect `(,var (query-binding-term-number ',var)))
868
,@(loop for var in all-bgp-variables
869
collect `(,var (query-binding-term-number ',var)))
870
,@(loop for (nil . var) in blank-node-map
871
when (variable-p var)
872
collect `(,var ,wildcard-term))
873
,@(when perform-slice
874
(destructuring-bind (&key (start 0) (offset start) end (count (when end (- end offset))))
876
`((solution-offset ,offset)
877
(solution-count ,count))))
879
;; if just paths, constants or other things whic do not actuall match
880
(declare (ignorable repository-id revision-id transaction-record)
881
(ignorable transaction-min-ordinal transaction-max-ordinal))
882
,@(when all-bgp-variables ; a variable may have been folded into a constant
883
`((declare (ignorable ,@all-bgp-variables))))
885
(labels ((coerce-to-term-id (term-number)
886
(typecase term-number
888
(t (log-warn "bgp match result not a term number: ~s." term-number)
890
(collect-solution ,collection-variables
891
(trace-bgp bgp.match.collect-solution ,@collection-variables)
892
(case *match-target-graph*
895
(t (log-warn "anomalous match target graph: ~s." *match-target-graph*)))
897
(unless (task-active-p *query*)
898
(log-debug "bgp premature completion: ~a" *query*)
899
(complete-solutions))
900
,(if projection-dimensions
901
`(let ,(loop for (alias . equivalent) in equivalents ; bind left-over constants
902
collect (list alias equivalent))
903
(next-solution-location)
904
(locally (declare (type (simple-array fixnum (* ,projection-variable-count)) result-page)
905
(type fixnum result-index)
906
(ftype (function (t) fixnum) coerce-to-term-id)
907
(optimize ,@*field-optimization*))
908
(setf ,@(loop for var-index from 0
909
for variable in projection-dimensions
910
nconc `((aref result-page result-index ,var-index)
911
(coerce-to-term-id ,variable))))))
912
'(next-solution-location))
914
(next-solution-location ()
915
;; return a page (possible newly created) and the next free location in that page
916
(when (>= (incf result-index) result-page-length)
917
(unless (check-query-status)
918
(complete-solutions))
919
(when result-page (put-result result-page))
920
(setf result-page (new-field-page bgp-destination result-page-length ,projection-variable-count)
922
(values result-page result-index))
923
(complete-solutions ()
924
(trace-bgp bgp.match.complete-solutions result-count)
925
(incf *match-requests* match-requests)
926
(incf *match-responses* match-responses)
927
(when (plusp (hash-table-count graph-ids-read))
928
(with-locked-cache ((transaction-read-graph-ids transaction))
929
(loop for id being each hash-key of graph-ids-read
930
do (setf (transaction-graph-id-read transaction id) t))))
931
(log-debug "bgp matches+counts: ~s: requests: ~s, matches: ~s, solutions: ~s"
932
repository-id match-requests match-responses result-count)
934
(let ((page-result-count (1+ result-index)))
935
(when (< page-result-count result-page-length)
937
(adjust-page result-page (list page-result-count ,projection-variable-count)))))
938
(put-result result-page))
939
(complete-field bgp-destination)
940
(incf-stat *solutions-constructed* result-count)
941
(return-from :bgp-match result-count))
943
(trace-bgp bgp.put bgp-destination ',projection-dimensions page)
944
(put-field-page bgp-destination page)
945
(unless (task-active-p *query*)
946
(complete-field bgp-destination)
947
(return-from :bgp-match result-count)))
948
(note-graph-reference (graph)
949
(unless (eql graph last-graph-id)
950
(setf last-graph-id graph)
951
(setf (gethash graph graph-ids-read) t))))
952
(trace-bgp bgp.start ',bgp-id (task-id *query*)
953
repository-id revision-id ',projection-dimensions)
954
;; (print (list :bgp.start ',id (task-id *query*) repository-id revision-id ',projection-dimensions))
955
(incf-stat *algebra-operations*)
956
(trace-bgp bgp.repository *repository* repository-id revision-id :dataset ',dataset-graphs)
957
(if ,(if (find-if #'property-path-p body :key #'third) t 'revision-id)
958
;; unless there are paths present (actually zero-length paths is the issue)
959
;; require an id to perform the query. otherwise there can be no result - skip it
961
; push this logic to the outer-most match call to pass
962
; the entire graph set as the context argument
963
,(let ((iterate-over-graphs `(flet ((match-patterns (effective-dataset-graphs &optional (*match-target-graph* nil)
964
,@(when (eq graph-variable '.graph)
965
'(&aux (.graph *wildcard-identifier*)))
967
;; effective-dataset-graphs is the initial match set
968
;; *match-target-graph*, if bound indicates to coerce the graph term to that
969
;; if the graph is coerced, subsequent matches re-use the original set
970
;; this manages, eg. the case where a set of graphs is 'merged' into the
971
;; default graph, but the result must indicate the default graph only
972
(trace-bgp bgp.next-graph effective-dataset-graphs *match-target-graph*)
973
(let ((*match-property-path-context* effective-dataset-graphs))
974
(when (= *wildcard-identifier* ,graph-variable)
975
(setf ,graph-variable effective-dataset-graphs))
976
,triple-matching-form)))
977
,transaction-match-form)))
978
(when *compute-bgp-lambda.debug*
979
(pprint iterate-over-graphs))
981
`(do-pages (page source)
982
,(let ((macros (loop for variable in base-dimensions
984
collect `(,variable (aref page page-index ,i)))))
985
`(locally (declare (type (simple-array fixnum (* ,(length base-dimensions))) page)
986
(optimize ,@*field-optimization*))
987
(assert (and (typep page '(simple-array fixnum))
988
(= (array-dimension page 1) ,(length base-dimensions)))
990
"Invalid propagated page: ~s" page)
991
(trace-data bgp-match.dequeue bgp-destination ',base-dimensions page (term-value-field page))
992
(loop for page-index from 0 below (array-dimension page 0)
993
do (symbol-macrolet ,macros
994
,iterate-over-graphs)))))
995
iterate-over-graphs))
996
(trace-bgp bgp.complete-after-graph-iteration)
997
(complete-solutions))
998
(progn (trace-bgp bgp.suppress repository-id)
999
(log-debug "suppress query for empty repository: ~a" repository-id)
1000
(complete-solutions))))
1001
(log-warn "incomplete bgp: ~s." ',bgp-id)
1003
(log-trace "query [~a] bgp [~a] lambda: ~s"
1004
(task-id *query*) bgp-id query-lambda)
1005
(values query-lambda
1006
projection-dimensions))))))))