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

KindCoveredAll%
expression4941030 48.0
branch47136 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; -*-
2
 
3
 ;;; compile a bgp into a function when i matches the statement pattern combination
4
 ;;;
5
 ;;;    ( ?s <p_i> "o" )*
6
 ;;;    ( ?s <p_j> ?o )+
7
 ;;;
8
 ;;; with possible addition of paths
9
 ;;;
10
 ;;;    ( ?s <p1>* ?x )
11
 ;;;    ( ?y <p1>* ?o )
12
 ;;;
13
 ;;; to serve as the anchor for a bounded (kleene) path,
14
 ;;; as well as values bindings and/or filters
15
 ;;;
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
20
 ;;; - path patterns
21
 ;;; + filters interposed as soon as all variables are bound
22
 
23
 (in-package :org.datagraph.spocq.implementation)
24
 
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))
33
               (iri-p predicate)))))
34
 
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)))))
42
 
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"
49
 
50
   (when (symbolp (first statements)) (pop statements))
51
   (case *star-bgp-mode*
52
     (:enabled
53
      (some #'(lambda (statement)
54
                (typep statement '(cons (eql declare) (cons (cons (eql spocq.e::processing-mode) (cons (eql :collated)))))))
55
            statements))
56
     (t
57
      nil)))
58
 
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
63
     (declare (ignore op))
64
     (let* ((bindings-op (gensym "bgp-values-"))
65
            (extended-variables (union-dimensions accumulated-variables variables))
66
            (next-continuation (funcall compute-next-continuation extended-variables)))
67
       `(progn
68
          (trace-bgp-operator spocq.a:|bindings| ',variables)
69
          (flet ((,bindings-op ,variables
70
                   (declare (ignorable ,@variables))
71
                   ,next-continuation))
72
            (declare (dynamic-extent #',bindings-op))
73
            (loop for solution in ',solutions
74
              do (apply #',bindings-op solution)))))))
75
 
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
80
     (declare (ignore op))
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)))
85
       `(progn
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))
94
                                          ,_test-expression))
95
                       ,@test-variables)
96
                      ,next-continuation
97
                      ;; must provide a true result if the filter fails for the scan to continue
98
                      t))
99
               `(if ,_test-expression ,next-continuation t))))))
100
 
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 
107
   ;;
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
113
   ;;
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
117
   ;;
118
   ;; invoke the repository map operator, capture the results and continue with them
119
   ;; as for other patterns.
120
 
121
   (multiple-value-bind (c-subject c-predicates c-variables)
122
                        (loop for (tag s p o) in collation-statements
123
                          for subject = s
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))
136
                                                    (gensym)
137
                                                    v)))
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))
143
            )
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
148
                             unless (eq u v)
149
                             collect `(= ,u ,v)))
150
                    ,next-continuation
151
                    t)))
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)))))))
173
 
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))
181
                                                   (gensym "constant"))
182
                                                  (t
183
                                                   term))))
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))
187
                                                  (gensym)
188
                                                  v)))
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)
193
                               ))
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
198
                                                                   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
206
                           unless (eq u v)
207
                           collect `(= ,u ,v)))
208
                  ,next-continuation
209
                  t)))
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)
214
               ,next-continuation))
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))))
220
 
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)
235
               ,next-continuation))
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
240
                            transaction
241
                            #',verb-continuation
242
                            ,@match-parameters))))
243
 
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))
251
                                                   (gensym "constant"))
252
                                                  (t
253
                                                   term))))
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))
257
                                                  (gensym)
258
                                                  v)))
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)
263
                               ))
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
268
                                                                   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)))
272
     
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
278
                           unless (eq u v)
279
                           collect `(= ,u ,v)))
280
                  ,next-continuation
281
                  t)))
282
     `(flet ((,verb-continuation (,@uniqued-result-arguments)
283
               ,@(when ignored-variables `((declare (ignorable ,@ignored-variables))))
284
               (trace-bgp bgp.extension-matched ,@uniqued-result-arguments)
285
               ,next-continuation))
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))))
290
 
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))
300
                                                   (gensym "constant"))
301
                                                  (t
302
                                                   term))))
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))
306
                                                  (gensym)
307
                                                  v)))
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)
312
                               ))
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
317
                                                                   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)))
321
     
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
327
                           unless (eq u v)
328
                           collect `(= ,u ,v)))
329
                  ,next-continuation
330
                  t)))
331
     `(flet ((,verb-continuation (,@uniqued-result-arguments)
332
               ,@(when ignored-variables `((declare (ignorable ,@ignored-variables))))
333
               (trace-bgp bgp.extension-matched ,@uniqued-result-arguments)
334
               (incf match-reqults)
335
               ,next-continuation))
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))))
341
 
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))
349
                                                   (gensym "constant"))
350
                                                  (t
351
                                                   term))))
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))
355
                                                  (gensym)
356
                                                  v)))
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)
361
                               ))
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
366
                                                                   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))
370
          (quad-pattern
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
378
                           unless (eq u v)
379
                           collect `(= ,u ,v)))
380
                  ,next-continuation
381
                  t)))
