Coverage report: /development/source/library/org/datagraph/spocq-shard/src/core/introspection.lisp
| Kind | Covered | All | % |
| expression | 0 | 1241 | 0.0 |
| branch | 0 | 74 | 0.0 |
Key
Not instrumented
Conditionalized out
Executed
Not executed
Both branches taken
One branch taken
Neither branch taken
1
;;;; -*- Mode: Lisp; Syntax: Common-Lisp; Package: org.datagraph.spocq.implementation; -*-
3
(in-package :org.datagraph.spocq.implementation)
5
(:documentation "This file defines an introspection functions for the 'org.datagraph.spocq' RDF engine."
8
"Copyright 2021 [james anderson](mailto:james.anderson@setf.de).")
11
"The compute-instance-model function generates a model for the various service entities
16
The covering-pattern-sets and pattern-expression functions combine patterns with
17
predicate constraints to compute the required patterns an the query which they
21
;;; externalize instance models for service elements
23
(defclass pattern-class ()
25
:initarg :type :initform nil :accessor pattern-type)
27
:initarg :predicates :initform nil :accessor pattern-predicates)
29
:initarg :subject-signature :initform nil :accessor pattern-subject-signature)
31
:initarg :object-signature :initform nil :accessor pattern-object-signature)
33
:initarg :superclasses :initform nil :accessor pattern-superclasses)
35
:initarg :subclasses :initform nil :accessor pattern-subclasses)
37
:initarg :count :initform 0 :accessor pattern-class-count)))
39
(defmethod print-object ((object pattern-class) stream)
40
(print-unreadable-object (object stream :identity t :type t)
41
(format stream "~b/~b ~@[(~a)~]x~a (~{~b~^ ~}) [~{~a~^ ~}]"
42
(pattern-subject-signature object)
43
(pattern-object-signature object)
45
(pattern-class-count object)
46
(mapcar #'pattern-subject-signature (pattern-superclasses object))
47
(pattern-predicates object))))
49
(defun make-pattern-class (&rest args)
50
(apply #'make-instance 'pattern-class args))
52
(defclass pattern-path ()
54
:initarg :object-predicate :initform (error "object-predicate is required") :accessor pattern-path-object-predicate)
56
:initarg :subject-predicate :initform (error "subject-predicate is required") :accessor pattern-path-subject-predicate)
58
:initarg :count :initform 0
59
:accessor pattern-path-count)))
61
(defmethod print-object ((object pattern-path) stream)
62
(print-unreadable-object (object stream :identity t :type t)
63
(format stream "~a/~a"
64
(pattern-path-object-predicate object)
65
(pattern-path-subject-predicate object))))
67
(defun make-pattern-path (&rest args)
68
(apply #'make-instance 'pattern-path args))
71
(defgeneric view-construct-patterns (view)
73
"Compute those of the patterns from the view's repository which provide the
74
most coverage to the templates of the view's construct clause.
75
Return a list of pattern instances, each of which provide the most coverage
76
for at least one of the sets of predicates.")
77
(:method ((view view))
78
(when (construct-form-p (view-sse-expression view))
79
(let* ((repository (view-repository view))
80
(patterns (repository-pattern-classes repository))
81
(predicate-set (mapcar #'statement-predicate (third (view-sse-expression view))))
82
(ordered-patterns (sort (copy-list patterns) #'>
83
:key #'(lambda (pattern) (length (intersection predicate-set (pattern-predicates pattern)))))))
84
(when ordered-patterns
85
(list (first ordered-patterns)))))))
87
(defgeneric view-modify-patterns (view)
89
"Compute those of the patterns from the view's repository which provide the
90
most coverage to the templates of the view's modify clause.
91
Return a list of pattern instances, each of which provide the most coverage
92
for at least one of the sets of predicates.")
93
(:method ((view view))
94
(when (update-form-p (view-sse-expression view))
95
(let* ((repository (view-repository view))
96
(patterns (repository-pattern-classes repository))
97
(predicate-sets (loop for form in (rest (view-sse-expression view))
98
when (modify-form-p form)
99
collect (let* ((template (first (getf (cddr form) :insert)))
100
(triples (remove-if-not #'triple-form-p template))
101
(graphs (remove-if-not #'graph-form-p template)))
102
(append (when triples (list (mapcar #'statement-predicate triples)))
103
(loop for graph in graphs
104
collect (mapcar #'statement-predicate (third graph))))))))
106
(loop for predicate-set in predicate-sets
107
for pattern = (first (sort (copy-list patterns) #'>
108
:key #'(lambda (pattern) (length (intersection predicate-set (pattern-predicates pattern))))))
109
when pattern collect pattern))))))
111
(defgeneric view-where-patterns (view)
113
"Compute a list of the respective least repository which covers each bgp instances.")
114
(:method ((view view))
115
(let* ((repository (view-repository view))
116
(patterns (repository-pattern-classes repository))
117
(view-patterns (expression-pattern-classes (view-sse-expression view))))
119
(loop for view-pattern in view-patterns
120
for covering-patterns = (remove-if-not #'(lambda (pattern)
121
(every #'(lambda (predicate) (find predicate (pattern-predicates pattern)))
124
for pattern = (when covering-patterns (reduce #'(lambda (p1 p2) (if (< (length (pattern-predicates p1)) (length (pattern-predicates p2))) p1 p2))
126
when pattern collect pattern)))))
128
(defgeneric view-where-attributes (view)
130
"Compute the list of where dimensions from the (should be single) select clause.")
131
(:method ((view view))
132
(let ((form (view-sse-expression view)))
133
(when (and (construct-form-p form)
134
(> (length (expression-table-forms (second form))) 0))
135
(let ((selects (expression-select-forms form)))
136
(unless (= (length selects) 1)
137
(warn "multiple select forms: ~a" view))
138
(let* ((select (first selects))
139
(projection (third select)))
140
(loop until (not (keywordp (first projection)))
141
do (setf projection (cddr projection)))
142
(loop for attribute in projection
143
collect (if (consp attribute) (first attribute) attribute))))))))
146
(defun classify-star-instances (node-signatures ordinal-predicates)
147
(let ((subject-patterns (make-hash-table :test 'eql))
148
(type-patterns (make-hash-table :test 'eql)))
149
(flet ((note-class (instance)
150
(destructuring-bind (type subject-signature object-signature) instance
151
(declare (ignore object-signature))
152
(when type (setf (gethash type type-patterns) instance)) ;; cache it although not used
153
(setf (gethash subject-signature subject-patterns) instance))))
154
(loop for instance being each hash-value of node-signatures
155
do (note-class instance)))
156
(let ((objects (make-hash-table :test 'eql)))
157
(flet ((get-object-term (term-number)
158
(or (gethash term-number objects)
159
(setf (gethash term-number objects) (term-number-object term-number))))
160
(signature-superclass (super? sub?)
161
(= (logand (pattern-subject-signature super?) (pattern-subject-signature sub?)) (pattern-subject-signature super?))))
163
(loop for (type subject-signature object-signature) being each hash-value of subject-patterns
164
collect (make-pattern-class
165
:type (when type (get-object-term type))
166
:predicates (reverse (loop with length = (integer-length subject-signature)
167
for ordinal below length
168
for present = (= 1 (ldb (byte 1 ordinal) subject-signature))
169
when present collect (get-object-term (gethash ordinal ordinal-predicates))))
170
:subject-signature subject-signature
171
:object-signature object-signature))))
172
(setf classes (sort classes #'(lambda (c1 c2)
173
(let ((s1 (pattern-subject-signature c1))
174
(s2 (pattern-subject-signature c2)))
175
(> (logcount s1) (logcount s2))))))
176
(loop for (sub? . rest) on classes
177
for sub-signature = (pattern-subject-signature sub?)
178
for supers = (remove-if #'(lambda (super?)
179
(loop for super-sub? in (ldiff rest (member super? rest))
180
when (and (signature-superclass super? super-sub?)
181
(signature-superclass super-sub? sub?))
183
(remove-if-not #'(lambda (super?)
184
(signature-superclass super? sub?))
186
do (setf (pattern-superclasses sub?) supers)
187
do (loop for super in supers
188
do (push sub? (pattern-subclasses super))
189
do (setf (pattern-object-signature super) (logior (pattern-object-signature super) (pattern-object-signature sub?)))))
196
(defparameter *repository-pattern-classes.verbose* nil)
198
(defparameter *repository-graph-pattern-query*
199
"select ?protoS ?p ?o
201
{ select distinct ?protoS
203
select (sample (?s) as ?protoS)
210
"generate exemplary star sub-graphs for a repository:
211
- enumerate predicates.
212
- collect one subject respective each predicate.
213
- generate the full description for each subject.
214
nb. no comments allowed, as it is sent as a single line.")
216
(defgeneric repository-pattern-classes (repository)
218
"Compute the tree of patterns present in the repository based on the star graphs connected
220
(:method ((designator string))
221
(repository-pattern-classes (repository designator)))
223
(:method ((repository repository))
224
(let ((instances (make-hash-table :test 'eql))
225
(node-patterns (make-hash-table :test 'eql))
226
(node-counts (make-hash-table :test 'eql))
227
(type-patterns (make-hash-table :test 'eql))
228
(predicate-ordinals (make-hash-table :test 'eql))
229
(ordinal-predicates (make-hash-table :test 'eql))
230
(type-predicate (object-term-number |rdf|:|type|))
231
(cached (gethash :classes (repository-library-cache repository))))
232
(when cached (return-from repository-pattern-classes cached))
233
;; enumerate the subject and object combinations
234
(flet ((note-instance (%quad)
235
(let* ((as-subject (or (gethash (%quad-subject %quad) instances)
236
(setf (gethash (%quad-subject %quad) instances)
238
(as-object (or (gethash (%quad-object %quad) instances)
239
(setf (gethash (%quad-object %quad) instances)
241
(predicate (%quad-predicate %quad)))
242
(cond ((eql predicate type-predicate)
243
(setf (first as-subject) (%quad-object %quad))
244
(setf (first as-object) (%quad-object %quad)))
246
(let ((predicate-ordinal (or (gethash (%quad-predicate %quad) predicate-ordinals)
247
(setf (gethash (hash-table-count predicate-ordinals) ordinal-predicates)
248
(%quad-predicate %quad)
249
(gethash (%quad-predicate %quad) predicate-ordinals)
250
(hash-table-count predicate-ordinals)))))
251
(setf (ldb (byte 1 predicate-ordinal) (second as-subject)) 1
252
(ldb (byte 1 predicate-ordinal) (third as-object)) 1)))))))
253
(rlmdb:map-repository-statements #'note-instance repository #(0 0 0 0)))
255
(when *repository-pattern-classes.verbose*
256
(loop for predicate being each hash-value of ordinal-predicates using (hash-key ordinal)
257
do (print (list ordinal predicate (term-number-object predicate)))))
258
;;;!!! this should be revised to use classify-star-instances
259
(flet ((note-class (node-signatures)
260
(destructuring-bind (type subject-signature object-signature) node-signatures
261
(declare (ignore object-signature))
262
(when type (setf (gethash type type-patterns) node-signatures)) ;; cache it although not used
263
(setf (gethash subject-signature node-patterns) node-signatures)
264
(incf (gethash subject-signature node-counts 0)))))
265
(loop for instance being each hash-value of instances
266
do (note-class instance)))
267
(let ((objects (make-hash-table :test 'eql)))
268
(flet ((get-object-term (term-number)
269
(or (gethash term-number objects)
270
(setf (gethash term-number objects) (term-number-object term-number))))
271
(signature-superclass (super? sub?)
272
(= (logand (pattern-subject-signature super?) (pattern-subject-signature sub?)) (pattern-subject-signature super?))))
274
(loop for (type subject-signature object-signature) being each hash-value of node-patterns
275
for predicates = (reverse (loop with length = (integer-length subject-signature)
276
for ordinal below length
277
for present = (= 1 (ldb (byte 1 ordinal) subject-signature))
278
when present collect (get-object-term (gethash ordinal ordinal-predicates))))
279
for count = (gethash subject-signature node-counts)
280
collect (make-pattern-class
281
:type (when type (get-object-term type))
282
:predicates predicates
283
:subject-signature subject-signature
284
:object-signature object-signature
286
(setf classes (sort classes #'(lambda (c1 c2)
287
(let ((s1 (pattern-subject-signature c1))
288
(s2 (pattern-subject-signature c2)))
289
(> (logcount s1) (logcount s2))))))
290
(loop for (sub? . rest) on classes
291
for sub-signature = (pattern-subject-signature sub?)
292
for supers = (remove-if #'(lambda (super?)
293
(loop for super-sub? in (ldiff rest (member super? rest))
294
when (and (signature-superclass super? super-sub?)
295
(signature-superclass super-sub? sub?))
297
(remove-if-not #'(lambda (super?)
298
(signature-superclass super? sub?))
300
do (setf (pattern-superclasses sub?) supers)
301
do (loop for super in supers
302
do (push sub? (pattern-subclasses super))
303
do (setf (pattern-object-signature super) (logior (pattern-object-signature super) (pattern-object-signature sub?)))))
304
(setf (gethash :classes (repository-library-cache repository))
307
(:method ((repository service-repository))
308
"Given a service repository, rather than scan the entire repository in
309
order to enumerate the star graph instances, extract exemplary subgraphs and
310
base the inventory on them."
312
(let ((instances (make-hash-table :test 'eql))
313
(predicate-ordinals (make-hash-table :test 'eql))
314
(ordinal-predicates (make-hash-table :test 'eql))
315
(type-predicate (object-term-number |rdf|:|type|))
316
(cached (gethash :classes (repository-library-cache repository))))
317
(when cached (return-from repository-pattern-classes cached))
318
(flet ((note-instance (s p o)
319
(let ((%subject (object-term-number s))
320
(%predicate (object-term-number p))
321
(%object (object-term-number o)))
322
(let* ((as-subject (or (gethash %subject instances)
323
(setf (gethash %subject instances)
325
;; given that the query sample subjects, this will not likely yield anything
326
(as-object (or (gethash %object instances)
327
(setf (gethash %object instances) (list nil 0 0)))))
328
(cond ((eql %predicate type-predicate)
329
(setf (first as-subject) %object)
330
(setf (first as-object) %object))
332
(let ((predicate-ordinal (or (gethash %predicate predicate-ordinals)
333
(setf (gethash (hash-table-count predicate-ordinals) ordinal-predicates)
335
(gethash %predicate predicate-ordinals)
336
(hash-table-count predicate-ordinals)))))
337
(setf (ldb (byte 1 predicate-ordinal) (second as-subject)) 1
338
(ldb (byte 1 predicate-ordinal) (third as-object)) 1))))))))
339
(with-open-repository ("system/null" :read-only-p t :normal-disposition :abort)
340
(map-external-service-response #'note-instance repository *repository-graph-pattern-query*)))
341
(when *repository-pattern-classes.verbose*
342
(loop for predicate being each hash-value of ordinal-predicates using (hash-key ordinal)
343
do (print (list ordinal predicate (term-number-object predicate)))))
344
(setf (gethash :classes (repository-library-cache repository))
345
(classify-star-instances instances ordinal-predicates)))))
347
;;; (repository-pattern-classes (service-repository "https://www.dydra.com/test/test/sparql") )
348
;;; (repository-pattern-classes (service-repository "http://edan.si.edu/saam/sparql") )
350
curl "-L" "-X" "POST" "-D" "-" "-k" "--silent" --data-binary @- \
351
"-H" "Accept: text/csv,application/sparql-results+json" \
352
"-H" "Content-Type: application/sparql-query" \
353
http://edan.si.edu/saam/sparql <<EOF
356
{ select distinct ?protoS
358
select (sample (?s) as ?protoS)
369
(defgeneric repository-pattern-paths (repository)
371
"Compute the predicate pairs which share respective object/subject nodes.
372
This differs from the intermediate data gathered to compute the classes in that
373
those signatures record all possible predicates per subject/object rather than
374
the respective specific predicate for a specific subject/object.")
375
(:method ((designator string))
376
(repository-pattern-paths (repository designator)))
377
(:method ((repository repository))
378
(let ((linkages (make-hash-table :test 'equal))
379
(statements (make-hash-table :test 'eql))
380
(terms (make-hash-table :test 'eql))
381
(cached (gethash :pattern-paths (repository-library-cache repository))))
382
(when cached (return-from repository-pattern-paths cached))
383
(flet ((collect-statements (%quad)
384
(let ((statement (list (%quad-subject %quad)
385
(%quad-predicate %quad)
386
(%quad-object %quad))))
387
(push statement (gethash (%quad-subject %quad) statements))))
388
(get-predicate-term (term-number)
389
(or (gethash term-number terms)
390
(setf (gethash term-number terms) (term-number-object term-number)))))
391
(rlmdb:map-repository-statements #'collect-statements repository #(0 0 0 0))
392
(loop for by-subject-statements being each hash-value of statements
393
do (loop for by-subject in by-subject-statements
394
for via-object-statements = (gethash (third by-subject) statements)
395
do (loop for via-object in via-object-statements
396
do (incf (gethash (list (second by-subject) (second via-object))
400
(setf (gethash :pattern-paths (repository-library-cache repository))
401
(loop for (object-predicate subject-predicate) being each hash-key of linkages
402
using (hash-value count)
403
collect (make-pattern-path :object-predicate (get-predicate-term object-predicate)
404
:subject-predicate (get-predicate-term subject-predicate)
406
(hash-table-count statements)
407
(loop for statement-list being each hash-value of statements
408
sum (length statement-list)))))))
411
(defgeneric repository-referenced-pattern-classes (repository &key abstract)
412
(:documentation "Return the repository graph patterns which are
413
referenced from some view as a where pattern.")
415
(:method ((repository repository) &key (abstract nil))
416
(let* ((patterns (repository-pattern-classes repository))
417
(views (repository-view-definitions repository))
418
(referenced-graph-patterns (make-hash-table :test 'equal))
419
(where-patterns (make-hash-table :test 'equal)))
420
;; note referencing patterns
421
(loop for view in views
422
do (loop for pattern in (view-where-patterns view)
423
for signature = (pattern-subject-signature pattern)
424
do (setf (gethash signature where-patterns) pattern)))
425
;; note referenced patterns
426
(loop for pattern in patterns
427
for signature = (pattern-subject-signature pattern)
428
when (gethash signature where-patterns)
429
do (setf (gethash signature referenced-graph-patterns) pattern))
431
;; include abstractions - everything where the signature is subsumed
432
(loop for pattern in patterns
433
for signature = (pattern-subject-signature pattern)
434
when (loop for referenced-pattern being each hash-value of referenced-graph-patterns
435
#|do (format *trace-output* "~&test: ~b . ~b = ~b"
437
(pattern-subject-signature referenced-pattern)
438
(logand signature (pattern-subject-signature referenced-pattern)))|#
439
when (= signature (logand signature (pattern-subject-signature referenced-pattern)))
441
do (setf (gethash signature referenced-graph-patterns) pattern)))
442
(loop for pattern being each hash-value of referenced-graph-patterns
445
(defgeneric repository-predicates (repository)
447
"Compute the predicate pairs which share respective object/subject nodes.
448
This differs from the intermediate data gathered to compute the classes in that
449
those signatures record all possible predicates per subject/object rather than
450
the respective specific predicate for a specific subject/object.")
451
(:method ((designator string))
452
(repository-pattern-paths (repository designator)))
453
(:method ((repository repository))
454
(let ((predicates ())
455
(cached (gethash :predicates (repository-library-cache repository))))
456
(flet ((collect-predicates (term-number)
457
(push (term-number-object term-number) predicates)))
458
(declare (dynamic-extent #'collect-predicates))
459
(cond (cached cached)
461
(map-repository-predicates #'collect-predicates repository :distinct t)
462
(setf (gethash :predicates (repository-library-cache repository)) predicates)))))))
464
(defgeneric compute-instance-model (resource as &key predicates views)
465
(:method ((account account) (as mime:application/json) &rest args)
466
"Generate a model for an account to include the repositories, their views and classes and the classes constituent predicates.
467
Only the repositories are included immediately.
468
The other entities appear in the respective repository."
469
(declare (ignore args))
470
(let* ((repositories (account-repositories account))
471
;; (views (loop for repository in repositories append (spocq.i::repository-view-definitions repository)))
472
(repository-models (map 'vector #'(lambda (repository)
473
(let ((model (compute-instance-model repository as)))
474
(loop for entry in model
475
for (name . value) = entry
477
collect `(name . ,(map 'vector #'(lambda (v) (rest (assoc 'name v))) value))
478
else collect entry)))
480
`(("@type" . "account")
481
("name" . ,(account-name account))
482
("location" . ,(resource-uri account))
483
("identifier" . ,(instance-identifier account))
484
("repositories" . ,repository-models))))
486
(:method ((repository repository-revision) (as t) &rest args)
487
(apply #'compute-instance-model (repository-revision-reference repository) as args))
489
(:method ((repository repository) (as mime:application/json) &key predicates views)
490
"Generate a model for arepository to include the views and classes and the classes constituent predicates"
491
(flet ((view-predicates (view)
492
(let ((patterns (append (view-construct-patterns view) (view-modify-patterns view) (view-where-patterns view))))
493
;; duplicates do not matter for the use
494
(reduce #'append patterns :key #'pattern-predicates))))
495
;; (when (and views (not predicates)) (setf predicates (reduce #'append views :key #'view-predicates)))
496
(let* ((all-views (remove-if-not #'(lambda (view)
498
(find (view-name view) views :test #'string-equal :key #'view-name)
499
(or (null predicates)
500
(some #'(lambda (predicate) (find (iri-lexical-form predicate) predicates :test #'string-equal
501
:key #'iri-lexical-form))
502
(view-predicates view)))))
503
(spocq.i::repository-view-definitions repository)))
504
(update-views (remove-if-not #'(lambda (view)
505
(let ((form (view-sse-expression view)))
506
(update-form-p form)))
508
(post-views (remove-if-not #'(lambda (view)
509
(let ((form (view-sse-expression view)))
510
(and (construct-form-p form)
511
(> (length (expression-table-forms (second form))) 0))))
513
(get-views (set-difference all-views (append post-views update-views)))
514
(patterns (remove-if-not #'(lambda (pattern)
515
(or (null predicates)
516
(some #'(lambda (predicate) (find (iri-lexical-form predicate) predicates :test #'string-equal
517
:key #'iri-lexical-form))
518
(pattern-predicates pattern))))
520
(remove-if #'pattern-subclasses (repository-pattern-classes repository))
521
(repository-referenced-pattern-classes repository))))
522
(paths (remove-if-not #'(lambda (pattern-path)
523
(or (null predicates)
524
(find (iri-lexical-form (pattern-path-object-predicate pattern-path)) predicates :test #'string-equal
525
:key #'iri-lexical-form)
526
(find (iri-lexical-form (pattern-path-subject-predicate pattern-path)) predicates :test #'string-equal
527
:key #'iri-lexical-form)))
528
(repository-pattern-paths repository))))
529
;; (print (list (repository-name repository) update-views post-views get-views))
530
`(("@type" . "repository")
531
("name" . ,(repository-name repository))
532
("location" . ,(resource-uri repository))
533
("identifier" . ,(instance-identifier repository))
534
("getViews" . ,(map 'vector #'(lambda (view) (compute-instance-model view as)) get-views))
535
("updateViews" . ,(map 'vector #'(lambda (view) (compute-instance-model view as)) update-views))
536
("postViews" . ,(map 'vector #'(lambda (view) (compute-instance-model view as)) post-views))
537
("patterns" . ,(map 'vector #'(lambda (pattern) (compute-instance-model pattern as)) patterns))
538
("description" . ,(repository-description repository))
539
("predicates" . ,(map 'vector #'identity (repository-predicates repository)))
540
("paths" . ,(remove-duplicates
541
(map 'vector #'(lambda (pattern-path) (vector (pattern-path-object-predicate pattern-path)
542
(pattern-path-subject-predicate pattern-path)))
546
#+(or) ;; this yields a graph from which the relations are mediated by the predicates
547
(:method ((repository repository) (as mime:image/vnd.dydra.sparql-results+circos+svg+xml) &rest args)
548
(declare (ignore args))
549
(let ((model (compute-instance-model repository mime:application/json)))
550
(let ((identifier (json-member-value model "identifier"))
551
(views (concatenate 'vector (json-member-value model "getViews")
552
(json-member-value model "updateViews")
553
(json-member-value model "postViews")))
554
(patterns (json-member-value model "patterns")))
555
(append (loop for pattern across patterns
556
collect `(,identifier <urn:dydra:pattern> ,(json-member-value pattern "signature")))
557
(loop for view-model across views
558
for view-identifier = (json-member-value view-model "identifier")
559
for patterns = (json-member-value view-model "wherePatterns")
560
append (cons `(,identifier <urn:dydra:view> ,view-identifier)
561
(loop for pattern across patterns
562
collect `(,view-identifier <urn:dydra:pattern> ,(json-member-value pattern "signature")))))))))
564
(:method ((repository repository) (as mime:image/vnd.dydra.sparql-results+circos+svg+xml) &rest args)
565
"Generate the repository model and transform it into a query result suitable for circos table processing.
566
Use the profile sp|:|Construct| or sp|:|Select| (the default) to specify the dimensions to determine
567
wheter the predicate appears in the rendering"
568
(labels ((signature-iri (signature)
570
(intern-iri (format nil "urn:dydra:signature:~a" signature))))
574
(intern-iri (concatenate 'string (iri-local-part p1) "..." (iri-local-part p2))))
575
(pattern-identifier (pattern)
576
(or (type-iri (json-member-value pattern "type"))
577
(signature-iri (json-member-value pattern "signature"))
579
(let* ((model (apply #'compute-instance-model repository mime:application/json args))
580
(repository-identifier (json-member-value model "identifier"))
581
(views (concatenate 'vector (json-member-value model "getViews")
582
(json-member-value model "updateViews")
583
(json-member-value model "postViews")))
584
(patterns (json-member-value model "patterns"))
585
(paths (json-member-value model "paths"))
586
;; choose between select and construct
587
(as-graph (mime-type-profile-p as |sp|:|Construct|)))
588
(cond ((> (length views) 0) (print :for-views)
589
(cons (if as-graph *construct-dimensions* '(?::reference ?::predicate ?repository))
590
(append (loop for pattern across patterns
591
for pattern-identifier = (pattern-identifier pattern)
592
;; this could use rdf:nil as the counterpart
593
collect `(,repository-identifier ,pattern-identifier ,repository-identifier))
594
(loop for view-model across views
595
for view-identifier = (json-member-value view-model "identifier")
596
for patterns = (json-member-value view-model "wherePatterns")
597
append (loop for pattern across patterns
598
for pattern-identifier = (pattern-identifier pattern)
599
collect `(,view-identifier ,pattern-identifier ,repository-identifier))))))
601
(let ((predicate-pattern-map (make-hash-table))
602
(pattern-join-map (make-hash-table :test #'equal)))
603
;; (print patterns) (print paths)
604
(loop for pattern across patterns
605
for pattern-identifier = (pattern-identifier pattern) ;; do (print pattern)
606
do (loop for predicate across (json-member-value pattern "predicates")
607
do (push pattern-identifier (gethash predicate predicate-pattern-map))))
608
(loop for path across paths
609
for p1 = (elt path 0)
610
for p2 = (elt path 1)
611
do (loop for pattern-identifier1 in (gethash p1 predicate-pattern-map)
612
do (loop for pattern-identifier2 in (gethash p2 predicate-pattern-map)
613
do (setf (gethash (list pattern-identifier1 pattern-identifier2) pattern-join-map)
615
(cons (if as-graph *construct-dimensions* '(?::signature1 ?::predicate ?::signature2))
616
(loop for (pattern-identifier1 pattern-identifier2) being each hash-key of pattern-join-map
617
using (hash-value path)
619
collect (list pattern-identifier1
621
pattern-identifier2)))))))))
623
(:method ((view view) (as mime:application/json) &rest args)
624
(declare (ignore args))
625
(let ((expression (view-sse-expression view))
626
(header-attributes (view-where-attributes view)))
628
("name" . ,(view-name view))
629
("location" . ,(resource-uri view))
630
("identifier" . ,(view-identifier view))
631
("constructPatterns" . ,(map 'vector #'(lambda (p) (compute-instance-model p as)) (remove-duplicates (view-construct-patterns view) :test #'eql)))
632
("modifyPatterns" . ,(map 'vector #'(lambda (p) (compute-instance-model p as)) (remove-duplicates (view-modify-patterns view) :test #'eql)))
633
("wherePatterns" . ,(map 'vector #'(lambda (p) (compute-instance-model p as)) (remove-duplicates (view-where-patterns view) :test #'eql)))
634
,@(when header-attributes `(("whereHeader" . (("@type" . "header") ("attributes" . ,(map 'vector #'string header-attributes))))))
635
("predicates" . ,(map 'vector #'iri-lexical-form
636
(remove-if-not #'iri-p
638
(map 'vector #'statement-predicate (expression-pattern-statements expression))))))
639
("references" . ,(let ((services (view-service-references view))
640
(views (view-view-references view)))
641
;; services are either the external url of a service or the id of a repository
642
;; views are the three-part view identifiers
643
`(("services" . ,(map 'vector #'repository-external-name services))
644
("views" . ,(map 'vector #'(lambda (view) (concatenate 'string (repository-id (view-repository view)) "/" (view-name view)))
646
("description" . ,(sparql-query-description view)))))
648
(:method ((class pattern-class) (as mime:application/json) &rest args)
649
(declare (ignore args))
650
`(("@type" . "pattern")
651
,@(when (pattern-type class) `(("type" . ,(pattern-type class))))
652
("predicates" . ,(map 'vector #'identity (pattern-predicates class)))
653
;;,@(when (pattern-subject-signature class) `(("signature" . ,(format nil "~b" (pattern-subject-signature class)))))
654
;;,@(when (pattern-object-signature class) `(("paths" . ,(format nil "~b" (pattern-object-signature class)))))
655
,@(when (pattern-subject-signature class) `(("signature" . ,(pattern-subject-signature class))))
656
,@(when (pattern-object-signature class) `(("paths" . ,(pattern-object-signature class))))
657
#+(or) ("subclasses" . ,(map 'vector #'(lambda (sc) (compute-instance-model sc as)) (pattern-subclasses class)))
658
("subclasses" . ,(map 'vector #'(lambda (sc) (or (pattern-subject-signature sc) -1)) (pattern-subclasses class))))))
662
(defstruct (pattern-node (:conc-name node-))
669
(defmethod node-predicates ((node pattern-node))
670
(pattern-predicates (node-pattern node)))
671
(defmethod node-count ((node pattern-node))
672
(pattern-class-count (node-pattern node)))
673
(defmethod node-object-signature ((node pattern-node))
674
(pattern-object-signature (node-pattern node)))
675
(defmethod node-subject-signature ((node pattern-node))
676
(pattern-subject-signature (node-pattern node)))
677
(defun ensure-node-subject (node)
678
(or (node-subject node)
679
(setf (node-subject node) (make-variable (string (gensym "pattern"))))))
681
(defstruct (path-link (:conc-name link-))
684
(defmethod link-value ((link path-link))
685
(pattern-path-count (link-path link)))
686
(defmethod link-object-predicate ((link path-link))
687
(pattern-path-object-predicate (link-path link)))
688
(defmethod link-subject-predicate ((link path-link))
689
(pattern-path-subject-predicate (link-path link)))
692
(defun compute-combinations (sets continuation &optional list)
694
(loop for member in (first sets)
695
do (compute-combinations (rest sets) continuation (cons member list)))
696
(funcall continuation (reverse list))))
700
(defmethod compute-pattern-expression (pattern-lists iri-attributes)
701
;; compute equivalent variables
702
;; - use the attribute name directly
703
;; - generate non-distinguished variables for join nodes
704
;; compute the covering graph
705
;; compute connected paths which incorporate all patterns
706
;; translate that into sparql
707
;; (print iri-attributes)
708
(let ((all-patterns (remove-duplicates (reduce #'append pattern-lists))))
709
(labels ((predicate-variable (predicate)
710
(make-variable (rest (assoc predicate iri-attributes))))
711
(compute-join (form-list)
712
(reduce #'(lambda (form1 form2) `(spocq.a:|join| ,form1 ,form2))
714
(compute-union (form-list)
715
(reduce #'(lambda (form1 form2) `(spocq.a:|union| ,form1 ,form2))
717
(compute-pattern-form (form)
718
(let ((subject (ensure-node-subject form)))
719
;; (let ((*print-circle* t) (*print-level* 4))(print (list subject (node-predicates form) (node-object-links form))))
720
;; first collect the specified predicates.
721
;; consider them datatype properties which constitute a natural join
722
;; join them with a union of all link (object) predicates.
723
(let* ((datatype-predicates (loop for predicate in (node-predicates form)
724
when (assoc predicate iri-attributes)
726
(object-links (loop for predicate in (node-predicates form)
727
for link = (find-if #'(lambda (link)
728
(and (not (eq form (link-destination link))) ;; suppress reflexive paths
729
(eq predicate (link-object-predicate link))
730
(member (link-destination link) all-patterns)))
731
(node-object-links form))
732
when link collect link))
733
(datatype-statement-patterns (loop for predicate in datatype-predicates
734
unless (find predicate object-links :key #'link-object-predicate)
735
collect `(spocq.a:|triple| ,subject
737
,(predicate-variable predicate))))
738
(object-statement-patterns (loop for link in object-links
739
collect `(spocq.a:|bgp|
740
(spocq.a:|triple| ,subject
741
,(link-object-predicate link)
742
,(ensure-node-subject (link-destination link))))))
743
(base (if datatype-statement-patterns
744
(cons 'spocq.a:|bgp| datatype-statement-patterns)
745
'(spocq.a:|table| spocq.a:|unit|))))
746
(if object-statement-patterns
749
,(reduce #'(lambda (form1 form2) `(spocq.a:|union| ,form1 ,form2))
750
object-statement-patterns))
755
(cons (mapcar #'typed x))
757
(print (typed pattern-lists)))
758
(compute-join (loop for pattern-list in pattern-lists
759
when pattern-list ;; skip empty intermediates
760
collect (compute-union (loop for pattern in pattern-list
761
;; do (print (list :in-pattern-list (type-of pattern)))
762
collect (compute-pattern-form pattern))))))))
765
for saam, :predicate-pattern-sets, the straight combination yields unwieldy intermediates
766
(reduce #'* '(1 1 1 1 1 2 1 2 1 1 1 1 1 1 1 1 1 2 2 1 2 2 4 2 3 2 3 2 3 3 3 5 2 1 1 1 1 2 2
770
(defun compute-covering-pattern-sets (patterns paths predicates &aux (*print-circle* t))
772
- build the adjacency graph based on object.subject links
773
- iterate over all patterns
774
- for each one which includes some of the given predicates, add a node to the graph
775
- collate each node according to the predicates which it covers
776
- associate with each one the paths for which it is either the subject or the object
777
- arrange sets of nodes by covered predicates
778
- take the node sets of the covered predicate cache
779
- order the group by aggregate cardinality
780
- enumerate traversals between groupd nodes
782
return a list of node sets.
783
the intent is that the list be joined into unions of each se
785
;;(print (list (length patterns) (length paths) predicates))
786
(let* ((predicate-patterns (make-hash-table))
787
(covering-patterns (make-hash-table :test 'equal))
788
(nodes (loop for pattern in patterns
789
for covered-predicates = (sort (intersection predicates (pattern-predicates pattern))
791
;; need to include intermediates which cover nothing - when covered-predicates
792
collect (make-pattern-node :pattern pattern
793
:covered-predicates covered-predicates))))
794
;; collate patterns according to the predicates which they include
795
(loop for node in nodes
797
(loop for predicate in (node-predicates node)
798
unless (eq predicate |rdf|:|type|)
799
do (push node (gethash predicate predicate-patterns)))
800
(when (node-covered-predicates node)
801
(push node (gethash (node-covered-predicates node) covering-patterns)))))
803
(print (list predicate-patterns covering-patterns))
804
(print (list :predicate-pattern-sets (loop for pattern-set being each hash-value of predicate-patterns
805
collect (length pattern-set))))
806
(print (list :covering-pattern-sets (loop for pattern-set being each hash-value of covering-patterns
807
using (hash-key predicates)
808
collect (cons (length pattern-set) predicates)))) )
810
(loop for path in paths
811
for subject-predicate = (pattern-path-subject-predicate path)
812
for object-predicate = (pattern-path-object-predicate path)
813
for nodes-with-object = (gethash object-predicate predicate-patterns)
814
for nodes-with-subject = (gethash subject-predicate predicate-patterns)
815
do (loop for nwop in nodes-with-object
816
do (loop for nwsp in nodes-with-subject
817
;;if (eq nwop nwsp) do (print (list :reflexive path nwo nws))
818
do (push (make-path-link :path path :destination nwsp)
819
(node-object-links nwop))
820
do (push (make-path-link :path path :destination nwop)
821
(node-subject-links nwsp)))))
822
;; collect the sequence of pattern sets for each intended attribute's predicate,
824
(let ((node-list (remove-duplicates (reduce #'append (loop for predicate in predicates
825
collect (gethash predicate predicate-patterns))))))
826
(print (list :reduced-pattern-sets (length node-list) node-list)))
827
(let ((covering-pattern-sets (loop for pattern-set being each hash-value of covering-patterns
828
collect pattern-set)))
829
(setf covering-pattern-sets
830
(sort covering-pattern-sets #'< :key #'(lambda (set) (reduce #'+ set :key #'node-count))))
831
;; (print (list :covering-pattern-sets covering-pattern-sets))
832
(let ((collected-paths ()))
833
(labels ((connect-nodes (start-nodes end-nodes)
834
;; clear entire graph
835
(loop for node in nodes do (setf (node-value node) nil))
836
;; derive intermediate paths and respective end nodes
837
(let ((intermediate-paths ()) (new-end-nodes ()))
838
(flet ((collect (path node)
839
(setf path (butlast path)) ;; remove the start node
841
(pushnew path intermediate-paths :test #'equal))
842
(pushnew node new-end-nodes)))
843
(declare (dynamic-extent #'collect))
844
(loop for start-node in start-nodes
845
for path = (extend-path (list start-node) end-nodes #'collect)))
846
(values intermediate-paths new-end-nodes)))
847
(extend-path (path end-nodes collect)
848
(flet ((extend-to-node (node)
849
(cond ((member node end-nodes)
850
(funcall collect (reverse path) node))
851
((null (node-value node))
852
(setf (node-value node) (node-count node))
853
(extend-path (cons node path) end-nodes collect)))))
854
(loop for link in (node-object-links (first path))
855
for node = (link-destination link)
856
do (extend-to-node node))
857
(loop for link in (node-subject-links (first path))
858
for node = (link-destination link)
859
do (extend-to-node node)))))
860
(loop with start-nodes = (pop covering-pattern-sets)
861
for end-nodes = (pop covering-pattern-sets)
862
until (null end-nodes)
863
;; do (print (list :start-nodes start-nodes))
864
do (multiple-value-bind (intermediates end-nodes)
865
(connect-nodes start-nodes end-nodes)
866
;; (print (list :intermediates-and-ends intermediates end-nodes))
867
(push start-nodes collected-paths)
869
(push (first (sort intermediates #'< :key #'length)) collected-paths))
870
(setf start-nodes end-nodes))
871
finally (push start-nodes collected-paths)))
872
(reverse collected-paths)))))
876
(let* ((predicate-pattern-sets (loop for predicate in predicates
877
collect (gethash predicate predicate-patterns)))
878
#+(or) ;;; for saam there would be 150493593600 combinations for 21 patterns by 46 predicates
879
(pattern-sets (sort (loop for set in (let ((combinations ()))
880
(compute-combinations predicate-pattern-sets
881
#'(lambda (combo) (push combo combinations)))
883
collect (sort (remove-duplicates set) #'< :key #'node-count))
884
#'< :key #'(lambda (set) (node-count (first set)))))
885
(destinations (sort (remove-duplicates (reduce #'append predicate-pattern-sets))
886
#'< :key #'node-count)))
887
;; attempt to connect all patterns in a given set
888
(print (list :destinations (length destinations)
889
(mapcar #'(lambda (n) (list (ensure-node-subject n)
890
(node-subject-signature n)
891
(node-object-signature n)
892
(node-predicates n)))
894
(loop for destination in destinations do (describe destination))
895
(labels ((build-tree (node)
896
(setf (node-value node) (node-count node))
897
(let ((sub-tree (loop for link in (node-object-links node)
898
for destination = (link-pattern link)
899
unless (node-value destination)
900
collect (build-tree destination))))
901
(cons node sub-tree)))
903
(cons (first tree) (loop for branch in (rest tree)
904
append (flatten-tree branch)))))
906
(let ((all-sets (loop for node = (find-if #'(lambda (node) (null (node-value node)))
909
collect (flatten-tree (build-tree node)))))
910
(print (loop for set in all-sets collect (length set)))
911
(setf all-sets (sort all-sets #'< :key #'length))
915
;;; (spocq.i::repository-view-definitions (repository "james/cms"))
916
;;; (compute-instance-model (repository-view-definition (repository "james/cms") "topics") mime:application/json)
917
;;; (compute-instance-model (repository "james/cms") mime:application/json)
918
;;; (compute-instance-model (repository "james/cms") mime:image/vnd.dydra.sparql-results+circos+svg+xml)
919
;;; (defparameter *m* (compute-instance-model (account "james") mime:application/json))
920
;;; (view-construct-patterns (repository-view-definition (repository "fbfpt/kombuchadata") "ph_sensor_1"))
921
;;; (view-modify-patterns (repository-view-definition (repository "fbfpt/kombuchadata") "ph_sensor_1"))
922
;;; (view-where-patterns (repository-view-definition (repository "fbfpt/kombuchadata") "sugarPercentageReadings"))
923
;;; (compute-instance-model (repository "fbfpt/kombuchadata") mime:application/json)
924
;;; (defparameter *m* (compute-instance-model (account "fbfpt") mime:application/json))
925
;;; (format-json-object *trace-output* *m*)
926
;;; (format-json-compact *trace-output* *m*)
927
;;; curl -H "Accept: application/json" -H "Authorization: Bearer $TOKEN" -H "application/json" https://nl4.dydra.com/system/accounts/james
928
;;; curl -H "Accept: application/json" -H "Authorization: Bearer $TOKEN" -H "application/json" https://nl4.dydra.com/system/accounts/james/repositories/test
932
(compute-covering-pattern-sets
933
(list (make-pattern-class :count 5 :predicates '(<http://example.org/p1> <http://example.org/pa>))
934
(make-pattern-class :count 10 :predicates '(<http://example.org/p2> <http://example.org/pb>)))
935
(list (make-pattern-path :count 10
936
:object-predicate <http://example.org/pa> :subject-predicate <http://example.org/pb>))
937
'(<http://example.org/p1> <http://example.org/p2>))
939
(compute-pattern-expression
940
(compute-covering-pattern-sets
941
(list (make-pattern-class :count 5 :predicates '(<http://example.org/p1> <http://example.org/pa>))
942
(make-pattern-class :count 10 :predicates '(<http://example.org/p2> <http://example.org/pb>)))
943
(list (make-pattern-path :count 10
944
:object-predicate <http://example.org/pa> :subject-predicate <http://example.org/pb>))
945
'(<http://example.org/p1> <http://example.org/p2>))
946
'((<http://example.org/p1> . ?::p1)
947
(<http://example.org/p2> . ?::p2)
948
(<http://example.org/pa> . ?::pa)
949
(<http://example.org/pb> . ?::pb)))
951
(let* ((repository (repository "saam-mirror/saam"))
954
(compute-covering-pattern-sets
955
(remove-if #'spocq.i::pattern-subclasses (spocq.i::repository-pattern-classes repository))
956
(spocq.i::repository-pattern-paths repository)
957
'(<http://edan.si.edu/saam/id/ontologies/PE_lastname>
958
<http://edan.si.edu/saam/id/ontologies/PE_firstname>
959
<http://www.cidoc-crm.org/cidoc-crm/P69_is_associated_with>))))
960
(print (list :sets sets))
963
(let* ((repository (repository "saam-mirror/saam"))
966
(compute-covering-pattern-sets
967
(remove-if #'spocq.i::pattern-subclasses (spocq.i::repository-pattern-classes repository))
968
(spocq.i::repository-pattern-paths repository)
969
'(<http://edan.si.edu/saam/id/ontologies/PE_lastname>
970
<http://edan.si.edu/saam/id/ontologies/PE_firstname>
971
<http://www.cidoc-crm.org/cidoc-crm/P69_is_associated_with>)))
972
(sparql (compute-pattern-expression sets
973
'((<http://edan.si.edu/saam/id/ontologies/PE_lastname> . "lastname")
974
(<http://edan.si.edu/saam/id/ontologies/PE_firstname> . "firstname")
975
(<http://www.cidoc-crm.org/cidoc-crm/P69_is_associated_with>. "is_associated_with")))))
976
(print-sparql sparql)
979
(let* ((repository (repository "saam-mirror/saam"))
982
(compute-covering-pattern-sets
983
(remove-if #'spocq.i::pattern-subclasses (spocq.i::repository-pattern-classes repository))
984
(spocq.i::repository-pattern-paths repository)
985
'(<http://edan.si.edu/saam/id/ontologies/PE_lastname>
986
<http://edan.si.edu/saam/id/ontologies/PE_firstname>
987
<http://www.cidoc-crm.org/cidoc-crm/P69_is_associated_with>)))
988
(sparql (compute-pattern-expression sets
989
'((<http://edan.si.edu/saam/id/ontologies/PE_lastname> . "lastname")
990
(<http://edan.si.edu/saam/id/ontologies/PE_firstname> . "firstname")
991
(<http://www.cidoc-crm.org/cidoc-crm/P69_is_associated_with>. "is_associated_with")))))
992
(print-sparql sparql)
993
(let ((result (test-sparql sparql :repository-id (repository-id repository))))
994
(list (length result) (subseq result 0 10)))
998
;;; (test-sparql "select distinct ?p where {?s ?p ?o}" :repository-id "james/cms")
999
;;; (repository-pattern-classes "james/cms")
1000
;;; (repository-pattern-classes "james/test")
1001
;;; (repository-pattern-classes "fbfpt/kombuchadata")
1002
;;; (repository-pattern-classes "nxp/plm")
1003
;;; (test-sparql "select (count (distinct ?p) as ?count) from <urn:dydra:all> where {?s ?p ?o}" :repository-id "nxp/plm")
1005
;;; (time (length (repository-pattern-paths (repository "nxp/plm")))) ca 40s, 32368029 stmts, 2536605 subjects
1006
;;; (time (compute-instance-model (repository "nexperia/pcn") mime:application/json))
1007
;;; (time (compute-instance-model (repository "nexperia/public-data") mime:application/json))
1008
;;; (time (defparameter *m* (compute-instance-model (account "nexperia") mime:application/json)))
1009
;;; (time (compute-instance-model (repository "nexperia/plm") mime:application/json))
1010
;;; (length (repository-referenced-pattern-classes (repository "nexperia/plm") :abstract t))
1011
;;; (length (repository-pattern-paths (repository "nexperia/plm")))
1012
;;; (length (remove-duplicates (repository-pattern-paths (repository "nexperia/plm"))))
1016
the test target is the account "ui-test", which comprises the repositories
1017
- internal : with a simple foaf pattern
1018
- withServiceClause : with a service clause
1019
- withSubquery : : with a view as the service location
1020
- asImport : with a construct clause and some empty where
1022
(compute-instance-model (account "ui-test") mime:application/json)
1023
(compute-instance-model (repository "ui-test/base") mime:application/json)
1024
(compute-instance-model (repository-view-definition (repository "ui-test/base") "asImport") mime:application/json)
1025
(compute-instance-model (repository-view-definition (repository "ui-test/base") "withServiceClause") mime:application/json)
1026
(compute-instance-model (repository-view-definition (repository "ui-test/base") "withSubquery") mime:application/json)
1027
(view-construct-patterns (repository-view-definition (repository "ui-test/base") "asImport"))
1028
(view-servicereferences (repository-view-definition (repository "ui-test/base") "asImport"))
1029
(view-view-references (repository-view-definition (repository "ui-test/base") "withSubquery"))
1031
(run-sparql-internal "
1033
<http://example.org/basePerson>
1035
foaf:name 'Base Person' ;
1036
foaf:mbox <mailto:base.person@example.org> ;
1037
foaf:homepage <https://dydra.com/ui-test> ;
1040
foaf:name 'ServicePerson'
1044
:repository-id "ui-test/base"
1045
:agent (system-agent))
1047
(run-sparql-internal "
1049
<http://example.org/servicePerson>
1051
foaf:name 'ServicePerson' ;
1052
foaf:mbox <mailto:service.person@example.org> ;
1053
foaf:homepage <https://dydra.com/ui-test> ;
1056
foaf:name 'Base Person'
1060
:repository-id "ui-test/service-target"
1061
:agent (system-agent))