Coverage report: /development/source/library/org/datagraph/spocq-shard/src/core/ssl/ssl-api.lisp

KindCoveredAll%
expression0169 0.0
branch010 0.0
Key
Not instrumented
Conditionalized out
Executed
Not executed
 
Both branches taken
One branch taken
Neither branch taken
1
 ;;; -*- package: org.dataagraph.spocq.implementation -*- 
2
 ;;;
3
 ;;; spocq runtime query processor implementation as an interpreter for
4
 ;;; concrete and abstract model combinations
5
 
6
 ;;; (load (compile-file "/opt/spocq/patches/ssl-api.lisp"))
7
 
8
 ;;; see file:///Development/Downloads/VOWL/WebVOWL_Single_Ont/webvowl.html
9
 ;;; for the ontology diagram generator
10
 
11
 ;;; http : http://www.w3.org/TR/HTTP-in-RDF10
12
 ;;; http-headers : http://www.w3.org/2011/http-headers
13
 
14
 (in-package :spocq.i)
15
 
16
 #|
17
 the current query implementation includes a simple control structure and a very limited
18
 operator set.
19
 the control structure is a data-flow network which is constructed by a reduction combination
20
 of the algebra tree.
21
 if the algebra operations are abstracted into two classes:
22
 - match-quad/bgp
23
 - combine (arity 0 -- 2 operators)
24
 
25
 then the set is 
26
 - query :
27
   - match : (quad-field x quad-pattern) -> solution-field
28
   - combine : (operator x solution-field* ) -> solution-field
29
   - modify : (operator x quad-field x solution-field*) -> quad-field
30
       includes an implicit data commission step
31
 - encode [emit a field to a destination] : (solution-field x media-type) -> (media-stream) -> t
32
 - decode [receive a query from a source] : (media-stream x media-type) -> query
33
 - project : (query) ->  (quad-field) -> solution-field
34
 
35
 the standard operation, as implemented, is
36
 
37
  request (media-stream x media-stream x media-type x media-type x quad-field)
38
 
39
 which the current version expresses as the operator
40
 
41
  pipe-query (input-source output-destination
42
              &key
43
              repository-id revision-id
44
              response-content-type request-content-type)
45
 
46
 of which the repository-id and revision-id designate the quad-field
47
 
48
 and implements the control flow as the fixed form combination
49
 
50
  (respond-to-request (make-query (receive-message input-source request-content-type)
51
                                  :repository repository-id
52
                                  :revision-id revision-id
53
                                  :response-content-type response-content-type)
54
                      output-destination)
55
 
56
 that combination parses the query from the input stream, instantiates a task which binds the dataset and response
57
 content type, distributes the query processing across per-node threads, and emits the collected result to
58
 the destination stream.
59
 this suffices for simple sparql processing, but not to accomplish anything other than a direct data flow
60
 from one the source and back.
61
 
62
 in active development and production environments, however essential a black box
63
 which emits data streams encoded as
64
 application/rdf+xml and application/sparql-results may be, it fails to provide numerous
65
 essential capabilities:
66
 
67
 - provide notification of repository modification
68
 - provide those changes selectively with respect to content and/or destination
69
 - provide introspective statistics as to processing resources
70
 - enforce constraints on repository modifications
71
 - augment repository modifications
72
 - enforce contraints on query patterns
73
 - augment query patterns
74
 - perform one query or update step contingent on the result of some initial step
75
 - share a result stream between multiple successive stept
76
 - consolidate result streams in a single step.
77
 
78
 in general, permit more general data flow combinations among more capable operations than just a
79
 
80
    match - project - encode
81
 
82
 chain.
83
 
84
 the closed process restricts operations on the intermediate results and allows no alternative,
85
 whether for introspective analysis or to provide data to additional detinations and it has no allowance
86
 for additional steps, such as augmenting the entailment process
87
 or constraining the state of the store subsequent to an update.
88
 sparql processing environments have taken to either supporting additional processing
89
 instructions in-line as annotations to the query document,
90
 introducing support elsewhere in the processing stack for processors which are embedded
91
 in a language such as java (for Sesame or Jena), c# (for dotnetrdf) or ruby (for rdf.rb),
92
 or by integrating query processing into a larger application framework, as in rdf pipes[1] or dapaas[2].