382
     `(flet ((,lmdb-continuation (%quad)
383
               (let ,(loop for var in uniqued-result-arguments
384
                       for index from 0
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
396
                                         ,quad-pattern
397
                                         :revision-predicate (compute-revision-predicate (list :first transaction-min-ordinal
398
                                                                                               :last transaction-max-ordinal))))))
399
 
400
 
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)))))
408
     ;; constant match
409
     `(progn (incf match-requests)
410
        (trace-bgp-operator read-rlmdb-pattern-count ',form)
411
        (cond ((plusp ,count-form)
412
               (incf match-responses)
413
               ,next-continuation)
414
              (t t)))))
415
 
416
 
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))
428
           (is-index-statement
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))
434
           (t
435
            (log-warn "compute-bgp-statement-continuation: unknown form: ~s" form)
436
            `(progn
437
               (log-warn "compute-bgp-statement-continuation: unknown form: ~s" ',form)
438
               (funcall compute-next-continuation accumulated-variables))))))
439
 
440
 
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
448
                                     (dataset-graphs nil)
449
                                     (named-graphs (dataset-named-graphs dataset-graphs))
450
                                     (default-graphs (dataset-default-graphs dataset-graphs))        
451
                                     (graphs nil)
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
460
 
461
   lmdb contexts can use the shard-based methods directly.
462
   "
463
   
464
   ;; ensure uniqness
465
   (setf variables (remove-duplicates variables :from-end t))
466
   (when graphs
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)
492
                                                   (cons-variable)))))
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))
499
 
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))))
516
                                          body))
517
          (filter-statements  (remove-if-not #'filter-form-p body))
518
          )
519
 
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))
527
     #+(or)
528
     (progn
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)))
535
 
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)
539
                       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))
545
                      (t  ;; for spo only
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)
551
                       object)
552
                      (t
553
                       (rlmdb:graph-term-number object :allow-all t))))
554
              (intern-predicate (expression)
555
                (typecase 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)))
560
 
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)))
565
                   (if perform-slice
566
                      `(if (minusp (decf solution-offset))
567
                           (if (and solution-count (minusp (decf solution-count)))
568
                               (complete-solutions)
569
                               ,collect-form)
570
                           solution-offset)
571
                      collect-form)))
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
575
                  (case tag
576
                    (spocq.a:|triple|
577
                             (destructuring-bind (s p o &rest args) spo
578
                               `(,tag ,(intern-if-constant s)
579
                                      ,(cond ((extension-operator-p p)
580
                                              p)
581
                                             ((active-verb-p p)
582
                                              p)
583
                                             (t
584
                                              (intern-if-constant p)))
585
                                      ,(intern-if-constant o)
586
                                      ,@args)))
587
                    (t
588
                     (cons tag spo)))))
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))
593
                  return filter-form))
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)))
598
                        ,variables))))
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))
604
       
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)))
623
       
624
       (setf equivalents (loop for (var . value-or-var) in equivalents collect (cons var (intern-if-constant value-or-var))))
625
 
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.
629
              ;;
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)
636
                               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 :
643
               ;;
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
650
               ;;
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
663
               ;;
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
666
               ;;
667
               (if graph
668
                   ;; in the scope of a graph clause
669
                   (cond (named-graphs
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))))
681
                                ((variable-p graph)
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
691
                                 #+(or)
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))))
703
                                ((iri-p graph)
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)))
711
                                (t
712
                                 (error "Invalid graph term: ~s." graph))))
713
                         (t
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
716
                          nil))
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))
720
                         (t
721
                          nil))))
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))))
729
 
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))))
737
                      (cond (bound-filter
738
                             (setf filter-forms (remove bound-filter filter-forms))
739
                             (compute-bgp-filter-continuation bound-filter
740
                                                          accumulated-variables ;; for symmetry
741
                                                          graph-variable
742
                                                          #'(lambda (accumulated-variables)
743
                                                              (compute-next-continuation accumulated-variables
744
                                                                                         forms
745
                                                                                         (remove bound-filter filter-forms)))))
746
                            (forms
747
                             (let ((form (first forms)))
748
                               (case (first form)
749
                                 (spocq.a:|triple|
750
                                          (compute-bgp-statement-continuation form
751
                                                                              accumulated-variables
752
                                                                              graph-variable
753
                                                                              #'(lambda (accumulated-variables)
754
                                                                                  (compute-next-continuation accumulated-variables
755
                                                                                                             (rest forms)
756
                                                                                                             filter-forms))))
757
                                 (spocq.a:|bindings|
758
                                          (compute-bgp-binding-continuation form
759
                                                                            accumulated-variables
760
                                                                            graph-variable
761
                                                                            #'(lambda (accumulated-variables)
762
                                                                                (compute-next-continuation accumulated-variables
763
                                                                                                           (rest forms)
764
                                                                                                           filter-forms))))
765
                                 (collated-statements
766
                                  (let ((statements (rest form)))
767
                                    (if (rest statements)
768
                                        (compute-bgp-collated-continuation (rest form)
769
                                                                           accumulated-variables
770
                                                                           graph-variable
771
                                                                           #'(lambda (accumulated-variables)
772
                                                                               (compute-next-continuation accumulated-variables
773
                                                                                                          (rest forms)
774
                                                                                                          filter-forms)))
775
                                        ;; if it is somehow just one pattern
776
                                        (compute-bgp-statement-continuation (first statements)
777
                                                                            accumulated-variables
778
                                                                            graph-variable
779
                                                                            #'(lambda (accumulated-variables)
780
                                                                                (compute-next-continuation accumulated-variables
781
                                                                                                           (rest forms)
782
                                                                                                           filter-forms)))))))))
783
                            (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
787
                                                              graph-variable
788
                                                              #'(lambda (accumulated-variables)
789
                                                                  (compute-next-continuation accumulated-variables
790
                                                                                             nil
791
                                                                                             (rest filter-forms)))))
792
                            (t
793
                             (compute-base-continuation
794
                              accumulated-variables))))))
795
           (let ((triple-matching-form
796
                  (compute-next-continuation initial-bindings
797
                                             (append bindings-forms
798
                                                     constant-statements
799
                                                     collated-constant-statements
800
                                                     (when collated-variable-statements
801
                                                       (list `(collated-statements ,@collated-variable-statements)))
802
                                                     path-statements)
