nRQL has been revised extensively. Some important features of the new nRQL versions are:
Here are some nRQL examples which demonstrates the points 1. to 3. Consider the following simple ABox:
(related i j r)
(related j k r) |
Suppose you want to create a comma separated values file called test.csv which contains all the (possible implied) R role assertions. nRQL allows you to do this:
(retrieve (((:lambda (x y)
(with-open-output-file ("~/test.csv") (format *output-stream* "~A;~A~%" x y))) ?x ?y)) (?x ?y r)) |
So, the query body retrieves all ?x, ?y individuals which stand in an R relationship; for each ?x, ?y tuple, one more line is attached to the file test.csv.
Regarding point 2, let us illustrate how “combined” TBox/ABox queries can be used to retrieve the direct instances of a concept, which has been requested by many users.
Let us create two concepts c and d such that d is a sub concept (child concept) of c:
? (full-reset)
> :okay-full-reset ? (define-concept c (some r top)) > :OKAY ? (define-concept d (and c e)) > :OKAY |
We can verify that d is indeed a child concept of c, using a so-called TBox query:
? (tbox-retrieve (?x) (c ?x has-child))
> (((?x d))) |
Let us create two individuals so that i and j are instances of c; moreover, j is also an instances of d:
? (related i j r)
> :OKAY ? (related j k r) > :OKAY ? (instance j e) > :OKAY ? (retrieve (?x) (?x c)) > (((?x j)) ((?x i))) ? (retrieve (?x) (?x d)) > (((?x j))) |
Thus, both i and j are c instances. However, only i is a direct c instance. We can retrieve these direct instances of c as follows:
? (retrieve1 (?x c)
( ((:lambda (x) (if (some (lambda (subclass) (retrieve () ‘(,x ,subclass))) (flatten (tbox-retrieve1 ‘(c ?subclass has-child) ‘( ((:lambda (subclass) subclass) ?subclass))))) :reject ‘(?x ,x))) ?x))) > (((?x i))) |
Basically, retrieve1 is like retrieve, but first comes the body, and then the head. Thus, ?x is bound to a c instance. Using this binding, it is checked by means of a TBox subquery (tbox-retrieve1) whether the individual bound to ?x is also an instance of any subclass of c. If this is the case, the result tuple (resp. the current binding of ?x) is rejected (see the special :reject token); otherwise, the result tuple is constructed and returned. The returned result tuples make up the final result set.
Please note that the combination of lambda expressions and :reject token gives you the ability to define arbitrary, used-defined filter predicates which are executed efficiently since they are directly on the RacerPro server.
Finally, let us consider how aggregation operators can be implemented. Consider the following book store scenario:
(full-reset)
(instance b1 book) (instance b2 book) (related b1 a1 has-author) (related b1 a2 has-author) (related b2 a3 has-author) (define-concrete-domain-attribute price :type real) (instance b1 (= price 10.0)) (instance b2 (= price 20.0)) |
We can now determine the number of authors of the single books with the following query:
? (retrieve (?x
((lambda (book) (let ((authors (retrieve ‘(?a) ‘(,book ?a has-author)))) (length authors))) ?x)) (?x book) :dont-show-lambdas-p t) > (((?x b1) 2) ((?x b2) 1)) |
Another interesting question might be to ask for the average price of all the books. This is a little bit more tricky, but works in nRQL as well:
? (retrieve
(((lambda nil (let ((prices (flatten (retrieve ‘((told-value-if-exists (price ?x))) ‘(?x book) :dont-show-head-projection-operators-p t)))) ‘(average-book-price ,(float (/ (reduce ’+ prices) (length prices)))))))) true-query :dont-show-lambdas-p t) > (((average-book-price 15.0))) |
The trick here is to use the always true query body true-query in order to let nRQL first evaluate the lambda body (since lambda bodies are only valid in nRQL heads, but not available as “first order statements” in RacerPro); thus, the lambda simply acquires all the prices of the individual books and then computes and returns the average price, as expected.
Please refer to the chapter on nRQL in the RacerPro User Guide in order to learn how to implement even more efficient versions of these queries.
Functional programming statements cannot only be used in Lambda terms in queries, but also at toplevel.
(evaluate (let ((x ...)) ...))
|
Often, user-defined query format output must be generated from query results, e.g., HTML reports. This is easy with nRQL as well. For example, the result of the query
(retrieve (?x ?y) (and (?x #!:person) (?x ?y #!:has_pet) (?y #!:cat)))
|
on the famous people+pets.owl KB is
(((?x http://cohse.semanticweb.org/ontologies/people#Fred)
(?y http://cohse.semanticweb.org/ontologies/people#Tibbs)) ((?x http://cohse.semanticweb.org/ontologies/people#Minnie) (?y http://cohse.semanticweb.org/ontologies/people#Tom))) |
Suppose this result shall be presented as an HTML table, together with some additional information about the query which has been posed. In principle, it is easy to generate a HTML file using with-open-output-file and print (format) statements to write some HTML. However, this results in ugly code. For this reason, some syntactic sugar is available. The following query will generate an HTML file example.html which is shown in Figure 8:
(evaluate
(let ((res (retrieve ’(?x ?y) ’(and (?x \# !"person") (?x ?y \# !"has_pet"))))) (with-html ("example.html") (html (head) (html (title) (content "MiniLisp Output"))) (html (body) (html (h1) (content "MiniLisp Output")) (html (h2) (content "Query Head")) (content (query-head :last)) (html (h2) (content "Query Body")) (content (query-body :last)) (html (h2) (content "Query Answer")) (html (table :border 1) (let ((count 0)) (maplist (lambda (bindings) (html (tr) (html (th :colspan 2) (content (format nil "Result Tuple No. ~a" (incf count))))) (maplist (lambda (var-val) (let ((var (first var-val)) (val (second var-val))) (html (tr) (html (td) (content var)) (html (td) (content val))))) bindings)) res))))))) |