Coverage report: /development/source/library/org/datagraph/spocq-shard/src/core/encoding/json-query.lisp
| Kind | Covered | All | % |
| expression | 0 | 142 | 0.0 |
| branch | 0 | 16 | 0.0 |
Key
Not instrumented
Conditionalized out
Executed
Not executed
Both branches taken
One branch taken
Neither branch taken
1
;;; -*- Mode: lisp; Syntax: ansi-common-lisp; Base: 10; Package: org.datagraph.spocq.implementation; -*-
3
(in-package :org.datagraph.spocq.implementation)
5
;;; json-encoded formulation for queries
6
;;; potentially as an alternative to sparql for schema-based prototypes.
7
;;; NOT as an encoding for sparql queries
9
;;; see https://www.semantics3.com/docs/
10
;;; http://lists.w3.org/Archives/Public/public-linked-json/2012Jul/0043.html
12
;;; http://www.jsoniq.org/ : uses '$' to indicate variables
13
;;; flowr model manipulation with stream positioning, aggregation, modifiers, but little pattern support
15
;;; http://community.jaspersoft.com/wiki/jaspersoft-mongodb-query-language
16
;;; http://docs.mongodb.org/manual/tutorial/query-documents/
17
;;; http://docs.mongodb.org/manual/reference/operator/query-modifier/
18
;;; combines a control object, a prototype(FIndFields), possibley with conditional operators (FindQuery),
19
;;; modifiers object and disposition
21
;;; http://docs.marklogic.com/guide/rest-dev/appendixa
22
;;; support "google-style" parsing
23
;;; no prototypes, just a json-structured query declaration
25
;;; https://npmjs.org/package/json-query
26
;;; path-based selection
27
;;; supports anonymous functions as constraint values
29
;;; http://coffeecode.net/egdocs/1.6/draft/html/JSON_Queries.html
30
;;; uses table definitions to translate json into sql
31
;;; carries over the sql idiom "<column> as <alias>"
32
;;; uses the sql standard conditional operator names
33
;;; mql (freebase "metaweb query language")
34
;;; http://wiki.freebase.com/wiki/MQL
35
;;; http://wiki.freebase.com/images/e/e0/MQLcheatsheet-081208.pdf
36
;;; uses 'null' and '[]' to indicate variables
37
;;; inflects the field names with appended conditionals to indicate the constraint: "date_of_birth<" : "2000"
38
;;; expresses filters on variables as reappearing, inflected field names
39
;;; whereby, the filter can appear without the binding
40
;;; aggregation, select assignment, and modifiers are not clear
42
;;; http://pic.dhe.ibm.com/infocenter/bigins/v1r1/index.jsp?topic=%2Fcom.ibm.swg.im.infosphere.biginsights.doc%2Fdoc%2Fc0057474.html
43
;;; http://code.google.com/p/jaql/wiki/JaqlOverview
44
;;; provides retrieval and manipulation for an active json object
46
;;; http://danski.github.io/spahql/
47
;;; operates on an in-memory object
48
;;; uses xpath syntax to select nodes in the tree for retrieval or modification
49
;;; jsonpath (jsonquery claims same capabilities, but more performance)
51
;;; http://livedocs.dojotoolkit.org/dojox/json/query
52
;;; path-oriented procedural operators to to select nodes in the tree for retrieval or modification
53
;;; 20191102: reimplimented as json query
54
;;; https://dojotoolkit.org/reference-guide/1.10/dojox/json/query.html
56
;;; http://orangevolt.blogspot.de/2012/12/8-ways-to-query-json-structures.html
57
;;; json:select (20191102: domain has been retired
58
;;; http://jsonselect.org/#overview
59
;;; uses css patterns to extract data, but although it claims to have queries aganst a data store
60
;;; among its uses cases, there was no example for that.
63
;;;??? how to express more than a construct
64
;;;??? aggregation, grouping, and binding in specific
65
;;;??? sub-queries in general
69
(defparameter *parse-json.type-predicate* |rdf|:|type|)
71
(defun parse-json-query (source &key ((:type-predicate *parse-json.type-predicate*) *parse-json.type-predicate*))
72
(let* ((json-object (parse-json source))
73
(sparql-form (rewrite-json-query-to-sparql json-object)))
74
;; if the expression is just a bgp, wrap it as a construct
75
(if (bgp-form-p sparql-form)
76
`(spocq.a:|construct| ,sparql-form ,sparql-form)
79
(defgeneric rewrite-json-query-to-sparql (json-object)
80
(:documentation "convert a jon object/array into a program definition"
81
;;!! recognize json {"value" ...} rdf formulations
82
;;!! recognize semantics3 value constrainst
83
;;!! recognize explicit filters
84
;;!! allow specific variables
85
;;!! arrays of atomic values imply the 'in' filter condition
87
(:method ((value null))
89
(:method ((value string))
90
(if (or (and (> (length value) 7) (string= "http://" value :end2 7))
91
(and (> (length value) 4) (string= "urn:" value :end2 4)))
94
(:method ((value number))
96
(:method ((value symbol))
97
(or (find-symbol (string value) :spocq.a)
98
(intern (symbol-name value) :?) ))
100
(:method ((json-object cons))
101
"Each object should have two levels.
102
the first is the subject and a nested object.
103
The second is the a-list of predicate and object pairs"
104
(flet ((rewrite-name (name)
106
(construct-prefixed-name (if (position #\: name) name (concatenate 'string ":" name)))
107
(intern (symbol-name name) :?))))
108
(destructuring-bind ((subject . po-pairs)) json-object
110
(string (setf subject (rewrite-json-query-to-sparql subject))
111
(assert (iri-p subject) () "rewrite-json-query-to-sparql: invalid subject term: ~s" subject))
112
(symbol (setf subject (intern (symbol-name subject) :?)))
113
(t (error "rewrite-json-query-to-sparql: invalid subject term: ~s" subject)))
114
(let* ((interned-json (loop for (name . value) in po-pairs
115
for qname = (rewrite-name name)
116
for interned-value = (rewrite-json-query-to-sparql value)
117
if (equalp "a" qname)
118
do (setf qname *parse-json.type-predicate*)
119
collect (cons qname interned-value)))
120
(statement-patterns (loop for (predicate . value) in interned-json
121
append (typecase value
122
(atom `((spocq.a:|triple| ,subject ,predicate ,value)))
123
(cons (let ((value-subject (second (second value))))
124
(cons `(spocq.a:|triple| ,subject ,predicate ,value-subject)
126
(t (error "rewrite-json-query-to-sparql: invalid value term: ~s" value))))))
127
`(spocq.a:|bgp| ,@statement-patterns)))))
129
(:method ((json-array vector))
130
(let ((interned-json (map 'list #'rewrite-json-query-to-sparql json-array)))
133
(defmethod receive-message ((message string) (content-type mime:application/json-query) &rest args)
134
"Given a STRING with application/sparql-query+json CONTENT-TYPE,
135
parse the JSON content, transform into a SPARQL expression
136
Returns an abbreviated property list which specifies just the query sse expression and the original text.
138
If the query string fails to parse, signal a message syntax error which captures the string and the position
139
to which the parse had progressed.
141
(declare (ignore args))
143
((spocq.e::message-syntax-error (lambda (c) c))
144
(error (lambda (error)
145
(spocq.e:message-syntax-error
146
:task (make-instance 'query-error-task
149
:repository-id *repository-id*
150
:revision-id *revision-id*)
152
:expression message))))
153
(let ((sse (parse-json-query message)))
154
(log-info "task ~s~@[/~s~], repository ~s, query[~d]: '~a'"
155
*task-id* *user-id* *repository-id* (length message) (substitute #\space #.(code-char #o012) message))
157
(list :query-expression message
158
:sse-expression sse)))))
162
(tokenize-json "[select s p o [where {a : \"http://example.org\"}]]")
164
(parse-json-query "[select, s, a, b, d, {s : {a : a, \"http://example.org/b\": b , \"http://example.org/c\": {c: {\"http://example.org/d\": d}}}}]")
166
(receive-message "[select, s, a, b, c, d, {s : {\"http://example.org/a\" : a, \"http://example.org/b\": b , \"http://example.org/c\": {c: {\"http://example.org/d\": d}}}}]"
167
mime:application/json-query)
169
(parse-sparql+json "[select, s, p, o]")