803
                                             filter-statements)))
804
 
805
         
806
             ;; (print (list :match-form transaction-match-form :graph graph :named-graphs named-graphs))
807
             (when trace
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
820
         ;;
821
         ;;  (join (?a ?b ?c) (join (?b ?c) (?c))) 
822
         ;;
823
         ;; would reduce with data flow
824
         ;;
825
         ;;  ((?b ?c) . (?c) . (?a ?b ?c))
826
         ;;
827
         ;; but match with data flow
828
         ;;  (?c) -> (?b ?c) -> (?a ?b ?c)
829
         ;;
830
         ;; which entail inverted match orders. the question is, how to enable both?
831
         
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))
837
                (query-lambda
838
                `(lambda (bgp-destination ,@(when base-dimensions '(source)))
839
                   (declare (optimize ,@*field-optimization*))
840
                   ,@(when declarations
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*))
852
                          (match-requests 0)
853
                          (match-responses 0)
854
                          (result-page nil)
855
                          (result-page-length (channel-page-length bgp-destination))
856
                          (result-index *field-page-length*)
857
                          (result-count 0)
858
                          (last-graph-id 0)
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
865
                          #+(or)
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))))
875
                                                  slice
876
                                `((solution-offset ,offset)
877
                                  (solution-count ,count))))
878
                          )
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))))
884
                     (block :bgp-match
885
                       (labels ((coerce-to-term-id (term-number)
886
                                  (typecase term-number
887
                                    (fixnum term-number)
888
                                    (t (log-warn "bgp match result not a term number: ~s." term-number)
889
                                        +null-term-id+)))
890
                                (collect-solution ,collection-variables
891
                                  (trace-bgp bgp.match.collect-solution ,@collection-variables)
892
                                  (case *match-target-graph*
893
                                    ((nil) )
894
                                    (-1 )
895
                                    (t (log-warn "anomalous match target graph: ~s." *match-target-graph*)))
896
                                  (incf result-count)
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))
913
                                  result-count)
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)
921
                                          result-index 0))
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)
933
                                  (when result-page
934
                                    (let ((page-result-count (1+ result-index)))
935
                                      (when (< page-result-count result-page-length)
936
                                        (setf result-page
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))
942
                                (put-result (page)
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
960
                             (progn 
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*)))
966
                                                                                                             )
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))
980
                                  (if base-dimensions
981
                                      `(do-pages (page source)
982
                                                 ,(let ((macros (loop for variable in base-dimensions
983
                                                                  for i from 0
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)))
989
                                                               ()
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)
1002
                       result-count)))))
1003
           (log-trace "query [~a] bgp [~a] lambda: ~s"
1004
                      (task-id *query*) bgp-id query-lambda)
1005
           (values query-lambda
1006
                   projection-dimensions))))))))
1007
 
1008