pho 0.4.1 → 0.5
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGES +18 -1
- data/README +10 -0
- data/Rakefile +2 -1
- data/doc/rdoc/classes/Pho.html +33 -22
- data/doc/rdoc/classes/Pho/DatatypeProperty.html +12 -12
- data/doc/rdoc/classes/Pho/{RDF_JSON.html → Enrichment.html} +8 -7
- data/doc/rdoc/classes/Pho/Enrichment/ResourceEnricher.html +310 -0
- data/doc/rdoc/classes/Pho/Etags.html +42 -42
- data/doc/rdoc/classes/Pho/Facet/Results.html +19 -19
- data/doc/rdoc/classes/Pho/Facet/Term.html +6 -6
- data/doc/rdoc/classes/Pho/FieldPredicateMap.html +110 -105
- data/doc/rdoc/classes/Pho/FieldWeighting.html +12 -12
- data/doc/rdoc/classes/Pho/FileManagement.html +121 -0
- data/doc/rdoc/classes/Pho/FileManagement/AbstractFileManager.html +443 -0
- data/doc/rdoc/classes/Pho/FileManagement/FileManager.html +258 -0
- data/doc/rdoc/classes/Pho/FileManagement/RDFManager.html +271 -0
- data/doc/rdoc/classes/Pho/Job.html +64 -64
- data/doc/rdoc/classes/Pho/Jobs.html +60 -60
- data/doc/rdoc/classes/Pho/QueryProfile.html +60 -60
- data/doc/rdoc/classes/Pho/RDFCollection.html +4 -378
- data/doc/rdoc/classes/Pho/ResourceHash.html +123 -0
- data/doc/rdoc/classes/Pho/ResourceHash/Converter.html +323 -0
- data/doc/rdoc/classes/Pho/{RDF_JSON → ResourceHash}/SetAlgebra.html +18 -18
- data/doc/rdoc/classes/Pho/Snapshot.html +35 -35
- data/doc/rdoc/classes/Pho/Sparql.html +137 -0
- data/doc/rdoc/classes/Pho/Sparql/SparqlClient.html +515 -0
- data/doc/rdoc/classes/Pho/Sparql/SparqlHelper.html +575 -0
- data/doc/rdoc/classes/Pho/Status.html +26 -26
- data/doc/rdoc/classes/Pho/Store.html +271 -241
- data/doc/rdoc/classes/Pho/Update/Changeset.html +73 -73
- data/doc/rdoc/classes/Pho/Update/ChangesetBuilder.html +34 -34
- data/doc/rdoc/classes/Pho/Update/Changesets.html +14 -14
- data/doc/rdoc/classes/Pho/Update/LiteralStatement.html +31 -23
- data/doc/rdoc/classes/Pho/Update/ResourceStatement.html +45 -21
- data/doc/rdoc/classes/Pho/Update/Statement.html +29 -29
- data/doc/rdoc/classes/String.html +1 -1
- data/doc/rdoc/created.rid +1 -1
- data/doc/rdoc/files/CHANGES.html +49 -3
- data/doc/rdoc/files/README.html +15 -1
- data/doc/rdoc/files/lib/pho/changeset_builder_rb.html +1 -1
- data/doc/rdoc/files/lib/pho/changeset_rb.html +1 -1
- data/doc/rdoc/files/lib/pho/converter_rb.html +108 -0
- data/doc/rdoc/files/lib/pho/enrichment_rb.html +101 -0
- data/doc/rdoc/files/lib/pho/etags_rb.html +1 -1
- data/doc/rdoc/files/lib/pho/field_predicate_map_rb.html +1 -1
- data/doc/rdoc/files/lib/pho/file_management_rb.html +101 -0
- data/doc/rdoc/files/lib/pho/file_manager_rb.html +108 -0
- data/doc/rdoc/files/lib/pho/rdf_collection_rb.html +1 -1
- data/doc/rdoc/files/lib/pho/resource_hash_rb.html +101 -0
- data/doc/rdoc/files/lib/pho/{rdf_json_rb.html → sparql_rb.html} +4 -4
- data/doc/rdoc/files/lib/pho/store_rb.html +1 -1
- data/doc/rdoc/files/lib/pho_rb.html +7 -2
- data/doc/rdoc/fr_class_index.html +12 -2
- data/doc/rdoc/fr_file_index.html +6 -1
- data/doc/rdoc/fr_method_index.html +176 -139
- data/examples/sparql_construct_hash.rb +26 -0
- data/examples/sparql_select.rb +18 -0
- data/lib/pho.rb +6 -1
- data/lib/pho/changeset.rb +24 -9
- data/lib/pho/changeset_builder.rb +10 -10
- data/lib/pho/converter.rb +74 -0
- data/lib/pho/enrichment.rb +81 -0
- data/lib/pho/etags.rb +1 -0
- data/lib/pho/field_predicate_map.rb +6 -1
- data/lib/pho/file_management.rb +102 -0
- data/lib/pho/file_manager.rb +61 -0
- data/lib/pho/rdf_collection.rb +54 -120
- data/lib/pho/{rdf_json.rb → resource_hash.rb} +3 -4
- data/lib/pho/sparql.rb +332 -0
- data/lib/pho/store.rb +20 -14
- data/tests/tc_changeset.rb +46 -0
- data/tests/tc_changeset_builder.rb +122 -1
- data/tests/tc_converter.rb +95 -0
- data/tests/tc_enrichment.rb +83 -0
- data/tests/tc_file_manager.rb +88 -0
- data/tests/tc_rdf_collection.rb +3 -0
- data/tests/{tc_rdf_json.rb → tc_resource_hash.rb} +23 -23
- data/tests/tc_search.rb +1 -1
- data/tests/tc_sparql.rb +131 -6
- data/tests/tc_sparql_helper.rb +214 -0
- data/tests/ts_pho.rb +6 -2
- metadata +47 -8
@@ -0,0 +1,61 @@
|
|
1
|
+
module Pho
|
2
|
+
|
3
|
+
require 'mime/types'
|
4
|
+
|
5
|
+
module FileManagement
|
6
|
+
|
7
|
+
class FileManager < AbstractFileManager
|
8
|
+
|
9
|
+
def initialize(store, dir, ok_suffix=OK, fail_suffix=FAIL, sleep=1)
|
10
|
+
super(store, dir, ok_suffix, fail_suffix, sleep)
|
11
|
+
end
|
12
|
+
|
13
|
+
#List files being managed, i.e. everything not .ok or .fail
|
14
|
+
def list()
|
15
|
+
files = []
|
16
|
+
Dir.glob( File.join(@dir, "*") ) do |file|
|
17
|
+
if File.extname(file) != ".#{@ok_suffix}" && File.extname(file) != ".#{@fail_suffix}"
|
18
|
+
files << file
|
19
|
+
end
|
20
|
+
end
|
21
|
+
return files
|
22
|
+
end
|
23
|
+
|
24
|
+
#List any new files in the directory
|
25
|
+
def new_files()
|
26
|
+
newfiles = Array.new
|
27
|
+
Dir.glob( File.join(@dir, "*") ) do |file|
|
28
|
+
|
29
|
+
if File.extname(file) != ".#{@ok_suffix}" && File.extname(file) != ".#{@fail_suffix}"
|
30
|
+
ok_file = get_ok_file_for(file)
|
31
|
+
fail_file = get_fail_file_for(file)
|
32
|
+
if !( File.exists?(ok_file) or File.exists?(fail_file) )
|
33
|
+
newfiles << file
|
34
|
+
end
|
35
|
+
|
36
|
+
end
|
37
|
+
|
38
|
+
end
|
39
|
+
return newfiles
|
40
|
+
end
|
41
|
+
|
42
|
+
|
43
|
+
protected
|
44
|
+
|
45
|
+
def store_file(file, filename)
|
46
|
+
response = @store.upload_item(file, MIME::Types.type_for(filename)[0].to_s )
|
47
|
+
if (response.status < 300 )
|
48
|
+
File.open(get_ok_file_for(filename), "w") do |file|
|
49
|
+
file.print( "OK" )
|
50
|
+
end
|
51
|
+
else
|
52
|
+
File.open(get_fail_file_for(filename), "w") do |file|
|
53
|
+
YAML::dump(response, file)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
end
|
59
|
+
|
60
|
+
end
|
61
|
+
end
|
data/lib/pho/rdf_collection.rb
CHANGED
@@ -1,129 +1,63 @@
|
|
1
1
|
module Pho
|
2
2
|
|
3
|
-
|
4
|
-
attr_reader :store
|
5
|
-
|
6
|
-
# Provides a simple mechanism for managing a directory of RDF/XML documents
|
7
|
-
# and uploading them to platform store.
|
8
|
-
#
|
9
|
-
# Allows a collection to be mirrored into the platform
|
10
|
-
class RDFCollection
|
11
|
-
|
12
|
-
RDF = "rdf".freeze
|
13
|
-
OK = "ok".freeze
|
14
|
-
FAIL = "fail".freeze
|
15
|
-
|
16
|
-
def initialize(store, dir, rdf_suffix=RDF, ok_suffix=OK, fail_suffix=FAIL, sleep=1)
|
17
|
-
@store = store
|
18
|
-
@dir = dir
|
19
|
-
@sleep = sleep
|
20
|
-
@rdf_suffix = rdf_suffix
|
21
|
-
@ok_suffix = ok_suffix
|
22
|
-
@fail_suffix = fail_suffix
|
23
|
-
end
|
3
|
+
module FileManagement
|
24
4
|
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
retries = failures()
|
37
|
-
retries.each do |filename|
|
38
|
-
File.delete( get_fail_file_for(filename) )
|
39
|
-
#store it
|
40
|
-
file = File.new(filename)
|
41
|
-
store_file(file, filename)
|
42
|
-
end
|
43
|
-
end
|
44
|
-
|
45
|
-
#Reset the directory to clear out any previous statuses
|
46
|
-
#Store can also be reset at the same time: use with care!
|
47
|
-
def reset(reset_store=false)
|
48
|
-
Dir.glob( File.join(@dir, "*.#{@fail_suffix}") ).each do |file|
|
49
|
-
File.delete(file)
|
50
|
-
end
|
51
|
-
Dir.glob( File.join(@dir, "*.#{@ok_suffix}") ).each do |file|
|
52
|
-
File.delete(file)
|
53
|
-
end
|
54
|
-
end
|
55
|
-
|
56
|
-
#List files being managed
|
57
|
-
def list()
|
58
|
-
return Dir.glob( File.join(@dir, "*.#{@rdf_suffix}") )
|
59
|
-
end
|
60
|
-
|
61
|
-
#List failures
|
62
|
-
def failures()
|
63
|
-
fails = Array.new
|
64
|
-
Dir.glob( File.join(@dir, "*.#{@fail_suffix}") ).each do |file|
|
65
|
-
fails << file.gsub(/\.#{@fail_suffix}/, ".#{@rdf_suffix}")
|
66
|
-
end
|
67
|
-
return fails
|
68
|
-
end
|
69
|
-
|
70
|
-
#List successes
|
71
|
-
def successes()
|
72
|
-
successes = Array.new
|
73
|
-
Dir.glob( File.join(@dir, "*.#{@ok_suffix}") ).each do |file|
|
74
|
-
successes << file.gsub(/\.#{@ok_suffix}/, ".#{@rdf_suffix}")
|
75
|
-
end
|
76
|
-
return successes
|
77
|
-
end
|
78
|
-
|
79
|
-
#List any new files in the directory
|
80
|
-
def new_files()
|
81
|
-
newfiles = Array.new
|
82
|
-
Dir.glob( File.join(@dir, "*.#{@rdf_suffix}") ) do |file|
|
83
|
-
ok_file = get_ok_file_for(file)
|
84
|
-
fail_file = get_fail_file_for(file)
|
85
|
-
if !( File.exists?(ok_file) or File.exists?(fail_file) )
|
86
|
-
newfiles << file
|
5
|
+
# Provides a simple mechanism for managing a directory of RDF/XML documents
|
6
|
+
# and uploading them to platform store.
|
7
|
+
#
|
8
|
+
# Allows a collection to be mirrored into the platform
|
9
|
+
class RDFManager < AbstractFileManager
|
10
|
+
|
11
|
+
RDF = "rdf".freeze
|
12
|
+
|
13
|
+
def initialize(store, dir, rdf_suffix=RDF, ok_suffix=OK, fail_suffix=FAIL, sleep=1)
|
14
|
+
super(store, dir, ok_suffix, fail_suffix, sleep)
|
15
|
+
@rdf_suffix = rdf_suffix
|
87
16
|
end
|
88
|
-
end
|
89
|
-
return newfiles
|
90
|
-
end
|
91
17
|
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
successes = successes()
|
97
|
-
newfiles = new_files()
|
98
|
-
total = failures.size + successes.size + newfiles.size
|
99
|
-
summary = "#{@dir} contains #{total} files: #{successes.size} stored, #{failures.size} failed, #{newfiles.size} new"
|
100
|
-
return summary
|
101
|
-
end
|
102
|
-
|
103
|
-
def get_fail_file_for(filename)
|
104
|
-
return filename.gsub(/\.#{@rdf_suffix}/, ".#{@fail_suffix}")
|
105
|
-
end
|
106
|
-
|
107
|
-
def get_ok_file_for(filename)
|
108
|
-
return filename.gsub(/\.#{@rdf_suffix}/, ".#{@ok_suffix}")
|
109
|
-
end
|
110
|
-
|
111
|
-
private
|
112
|
-
|
113
|
-
def store_file(file, filename)
|
114
|
-
response = @store.store_file(file)
|
115
|
-
if (response.status < 300 )
|
116
|
-
File.open(get_ok_file_for(filename), "w") do |file|
|
117
|
-
file.print( "OK" )
|
18
|
+
|
19
|
+
#List files being managed
|
20
|
+
def list()
|
21
|
+
return Dir.glob( File.join(@dir, "*.#{@rdf_suffix}") )
|
118
22
|
end
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
23
|
+
|
24
|
+
#List any new files in the directory
|
25
|
+
def new_files()
|
26
|
+
newfiles = Array.new
|
27
|
+
Dir.glob( File.join(@dir, "*.#{@rdf_suffix}") ) do |file|
|
28
|
+
ok_file = get_ok_file_for(file)
|
29
|
+
fail_file = get_fail_file_for(file)
|
30
|
+
if !( File.exists?(ok_file) or File.exists?(fail_file) )
|
31
|
+
newfiles << file
|
32
|
+
end
|
33
|
+
end
|
34
|
+
return newfiles
|
35
|
+
end
|
36
|
+
|
126
37
|
|
38
|
+
protected
|
39
|
+
|
40
|
+
def store_file(file, filename)
|
41
|
+
response = @store.store_file(file)
|
42
|
+
if (response.status < 300 )
|
43
|
+
File.open(get_ok_file_for(filename), "w") do |file|
|
44
|
+
file.print( "OK" )
|
45
|
+
end
|
46
|
+
else
|
47
|
+
File.open(get_fail_file_for(filename), "w") do |file|
|
48
|
+
YAML::dump(response, file)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
|
54
|
+
end
|
55
|
+
|
127
56
|
end
|
128
|
-
|
57
|
+
|
58
|
+
#Deprecated. Use Pho::FileManangement::RDFManager instead
|
59
|
+
class RDFCollection < Pho::FileManagement::RDFManager
|
60
|
+
#for backwards compatibility
|
61
|
+
end
|
62
|
+
|
129
63
|
end
|
@@ -1,11 +1,10 @@
|
|
1
1
|
module Pho
|
2
2
|
|
3
|
-
#TODO
|
4
|
-
#blank nodes
|
3
|
+
#TODO blank nodes
|
5
4
|
|
6
|
-
#Module providing code for manipulating
|
5
|
+
#Module providing code for manipulating resource hashes structured according
|
7
6
|
#to the RDF in JSON spec
|
8
|
-
module
|
7
|
+
module ResourceHash
|
9
8
|
|
10
9
|
#Class providing set algebra methods over triple hashes
|
11
10
|
class SetAlgebra
|
data/lib/pho/sparql.rb
ADDED
@@ -0,0 +1,332 @@
|
|
1
|
+
module Pho
|
2
|
+
|
3
|
+
#Module providing a SPARQL client library, support for parsing SPARQL query responses into Ruby objects
|
4
|
+
#and other useful behaviour
|
5
|
+
module Sparql
|
6
|
+
|
7
|
+
SPARQL_RESULTS_XML = "application/sparql-results+xml"
|
8
|
+
SPARQL_RESULTS_JSON = "application/sparql-results+json"
|
9
|
+
|
10
|
+
#A simple SPARQL client that handles the basic HTTP traffic
|
11
|
+
class SparqlClient
|
12
|
+
|
13
|
+
#URI of the endpoint
|
14
|
+
attr_reader :endpoint
|
15
|
+
#HTTPClient object
|
16
|
+
attr_reader :client
|
17
|
+
#Name of output parameter to use to control response format. If set then this parameter is added
|
18
|
+
#to the query string, rather than using Content Negotiation
|
19
|
+
attr_accessor :output_parameter_name
|
20
|
+
attr_reader :graphs
|
21
|
+
attr_reader :named_graphs
|
22
|
+
|
23
|
+
#Configures whether the remote endpoint supports the RDF-in-JSON specification for serializing
|
24
|
+
#RDF graphs as JSON. Will default to false.
|
25
|
+
attr_accessor :supports_rdf_json
|
26
|
+
#Configures whether the remote endpoint supports SPARQL JSON Results format
|
27
|
+
#Will default to true.
|
28
|
+
attr_accessor :supports_sparql_json
|
29
|
+
|
30
|
+
#Initialize a client for a specific endpoint
|
31
|
+
#
|
32
|
+
# endpoint:: uri of the SPARQL endpoint
|
33
|
+
# client:: optionally, a reference to an existing HTTPClient object instance
|
34
|
+
def initialize(endpoint, client=HTTPClient.new() )
|
35
|
+
@endpoint = endpoint
|
36
|
+
@graphs = nil
|
37
|
+
@named_graphs = nil
|
38
|
+
@client = client
|
39
|
+
@output_parameter_name = nil
|
40
|
+
@supports_rdf_json = false
|
41
|
+
@supports_sparql_json = true
|
42
|
+
end
|
43
|
+
|
44
|
+
#Add a default graph. This will be added as a default graph in the request protocol
|
45
|
+
def add_default_graph(graph_uri)
|
46
|
+
if @graphs == nil
|
47
|
+
@graphs = []
|
48
|
+
end
|
49
|
+
@graphs << graph_uri
|
50
|
+
end
|
51
|
+
|
52
|
+
#Add a named graph. This will be added as a named graph in the request protocol
|
53
|
+
def add_named_graph(graph_uri)
|
54
|
+
if @named_graphs == nil
|
55
|
+
@named_graphs = []
|
56
|
+
end
|
57
|
+
@named_graphs << graph_uri
|
58
|
+
end
|
59
|
+
|
60
|
+
#Perform a sparql query
|
61
|
+
#
|
62
|
+
# sparql:: a valid SPARQL query
|
63
|
+
# format:: specific a request format. Usually a media-type, but may be a name for a type, if not using Conneg
|
64
|
+
# graphs:: an array of default graphs
|
65
|
+
# named_graphs:: an array of named graphs
|
66
|
+
def query(sparql, format=nil, graphs=nil, named_graphs=nil)
|
67
|
+
|
68
|
+
params = {}
|
69
|
+
params["query"] = sparql
|
70
|
+
|
71
|
+
if graphs != nil
|
72
|
+
params["default-graph-uri"] = graphs
|
73
|
+
elsif @graphs != nil
|
74
|
+
params["default-graph-uri"] = @graphs
|
75
|
+
end
|
76
|
+
|
77
|
+
if named_graphs != nil
|
78
|
+
params["named-graph-uri"] = named_graphs
|
79
|
+
elsif @named_graphs != nil
|
80
|
+
params["named-graph-uri"] = @named_graphs
|
81
|
+
end
|
82
|
+
|
83
|
+
headers = {}
|
84
|
+
if format != nil
|
85
|
+
|
86
|
+
if @output_parameter_name != nil
|
87
|
+
params[@output_parameter_name] = format
|
88
|
+
else
|
89
|
+
headers["Accept"] = format
|
90
|
+
end
|
91
|
+
|
92
|
+
end
|
93
|
+
|
94
|
+
return @client.get( @endpoint, params, headers )
|
95
|
+
end
|
96
|
+
|
97
|
+
#Perform a SPARQL DESCRIBE query.
|
98
|
+
#
|
99
|
+
# query:: the SPARQL query
|
100
|
+
# format:: the preferred response format
|
101
|
+
def describe(query, format="application/rdf+xml")
|
102
|
+
return query(query, format)
|
103
|
+
end
|
104
|
+
|
105
|
+
#DESCRIBE multiple resources in a single query. The provided array should contain
|
106
|
+
#the uris that are to be described
|
107
|
+
#
|
108
|
+
#This will generate a query like:
|
109
|
+
# DESCRIBE <http://www.example.org> <http://www.example.com> ...
|
110
|
+
#
|
111
|
+
# uris:: list of the uris to be described
|
112
|
+
# format:: the preferred response format. Default is RDF/XML
|
113
|
+
def multi_describe(uris, format="application/rdf+xml")
|
114
|
+
query = "DESCRIBE " + uris.map {|u| "<#{u}>" }.join(" ")
|
115
|
+
return query(query, format)
|
116
|
+
end
|
117
|
+
|
118
|
+
#Perform a SPARQL CONSTRUCT query.
|
119
|
+
#
|
120
|
+
# query:: the SPARQL query
|
121
|
+
# format:: the preferred response format
|
122
|
+
def construct(query, format="application/rdf+xml")
|
123
|
+
return query(query, format)
|
124
|
+
end
|
125
|
+
|
126
|
+
#Perform a SPARQL ASK query.
|
127
|
+
#
|
128
|
+
# query:: the SPARQL query
|
129
|
+
# format:: the preferred response format
|
130
|
+
def ask(query, format=Pho::Sparql::SPARQL_RESULTS_XML)
|
131
|
+
return query(query, format)
|
132
|
+
end
|
133
|
+
|
134
|
+
#Perform a SPARQL SELECT query.
|
135
|
+
#
|
136
|
+
# query:: the SPARQL query
|
137
|
+
# format:: the preferred response format
|
138
|
+
def select(query, format=Pho::Sparql::SPARQL_RESULTS_XML)
|
139
|
+
return query(query, format)
|
140
|
+
end
|
141
|
+
|
142
|
+
end
|
143
|
+
|
144
|
+
#Simple helper class for manipulating and executing SPARQL queries and manipulating the results
|
145
|
+
class SparqlHelper
|
146
|
+
VARIABLE_MATCHER = /(\?|\$)([a-zA-Z]+)/
|
147
|
+
|
148
|
+
#Apply some initial bindings to parameters in a query
|
149
|
+
#
|
150
|
+
#The keys in the values hash are used to replace variables in a query
|
151
|
+
#The values are supplied as is, allowing them to be provided as URIs, or typed literals
|
152
|
+
#according to Turtle syntax.
|
153
|
+
#
|
154
|
+
#Any keys in the hash that are not in the query are ignored. Any variables not found
|
155
|
+
#in the hash remain unbound.
|
156
|
+
#
|
157
|
+
# query:: the query whose initial bindings are to be set
|
158
|
+
# values:: hash of query name to value
|
159
|
+
def SparqlHelper.apply_initial_bindings(query, bindings={})
|
160
|
+
copy = query.clone()
|
161
|
+
copy.gsub!(VARIABLE_MATCHER) do |pattern|
|
162
|
+
key = $2
|
163
|
+
if bindings.has_key?(key)
|
164
|
+
bindings[key].to_s
|
165
|
+
else
|
166
|
+
pattern
|
167
|
+
end
|
168
|
+
end
|
169
|
+
return copy
|
170
|
+
end
|
171
|
+
|
172
|
+
#Convert a SPARQL query result binding into a hash suitable for passing
|
173
|
+
#to the apply_initial_bindings method.
|
174
|
+
#
|
175
|
+
#The result param is assumed to be a Ruby hash that reflects the structure of
|
176
|
+
#a binding in a SELECT query result (i.e. the result of parsing the <tt>application/sparql-results+json</tt>
|
177
|
+
#format and extracting an specific result binding.
|
178
|
+
#
|
179
|
+
#The method is intended to be used to support cases where an initial select query is
|
180
|
+
#performed to extract some variables that can later be plugged into a subsequent
|
181
|
+
#query
|
182
|
+
#
|
183
|
+
# result:: hash conforming to structure of a <tt>binding</tt> in the SPARQL JSON format
|
184
|
+
def SparqlHelper.result_to_query_binding(result)
|
185
|
+
hash = {}
|
186
|
+
result.each_pair do |key, value|
|
187
|
+
if value["type"] == "uri"
|
188
|
+
hash[key] = "<#{value["value"]}>"
|
189
|
+
elsif (value["type"] == "literal" && !value.has_key?("datatype"))
|
190
|
+
hash[key] = "\"#{value["value"]}\""
|
191
|
+
elsif (value["type"] == "literal" && value.has_key?("datatype"))
|
192
|
+
hash[key] = "\"#{value["value"]}\"^^#{value["datatype"]}"
|
193
|
+
else
|
194
|
+
#do nothing for bnodes
|
195
|
+
end
|
196
|
+
end
|
197
|
+
return hash
|
198
|
+
end
|
199
|
+
|
200
|
+
#Convert Ruby hash structured according to SPARQL JSON format
|
201
|
+
#into an array of hashes by calling result_to_query_binding on each binding
|
202
|
+
#into the results.
|
203
|
+
#
|
204
|
+
#E.g:
|
205
|
+
# <tt>results = Pho::Sparql::SparqlHelper.select(query, sparql_client)</tt>
|
206
|
+
# <tt>bindings = Pho::Sparql::SparqlHelper.results_to_query_bindings(results)</tt>
|
207
|
+
#
|
208
|
+
# results:: hash conforming to SPARQL SELECT structure
|
209
|
+
def SparqlHelper.results_to_query_bindings(results)
|
210
|
+
bindings = []
|
211
|
+
|
212
|
+
results["results"]["bindings"].each do |result|
|
213
|
+
bindings << result_to_query_binding(result)
|
214
|
+
end
|
215
|
+
return bindings
|
216
|
+
end
|
217
|
+
|
218
|
+
#Perform a simple SELECT query on an endpoint.
|
219
|
+
#Will request the results using the SPARQL JSON results format, and parse the
|
220
|
+
#resulting JSON results. The result will therefore be a simple ruby hash of the results
|
221
|
+
#
|
222
|
+
#An error will be raised if the response is HTTP OK.
|
223
|
+
#
|
224
|
+
# query:: the SPARQL SELECT query
|
225
|
+
# sparql_client:: a configured SparqlClient object
|
226
|
+
def SparqlHelper.select(query, sparql_client)
|
227
|
+
#TODO: test whether endpoint supports json, and if not, switch to parsing XML
|
228
|
+
resp = sparql_client.select(query, Pho::Sparql::SPARQL_RESULTS_JSON)
|
229
|
+
if resp.status != 200
|
230
|
+
raise "Error performing sparql query: #{resp.status} #{resp.reason}\n#{resp.content}"
|
231
|
+
end
|
232
|
+
return JSON.parse( resp.content )
|
233
|
+
end
|
234
|
+
|
235
|
+
#Performs an ASK query on an endpoint, returing a boolean true/false response
|
236
|
+
#
|
237
|
+
#Will request the results using the SPARQL JSON results format, parse the
|
238
|
+
#resulting JSON results, and extract the true/false response.
|
239
|
+
#
|
240
|
+
# query:: the SPARQL SELECT query
|
241
|
+
# sparql_client:: a configured SparqlClient object
|
242
|
+
def SparqlHelper.ask(query, sparql_client)
|
243
|
+
json = SparqlHelper.select(query, sparql_client)
|
244
|
+
return json["boolean"] == "true"
|
245
|
+
end
|
246
|
+
|
247
|
+
#Perform a simple SELECT query on an endpoint and return a simple array of values
|
248
|
+
#
|
249
|
+
#Will request the results using the SPARQL JSON results format, and parse the
|
250
|
+
#resulting JSON results. The assumption is that the SELECT query contains a single "column"
|
251
|
+
#of values, which will be returned as an array
|
252
|
+
#
|
253
|
+
#Note this will lose any type information, only the value of the bindings are returned
|
254
|
+
#
|
255
|
+
# query:: the SPARQL SELECT query
|
256
|
+
# sparql_client:: a configured SparqlClient object
|
257
|
+
def SparqlHelper.select_values(query, sparql_client)
|
258
|
+
results = SparqlHelper.select(query, sparql_client)
|
259
|
+
v = results["head"]["vars"][0];
|
260
|
+
values = [];
|
261
|
+
results["results"]["bindings"].each do |binding|
|
262
|
+
values << binding[v]["value"]
|
263
|
+
end
|
264
|
+
return values
|
265
|
+
end
|
266
|
+
|
267
|
+
#Perform a simple SELECT query on an endpoint and return a single result
|
268
|
+
#
|
269
|
+
#Will request the results using the SPARQL JSON results format, and parse the
|
270
|
+
#resulting JSON results. The assumption is that the SELECT query returns a single
|
271
|
+
#value (i.e single variable, with single binding)
|
272
|
+
#
|
273
|
+
#Note this will lose any type information, only the value of the binding is returned
|
274
|
+
#
|
275
|
+
# query:: the SPARQL SELECT query
|
276
|
+
# sparql_client:: a configured SparqlClient object
|
277
|
+
def SparqlHelper.select_single_value(query, sparql_client)
|
278
|
+
results = SparqlHelper.select(query, sparql_client)
|
279
|
+
v = results["head"]["vars"][0];
|
280
|
+
return results["results"]["bindings"][0][v]["value"]
|
281
|
+
end
|
282
|
+
|
283
|
+
#Perform a SPARQL CONSTRUCT query against an endpoint, requesting the results in JSON
|
284
|
+
#
|
285
|
+
#Will request the results as application/json (with the expectation that it returns RDF_JSON),
|
286
|
+
#and parses the resulting JSON document.
|
287
|
+
#
|
288
|
+
# query:: the SPARQL SELECT query
|
289
|
+
# sparql_client:: a configured SparqlClient object
|
290
|
+
def SparqlHelper.construct_to_resource_hash(query, sparql_client)
|
291
|
+
#TODO: test whether endpoint supports json, and if not, switch to parsing XML
|
292
|
+
resp = sparql_client.construct(query, "application/json")
|
293
|
+
if resp.status != 200
|
294
|
+
raise "Error performing sparql query: #{resp.status} #{resp.reason}\n#{resp.content}"
|
295
|
+
end
|
296
|
+
return Pho::ResourceHash::Converter.parse_json( resp.content )
|
297
|
+
end
|
298
|
+
|
299
|
+
#Perform a SPARQL DESCRIBE query against an endpoint, requesting the results in JSON
|
300
|
+
#
|
301
|
+
#Will request the results as application/json (with the expectation that it returns RDF_JSON),
|
302
|
+
#and parses the resulting JSON document.
|
303
|
+
#
|
304
|
+
# query:: the SPARQL SELECT query
|
305
|
+
# sparql_client:: a configured SparqlClient object
|
306
|
+
def SparqlHelper.describe_to_resource_hash(query, sparql_client)
|
307
|
+
#TODO: test whether endpoint supports json, and if not, switch to parsing XML
|
308
|
+
resp = sparql_client.describe(query, "application/json")
|
309
|
+
if resp.status != 200
|
310
|
+
raise "Error performing sparql query: #{resp.status} #{resp.reason}\n#{resp.content}"
|
311
|
+
end
|
312
|
+
return Pho::ResourceHash::Converter.parse_json( resp.content )
|
313
|
+
end
|
314
|
+
|
315
|
+
#DESCRIBE multiple resources in a single SPARQL request
|
316
|
+
#
|
317
|
+
# uris:: an array of URIs
|
318
|
+
# sparql_client:: a configured SparqlClient objec
|
319
|
+
def SparqlHelper.multi_describe(uris, sparql_client)
|
320
|
+
#TODO: test whether endpoint supports json, and if not, switch to parsing XML
|
321
|
+
resp = sparql_client.multi_describe(uris, "application/json")
|
322
|
+
if resp.status != 200
|
323
|
+
raise "Error performing sparql query: #{resp.status} #{resp.reason}\n#{resp.content}"
|
324
|
+
end
|
325
|
+
return Pho::ResourceHash::Converter.parse_json( resp.content )
|
326
|
+
end
|
327
|
+
|
328
|
+
end
|
329
|
+
|
330
|
+
end
|
331
|
+
|
332
|
+
end
|