-
Notifications
You must be signed in to change notification settings - Fork 3
/
workspace.lisp
149 lines (135 loc) · 6.31 KB
/
workspace.lisp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;
;; This file is a collection of 'workspace' code for exploring a
;; 4store RDF knowledgebase using Steel Bank Common Lisp. The code has
;; been used with Steel Bank Common Lisp, but should run without
;; issues in many if not most Common Lisp implementations.
;;
;; Copyright (c) 2011, Patrick D. Logan
;; All rights reserved.
;;
;; See COPYING for more information. The license is the "new and
;; simplified BSD license" See
;; http://www.opensource.org/licenses/bsd-license
;;
;; For installing 4store on ubuntu see:
;; http://patricklogan.blogspot.com/2011/03/building-4store-on-ubuntu-1010.html
;;
;; Create a new 4store knowledgebase:
;; 4s-backend-setup sample
;;
;; Start the knowledgebase server as a daemon:
;; 4s-backend sample
;;
;; Start a sparql endpoint http server for the knowledgebase:
;; 4s-httpd -p 8080 -D sample
;;
;; -D prevents the http server from daemonizing in order to see
;; messages interactively.
;;
;; Status of the (empty) KB is now at http://localhost:8080/status/
;;
;; Load the sample data (an RDF/XML file) using curl (here) or a PUT
;; from lisp (below):
;; curl -v -T organogram-co-2010-10-31-index.rdf 'http://localhost:8080/data/organogram-co-2010-10-31-index'
;;
;; The data file comes from
;; http://source.data.gov.uk/data/reference/organogram-co/2010-10-31/index.rdf
;; and other formats are also available there.
;;
;; Install Steel Bank Common Lisp from http://www.sbcl.org/
;;
;; Install quicklisp for finding and managing CL libraries (similar to
;; ruby's gems)
;; http://www.quicklisp.org/
(ql:quickload "puri") ; Working with URIs.
(ql:quickload "drakma") ; An HTTP client.
(ql:quickload "cxml") ; XML parsing.
(ql:quickload "fare-matcher") ; Lisp pattern matching.
;(ql:quickload "cl-utilities") ; Em, utilities not used currently in this file.
(ql:quickload "cl-rdfxml") ; RDF/XML parsing into RDF-specific objects.
;(ql:quickload "hunchentoot") ; A web server, but only import a couple URL utilities.
(use-package 'drakma)
(use-package 'fare-matcher)
;(use-package 'cl-utilities)
(use-package 'cl-rdfxml)
(import 'puri::render-uri)
(import 'puri::uri-p)
;(import 'hunchentoot::url-encode)
;(import 'hunchentoot::url-decode)
(defun put-data ()
"This PUTs the data file at the given URL. Equivalalent of: curl -v -T organogram-co-2010-10-31-index.rdf 'http://localhost:8080/data/organogram-co-2010-10-31-index'"
(http-request "http://localhost:8080/data/organogram-co-2010-10-31-index"
:method :put
:content (open #p"/home/patrick/dev/sbcl-4store/organogram-co-2010-10-31-index.rdf" :element-type '(unsigned-byte 8))
:content-type "application/rdf+xml" :content-length t))
(defun sparql-server-status ()
"Return the status of the 4store sparql server from lisp. http-request returns multiple values and the second one is the numerical http status."
(nth-value 1 (http-request "http://localhost:8080/status")))
;; Tell drakma to treat these additional types as text rather than
;; binary.
(setq *text-content-types* (list* '("application". "sparql-results+xml")
'("application" . "rdf+xml")
*text-content-types*))
(defun remove-if-typep (type list)
"A shorthand way to remove an unwanted type of data from a list."
(remove-if (lambda (x) (typep x type)) list))
(defun select-rdfs-classes ()
"Select all the RDFS classses in the knowledgebase. Return the multiple values from the query POSTed to the knowledgebase's sparql http end-point. The first value is the body of the response in the sparql query results XML format."
(http-request "http://localhost:8080/sparql/"
:method :post
:parameters `(("query" . "prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>
prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#>
select distinct ?type
where {
?x a ?type .
}
order by ?type"))))
(defun construct-persons ()
"Select the FOAF (http://www.foaf-project.org/) Person instances with FOAF names, and all other triples having the Person as the subject. Return the multiple values from the query POSTed to the knowledgebase's sparql http end-point. The first value is a graph constructed from the selected triples in RDF/XML format."
(http-request "http://localhost:8080/sparql/"
:method :post
:parameters `(("query" . "prefix foaf: <http://xmlns.com/foaf/0.1/>
construct {
?person
a foaf:Person ;
foaf:name ?name ;
?prop ?value .
} where {
?person a foaf:Person ;
foaf:name ?name ;
?prop ?value .
}"))))
(defun map-uris-to-strings (list)
"Return a copy of the given list with top-level URIs rendered as strings."
(mapcar (lambda (x)
(if (uri-p x)
(with-output-to-string (stream)
(render-uri x stream))
x)) list))
(defun extract-persons ()
"Use cl-rdfxml to parse the RDF/XML of FOAF Persons and related triples. Return the triples as a list of three-lists with URIs rendered as strings."
(let ((persons '()))
(parse-document (lambda (s p o)
(push (map-uris-to-strings (list s p o))
persons))
(construct-persons))
;; not really necessary to reverse, but it's nice to be in the
;; same order as the RDF/XML.
(reverse persons)))
(defun parse-result (xml-result)
"Use cxml to parse a sparql result XML formatted string into a list structure."
(cxml:parse xml-result (cxml-xmls:make-xmls-builder)))
;; More meaningful names.
(setf (symbol-function 'extract-data) #'sixth)
(setf (symbol-function 'extract-rows) #'cdddr)
(defun extract-rdfs-classes ()
"Return a list of the RDFS class URIs in the knowledgebase. Use fare-matcher to pattern-match over a list structure parsed from the sparql XML format. Underscores in the pattern are 'don't care' positions. The 'uri' variable is the position of interest."
(mapcar (lambda (item)
(letm (list _ _ _ (list _ _ (list _ _ uri)) _)
item
uri))
(remove-if-typep 'string (extract-rows
(extract-data
(parse-result
(select-rdfs-classes)))))))