93
 
94
 
95
 in oder to reduce application complexity, this implementation proposes to retain
96
 the simple request-response interface of an http-based web service, but to permit
97
 the application to compose request processors from a broader range of constituent steps
98
 in combinations other than just piped sequences.
99
 
100
 as the base, a set of atomic operators implements the individual steps essential to process a query
101
 and adds several to provide capabilities absent from a standard sparql process:
102
 
103
 - decode : (stream x media type) -> query + quad-field + solution-field
104
   parse a an encoded document from a media-stream.
105
   as standard, the standard operator supports application/sparql-query and application/sparql-update
106
   and produces a query form
107
   in addition, it supports alternative query expressions, such as sse and cascalog,
108
   with the same result, as well as graph representations, such a application/rdf+xml and application/n-triples,
109
   in order to permit graph sources, and application/sparql-results, in order to composes
110
   processing chains.
111
 
112
 - matcher : (method x quad-field) -> matcher
113
   marshall rules from a dataset to be used to bind a query
114
 
115
 - bind : (query x quad-field x method) -> query
116
   combine matching inference rules (as the theory quad field) with a dataset
117
   (as the assertion quad field) to bind a query to the dataset
118
   either by modifying its patterns statically or preparing the rules to be applied
119
   when the query executes
120
 
121
 - notify : (query x quad-field) -> (media-stream) -> query
122
   generate and emit results to an arbitrary destination in the course of query processing
123
 
124
 - project : (query x quad-field) -> solution-field
125
   combine the bound query patterns and algebra reduction graph with a quad field to
126
   either yield a solution field/graph or project the solutions back into the original quad field.
127
 
128
 - commit : (quad-field) -> quad-field
129
   commit quad field changes to persistent storage
130
 
131
 - encode-result : ((solution-field + quad-field) x stream x media type) -> (solution-field + quad-field)
132
   combine a solution/graph field with a media type to emit the encoded results a media stream
133
 
134
 - constrain (query x quad-field) -> quad-field
135
   use a query to determine whether to continue processing or abort it
136
 
137
 control-flow operators allow processing patterns in addition to pipelines
138
 
139
 - if : (solution-field x code x code)
140
   execute the consequent or alternative operations dependent on the solution field state:
141
   a null field is false and otherwise true
142
 
143
 - when : (solution-field x code)
144
   execute the consequent operations when the solution field is true
145
 
146
 - not : (solution-field)
147
   invert the solution field state. a null field yields a unit field, otherwise a null field.
148
 
149
 - continue : (query + source)
150
   continue processing with the given query applied with the current agent and repository
151
 
152
 higher-level operations encapsulate elementry sequences and manage completion
153
 
154
 - query
155
 
156
 - update
157
 
158
 - import : (quad-field x quad-field)
159
   a simplified equivalent to a sparql load which obeys the graph store protocol
160
   with respect to HTTP method and resource designators.
161
 
162
 - export 
163
   a simplified equivalent to a sparql wild-card select which obeys the graph store protocol
164
   with respect to HTTP method and resource designators.
165
 
166
 
167
 the operations accept data of the folowing types
168
 
169
  query-prototype
170
   quad-pattern
171
   algebra-operation
172
  query-instance
173
  stream
174
  media-type
175
  media-stream
176
  repository/version
177
  logical-source (request stream, response stream)
178
  solution-field
179
  quad-field
180
 
181
 the abstract queries are represented as RDF graphs and compiled into
182
 operation scripts for execution.
183
 In order to facilitate composition, the execution model combines a global environment
184
 for configuration settings with an execution stack
185
 operators extract thei arguments from the stack and deposit reults upon completion.
186
 
187
 see the doc;rquest-schema; directory for examples.
188
 
189
 a simple query specifies just the text and relies on the request environment to specify
190
 the dataset, decoding and encoding media types, and the result destination
191
 
192
 (<ssl:name> "Simple Query"
193
  <ssl:Query>
194
  (<ssl:location> "select * where {?s ?p ?o}"
195
   <ssl:Decode>
196
   <ssl:Bind>
197
   <ssl:Project>
198
   <ssl:Encode>))
199
 
200
 
201
 in order to introduce entailment, the process specifies an alternative matching
202
 method, in the form of a dataset and entailment mechanism. these are integrated into
203
 the query statically as modification to its graph patterns.
204
 this is the approach of implementation such as jena.
205
 
206
 (<ssl:name> "Simple Query"
207
  <ssl:Query>
208
  (<ssl:location> "select * where {?s ?p ?o}"
209
   <ssl:Decode>
210
   <ssl:dataset> <ssl:location> _:requestRepository <ssl:Dataset>
211
   <ssl:method> <ssl:method> <urn:dydra:RIFCore> <ssl:dataset> _:ontologyRepository <ssl:Matcher>
212
   <ssl:Bind>
213
   <ssl:Project>
214
   <ssl:Encode>))
215
 
216
 
217
 alternatively, the request could specify to delay the effect of the entailment regime as as to apply it
218
 dynamically during execution:
219
 
220
 (<ssl:name> "Simple Query"
221
  <ssl:Query>
222
  (<ssl:location> "select * where {?s ?p ?o}"
223
   <ssl:Decode>
224
   <ssl:Bind>  
225
   <ssl:dataset> <ssl:location> _:requestRepository <ssl:Dataset>
226
   <ssl:method> <ssl:method> <urn:dydra:RIFCore> <ssl:dataset> _:ontologyRepository <ssl:Matcher>
227
   <ssl:Project>
228
   <ssl:Encode>))
229
 
230
 ;;; both approaches share the advantage, that modifications to the concrete dataset do not 
231
 ;;; invalidate the ontological dataset state, but the static approach is unable to
232
 ;;; implement unbounded inference, as its modifications are finite.
233
 
234
 ---
235
 [1] booth.2013
236
 [2] dapaas.2014
237
 
238
 |#
239
 
240
 ;;; abstract operator classes
241
 
242
 (defclass |ssl|:|Operation| (ssl:word)
243
   ()
244
   (:metaclass c2mop:funcallable-standard-class))
245
 
246
 (defclass |ssl|:|Composition| (|ssl|:|Operation|)
247
   ()
248
   (:metaclass c2mop:funcallable-standard-class))
249
   
250
 
251
 ;;; operator api definitions
252
 
253
 (ssl:defword |ssl|:|Abort| (&key transaction dataset)
254
   (:documentation "Abort the given transaction.
255
    If either no transaction exists, or the current transactionis no longer active, the operation
256
    has no effect.
257
    The default transaction is the thread's current transaction.
258
    Returns the transaction.")
259
   (:values (or transaction null))
260
   (:generic-function-class |ssl|:|Operation|)
261
 
262
   (:method (&key (transaction *transaction* t-s) dataset)
263
     ;; if a dataset or a list is specified, operate on the respective transactions
264
     ;; otherwise on the given transaction or the current one
265
     ;; if none is present or both arguments are supplied, signal an error.
266
     (if dataset
267
         (if (and t-s transaction)
268
             (invalid-argument-type |ssl|:|Abort| dataset null)
269
             (ssl::abort-transaction dataset))
270
         (if transaction
271
             (ssl::abort-transaction transaction)
272
             (invalid-argument-type |ssl|:|Abort| transaction transaction)))))
273
 
274
 
275
 (ssl:defword |ssl|:|Begin| (&key task dataset)
276
   (:documentation "Begin a new transaction for the given dataset in the active thread.
277
    If a transaction is already active, shadow it with the new transaction.
278
    The default dataset is the thread's current dataset.
279
    Returns no value.")
280
   (:values transaction)
281
   (:generic-function-class |ssl|:|Operation|)
282
 
283
   (:method (&key (task *task*) (dataset *repository*))
284
     (ssl::begin-transaction task dataset)))
285
     
286
 
287
 (ssl:defword |ssl|:|Bind| (projection &key dataset method)
288
   (:documentation "Given a task, bind it with an execution plan to the given data set.
289
    For a query, this includes using a particular matching approach and secondary assertions
290
    to compile the query such that the graph patterns effect a particular entailment regime.
291
    (see http://www.w3.org/TR/sparql11-entailment/)
292
    Iff no transaction is active for the given dataset, begin a new one.
293
    The default data set is the thread's current dataset.
294
    The default matching method is the d-entailment logic (<http://www.w3.org/ns/entailment/D>)
295
    implicit in simple quad pattern matching.
296
    The alternative the binding mechanims permits also
297
 
298
    - <http://www.w3.org/ns/entailment/D> : Datatype entailment, which recognizes the numeric class hierarchy
299
    - <urn:dydra:RIFCore> : simple inference based on SPIN definitions in an additional repository ; 
300
      (see http://spinrdf.org/spin.html)
301
 
302
    Returns the bound query.")
303
   (:values task)
304
   (:generic-function-class |ssl|:|Operation|)
305
 
306
   (:method ((task task) &key (dataset (task-revision task) d-s) (method nil))
307
     (when (and d-s (task-revision task))
308
       (assert (eql (task-revision task) dataset) ()
309
               "Task repository incompatible: ~s != ~s" (task-revision task) dataset))
310
     (ssl::bind-task task dataset method)))
311
 
312
 
313
 (ssl:defword |ssl|:|Commit| (&key transaction dataset)
314
   (:documentation "Commit the given transaction (by default, the thread's current transaction).
315
    If either no transaction exists, or the current transactionis no longer active, the operation
316
    signals an error.
317
    Alternatively supply a dataset or a list to arrange that all are committed inone operation.
318
    The default transaction is the thread's current transaction.
319
    Returns the transaction.")
320
   (:values transaction)
321
   (:generic-function-class |ssl|:|Operation|)
322
 
323
   (:method (&key (transaction *transaction* t-s) dataset)
324
     ;; if a dataset or a list is specified, operate on the respective transactions
325
     ;; otherwise on the given transaction or the current one
326
     ;; if none is present or both arguments are supplied, signal an error.
327
     (if dataset
328
         (if (and t-s transaction)
329
             (invalid-argument-type |ssl|:|Commit| dataset null)
330
             (ssl::commit-transaction dataset))
331
         (if transaction
332
             (ssl::commit-transaction transaction)
333
             (invalid-argument-type |ssl|:|Commit| transaction transaction)))))
334
         
335
 
336
 (ssl:defword |ssl|:|Compose| (&code steps)
337
   (:documentation "A generic composition just executes the given steps.
338
    The state values are those of the current thread context.
339
    Returns the result of the last step.")
340
   (:generic-function-class |ssl|:|Composition|)
341
 
342
   (:method (steps)
343
     (ssl::compose steps)))
344
 
345
 
346
 (ssl:defword |ssl|:|Conditional| (value &code consequent alternative)
347
   (:documentation "Given a predicate value and consequent and alternative plans,
348
    continue execution in the current dynamic context with one or the other depending on the give value.
349
    Iff the  value is a logical true (non-zero, non-empty solution field, boolean true, non-empty string, etc),
350
    execute the consequent plan. Otherwise perform the alternative. If the respective clause is <rdf:nil>,
351
    do nothing.
352
    Delegates to ssl:if, which yields the value of the respective plan.")
353
   (:values &rest)
354
 
355
   (:method ((value t) consequent alternative)
356
     (ssl::conditional value
357
       (unless (eq consequent |rdf|:|nil|) consequent)
358
       (unless (eq alternative |rdf|:|nil|) alternative))))
359
 
360
 
361
 (ssl:defword |ssl|:|Constrain| (&code steps &key dataset source name)
362
   (:documentation "A contrain plan is a query which is not intended to encode results as a response,
363
    but rather leave them active for subsequent conditional execution")
364
   (:values solution-field)
365
   (:generic-function-class |ssl|:|Composition|)
366
 
367
   (:method ((steps list) &rest args)
368
     (declare (dynamic-extent args))
369
     (apply #'ssl::constrain steps args)))
370
 
371
 
372
 (ssl:defword |ssl|:|Dataset| (&key location)
373
   (:documentation "Resolve the given location to instantiate a dataset respective the access form
374
    which is implicit in the location.
375
    The default location is the request repository.
376
    Returns the resolved dataset.")
377
   (:values (or iri stream repository))
378
   (:generic-function-class |ssl|:|Operation|)
379
 
380
   (:method (&key (location (ssl::request-repository)))
381
     (ssl::dataset location)))
382
 
383
 
384
 (ssl:defword |ssl|:|Decode| (&key location request-type response-type)
385
   (:documentation "A decode step parses the given location (a string, the resource designated by an IRI,
386
    or the request content) accoding to the given media type to yield a query instance.
387
    The default location is the request content stream.
388
    The default media type is the request content type.
389
    Returns the parsed (but unbound) task/query.")
390
   (:values task)
391
   (:generic-function-class |ssl|:|Operation|)
392
 
393
   (:method (&key (location *request-location*)
394
                  (request-type *request-content-type*)
395
                  (response-type *response-content-type*))
396
     (setq *task*  (decode-task location
397
                                (mime:mime-type request-type)
398
                                (mime:mime-type response-type)))
399
     (setq *query* *task*))
400
 
401
   (:method :after (&key &allow-other-keys)
402
     (generate-accounting-note :parse)))
403
 
404
 
405
 (ssl:defword |ssl|:|Encode| (projection &key location media-type)
406
   (:documentation "An encode step expects the projection result from a project step,
407
    encodes it according to the given media type and delivers it to the given destination location.
408
    The default location is the request response stream.
409
    The default media type is the request accept type.
410
    Returns the projection instance, which would have comsumed and emited its content,
411
    but would still have access to its structure and metadata.
412
 
413
    Handle authorization by delegating the location to a dataset, to control access.")
414
   (:values solution-field)
415
   (:generic-function-class |ssl|:|Operation|)
416
 
417
   (:method (projection &key
418
                        (location (processor-response-content-location (request-processor)))
419
                        (media-type (task-response-content-type *task*)))
420
     "delegate to the internal implementation providing the location, wrapped as a dataset
421
      to enforce authroization constraints, and media type in addition to the projected content.
422
      the default destination location is taken from the request settings as cached in the processor,
423
      while the content type is taken from the immediate task, as each task may be specific."
424
     (ssl::encode projection (ssl::dataset location :operation |acl|:|Write|) (mime:mime-type media-type))
425
     projection))
426
 
427
 
428
 (ssl:defword |ssl|:|End| (&key transaction)
429
   (:documentation "Closes a transaction without either aborting or commiting.
430
    The default transaction is the thread's current transaction.
431
    Returns no value.")
432
   (:values transaction)
433
   (:generic-function-class |ssl|:|Operation|)
434
 
435
   (:method (&key (transaction *transaction*))
436
     (ssl::end-transaction transaction)))
437
 
438
 (ssl:defword |ssl|:|For| (projection &code steps)
439
   (:documentation "Given a projected field, iterate over the solutions, bind the respective variable
440
    and execute the cose sequence for each solution")
441
   (:values t)
442
 
443
   (:method (projection steps)
444
     (ssl::for projection steps)))
445
 
446
 
447
 (ssl:defword |ssl|:|Loop| (&code steps &key name)
448
   (:documentation "Repeat the given code until something returns")
449
   (:values t)
450
 
451
   (:method (steps &rest args)
452
     (declare (dynamic-extent args))
453
     (apply #'ssl::loop steps args)))
454
 
455
 
456
 (ssl:defword |ssl|:|Matcher| (&key method dataset)
457
   (:documentation "Instantiate a graph matcher to integrate into either a bind or a projection step.
458
    The method determine the matching/entailment regime.
459
    As per regime, the dataset can serve as the source for entailment rules.
460
    Yields a matcher instance.")
461
   (:values ssl::matcher)
462
   (:generic-function-class |ssl|:|Operation|)
463
 
464
   (:method (&key (method nil) (dataset nil))
465
     (ssl::graph-matcher method dataset)))
466
   
467
 
468
 
469
 (ssl:defword |ssl|:|Notify| (&code steps &key dataset source destination mode)
470
   (:documentation "An notify step is a specialized query form which encodes the result to a
471
    location other than the response content stream. It also allows options to specify whether
472
    the notification is synchronous and whether errors are siliently suppressed or cause the
473
    primary request to fail
474
    No direct effect on an active transaction upon the step's successful completion.
475
    The default mode is asynchronous
476
    If wait, then execution suspends until the notification completes. If test, it must complete error-free.
477
    Yields the value of the last operation in the plan.")
478
   (:values task)
479
   (:generic-function-class |ssl|:|Composition|)
480
 
481
   (:method ((steps list) &rest args)
482
     (declare (dynamic-extent args))
483
     (apply #'ssl::notify steps args)))
484
 
485
 
486
 (ssl:defword |ssl|:|Project| (projection &key dataset method media-type)
487
   (:documentation "A Project step applies the bound query to the given dataset to generate solutions
488
    which are then cpmbined as per the query algebra and then projected through a select, construct,
489
    describe or ask clause to yield the final result.
490
    The default data set is the thread's current dataset.
491
    The default matching method is the d-entailment logic (<http://www.w3.org/ns/entailment/D>)
492
       Returns a solution/graph field")
493
   (:values solution-field)
494
   (:generic-function-class |ssl|:|Operation|)
495
 
496
   (:method ((projection query) &key
497
             (dataset (task-repository projection))
498
             (method nil)
499
             (media-type (task-response-content-type projection)))
500
     (ssl::project projection dataset method media-type)))
501
 
502
 
503
 (ssl:defword |ssl|:|Query| (&code steps &key dataset name transaction source destination)
504
   (:documentation "A query is a specialized plan form which intends to project the dataset content,
505
    but not modify it. Acts as a plan to establish a dynamic context.
506
    Ends an active transaction - which must be unmodified, upon successful completion.
507
    Returns the value of the last opration in the plan.")
508
   (:values task)
509
   (:generic-function-class |ssl|:|Composition|)
510
 
511
   (:method ((steps list) &rest args)
512
     (declare (dynamic-extent args))
513
     (apply #'ssl::query steps args)))
514
 
515
 
516
 (ssl:defword |ssl|:|Request| (projection &key location request-media-type response-media-type)
517
   (:documentation "An request step expects the projection result from a project step,
518
    encodes it according to the given media type and delivers it to the given destination location.
519
    The usual location is a remote http resource.
520
    Returns the decoded response (if any) or the original solution field,
521
    The combined media types and method are subject to constraints.
522
 
523
    Handle authorization by delegating the location to a dataset, to control access.")
524
   (:values solution-field)
525
   (:generic-function-class |ssl|:|Operation|)
526
 
527
   (:method (projection &key
528
                        (location (error "Request: locationis required"))
529
                        (request-media-type (task-request-content-type *task*))
530
                        (response-media-type (task-response-content-type *task*))
531
                        (method *request-http-method*))
532
     "delegate to the internal implementation providing the location, wrapped as a dataset
533
      to enforce authroization constraints, and media type in addition to the projected content.
534
      the default destination location is taken from the request settings as cached in the processor,
535
      while the content type is taken from the immediate task, as each task may be specific."
536
     (let* ((method-key (http-method-key method))
537
            (access-mode (ecase method-key 
538
                           ((:head :get) |acl|:|Read|)
539
                           ((:put :post :patch) |acl|:|Write|))))
540
       (ssl::request projection (ssl::dataset location :operation access-mode)
541
                     request-media-type
542
                     response-media-type
543
                     :method method-key))))
544
 
545
 
546
 (ssl:defword |ssl|:|Return| (&key name value)
547
   (:documentation "Return from the dynamic context identified by the tag.
548
    If no tag is provided, return from the immediately enclosing block")
549
   (:generic-function-class |ssl|:|Operation|)
550
   (:method (&key (name nil) (value nil))
551
     (invoke-restart name value)))
552
 
553
 (ssl:defword |ssl|:|Task| (&code steps &key name)
554
   (:documentation "A task serves as request container for other compositions")
555
   (:values )
556
   (:generic-function-class |ssl|:|Composition|)
557
 
558
   (:method ((steps list) &rest args)
559
     (declare (dynamic-extent args))
560
     (apply #'ssl::query steps args)))
561
 
562
 
563
 (ssl:defword |ssl|:|Update| (&code steps &key dataset destination mode name source transaction)
564
   (:documentation "An update is a specialized plan form which intends to project the dataset content to
565
    modifiy its content,
566
    unless :mode is :continue, it commits an active transaction - even if unmodified,
567
    upon successful completion.
568
    Returns the value of the last opration in the plan.")
569
   (:values task)
570
   (:generic-function-class |ssl|:|Composition|)
571
 
572
   (:method ((steps list) &rest args)
573
     (declare (dynamic-extent args))
574
     (apply #'ssl::update steps args)))