rdf-blazegraph 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 8e2e77f00c85d5d33fa1138638238040525b0311
4
+ data.tar.gz: d3cc73a2b6bd389a3206999ff1a8105d8764f065
5
+ SHA512:
6
+ metadata.gz: 34f1525cf7ded366446347c4b6721649943f6674d2e0f8bd03b494386cd8876c7671e891a664db3b6a3e282aebbf88eb851bc20a9d4a953aeee817fce236f014
7
+ data.tar.gz: 612f391cff097c9631f03f08b14d4350690a3c7db1bc10703043e3d72f8975d888eb4d5c60bca8df26e7362d15bde483846c19d908be48906e072d51f6a8f102
data/AUTHORS ADDED
@@ -0,0 +1,2 @@
1
+ * Tom Johnson <tom@dp.la>
2
+
@@ -0,0 +1,33 @@
1
+ RDF::Blazegraph
2
+ ================
3
+
4
+ ## Running the Tests
5
+
6
+ ```bash
7
+ $ bundle install
8
+ $ bundle exec rspec
9
+ ```
10
+
11
+ ## Contributing
12
+
13
+ This repository uses [Git Flow](https://github.com/nvie/gitflow) to mange development and release activity. All submissions _must_ be on a feature branch based on the _develop_ branch to ease staging and integration.
14
+
15
+ * Do your best to adhere to the existing coding conventions and idioms.
16
+ * Don't use hard tabs, and don't leave trailing whitespace on any line.
17
+ Before committing, run `git diff --check` to make sure of this.
18
+ * Do document every method you add using YARD annotations. Read the
19
+ [tutorial][YARD-GS] or look at the existing code for examples.
20
+ * Don't touch the `.gemspec` or `VERSION` files. If you need to change them,
21
+ do so on your private branch only.
22
+ * Do feel free to add yourself to the `CREDITS` file and the
23
+ corresponding list in the the `README`. Alphabetical order applies.
24
+ * Don't touch the `AUTHORS` file. If your contributions are significant
25
+ enough, be assured we will eventually add you in there.
26
+ * Do note that in order for us to merge any non-trivial changes (as a rule
27
+ of thumb, additions larger than about 15 lines of code), we need an
28
+ explicit [public domain dedication][PDD] on record from you.
29
+
30
+ ## License
31
+
32
+ This is free and unencumbered public domain software. For more information,
33
+ see <http://unlicense.org/> or the accompanying {file:UNLICENSE} file.
@@ -0,0 +1,24 @@
1
+ This is free and unencumbered software released into the public domain.
2
+
3
+ Anyone is free to copy, modify, publish, use, compile, sell, or
4
+ distribute this software, either in source code form or as a compiled
5
+ binary, for any purpose, commercial or non-commercial, and by any
6
+ means.
7
+
8
+ In jurisdictions that recognize copyright laws, the author or authors
9
+ of this software dedicate any and all copyright interest in the
10
+ software to the public domain. We make this dedication for the benefit
11
+ of the public at large and to the detriment of our heirs and
12
+ successors. We intend this dedication to be an overt act of
13
+ relinquishment in perpetuity of all present and future rights to this
14
+ software under copyright law.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
19
+ IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
20
+ OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
21
+ ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
22
+ OTHER DEALINGS IN THE SOFTWARE.
23
+
24
+ For more information, please refer to <http://unlicense.org/>
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.0.1
@@ -0,0 +1,19 @@
1
+ require 'rdf'
2
+ require 'sparql/client'
3
+ require 'rexml/document'
4
+ require 'net/http/persistent'
5
+
6
+ module RDF
7
+ ##
8
+ # This module bundles tools for working with Blazegraph using RDF.rb.
9
+ #
10
+ # @see http://ruby-rdf.github.io
11
+ # @see http://wiki.blazegraph.com
12
+ module Blazegraph
13
+ VOCAB_BD = RDF::Vocabulary.new('http://www.bigdata.com/rdf#')
14
+ NULL_GRAPH_URI = Blazegraph::VOCAB_BD.nullGraph
15
+
16
+ autoload :Repository, 'rdf/blazegraph/repository'
17
+ autoload :RestClient, 'rdf/blazegraph/rest_client'
18
+ end
19
+ end
@@ -0,0 +1,147 @@
1
+ module RDF::Blazegraph
2
+ ##
3
+ # An RDF::Repository implementaton for Blazegraph (formerly BigData).
4
+ #
5
+ # @see RDF::Repository
6
+ class Repository < SPARQL::Client::Repository
7
+ ##
8
+ # @return [RDF::Blazegraph::RestClient]
9
+ def rest_client
10
+ @rest_client = RestClient.new(@client.url)
11
+ end
12
+
13
+ ##
14
+ # @see RDF::Enumerable#count
15
+ def count
16
+ rest_client.fast_range_count
17
+ end
18
+
19
+ ##
20
+ # @see RDF::Repository#each
21
+ def each(&block)
22
+ rest_client.get_statements.each_statement(&block)
23
+ end
24
+
25
+ ##
26
+ # @see RDF::Repository#empty?
27
+ def empty?
28
+ !rest_client.has_statement?
29
+ end
30
+
31
+ ##
32
+ # @see RDF::Repository#has_predicate?
33
+ def has_context?(context)
34
+ rest_client.has_statement?(context: context)
35
+ end
36
+
37
+ ##
38
+ # @see RDF::Repository#has_predicate?
39
+ def has_predicate?(predicate)
40
+ rest_client.has_statement?(predicate: predicate)
41
+ end
42
+
43
+ ##
44
+ # @see RDF::Repository#has_object?
45
+ def has_object?(object)
46
+ return super if object.node?
47
+ rest_client.has_statement?(object: object)
48
+ end
49
+
50
+ ##
51
+ # @see RDF::Repository#has_triple?
52
+ def has_triple?(triple)
53
+ return super unless triple.find(&:node?).nil?
54
+ rest_client.has_statement?(subject: triple[0],
55
+ predicate: triple[1],
56
+ object: triple[2])
57
+ end
58
+
59
+ ##
60
+ # Calls the Blazegraph API unless a blank node is present, in which case we
61
+ # fall back on SPARQL ASK.
62
+ # @see RDF::Repostiory#has_statement?
63
+ def has_statement?(statement)
64
+ has_quad?(statement)
65
+ end
66
+
67
+ ##
68
+ # @see RDF::Repository#has_subject?
69
+ def has_subject?(subject)
70
+ return super if subject.node?
71
+ rest_client.has_statement?(subject: subject)
72
+ end
73
+
74
+ ##
75
+ # @see RDF::Repository#has_subject?
76
+ def has_quad?(statement)
77
+ statement = RDF::Statement.from(statement)
78
+ rest_client.has_statement?(subject: statement.subject,
79
+ predicate: statement.predicate,
80
+ object: statement.object,
81
+ context: statement.context)
82
+ end
83
+
84
+ ##
85
+ # @see SPARQL::Client::Repository#supports?
86
+ def supports?(feature)
87
+ return true if feature.to_sym == :context
88
+ super
89
+ end
90
+
91
+ protected
92
+
93
+ ##
94
+ # Deletes the given RDF statements from the underlying storage.
95
+ #
96
+ # Overridden here to use SPARQL/UPDATE
97
+ #
98
+ # @param [RDF::Enumerable] statements
99
+ # @return [void]
100
+ def delete_statements(statements)
101
+ @rest_client.delete(statements)
102
+ end
103
+
104
+ ##
105
+ # Queries `self` for RDF statements matching the given `pattern`.
106
+ #
107
+ # @see SPARQL::Client::Repository#query_pattern
108
+ def query_pattern(pattern, &block)
109
+ pattern = pattern.dup
110
+ pattern.subject ||= RDF::Query::Variable.new
111
+ pattern.predicate ||= RDF::Query::Variable.new
112
+ pattern.object ||= RDF::Query::Variable.new
113
+ pattern.initialize!
114
+
115
+ # Blazegraph objects to bnodes shared across the CONSTRUCT & WHERE scopes
116
+ # so we dup the pattern with fresh bnodes
117
+ where_pattern = pattern.dup
118
+ where_pattern.subject = RDF::Node.new if where_pattern.subject.node?
119
+ where_pattern.predicate = RDF::Node.new if where_pattern.predicate.node?
120
+ where_pattern.object = RDF::Node.new if where_pattern.object.node?
121
+ where_pattern.initialize!
122
+
123
+ query = client.construct(pattern).where(where_pattern)
124
+
125
+ unless where_pattern.context.nil?
126
+ where_pattern.context ||= NULL_GRAPH_URI
127
+ query.graph(where_pattern.context)
128
+ query.filter("#{where_pattern.context} != #{NULL_GRAPH_URI.to_base}") if
129
+ where_pattern.context.variable?
130
+ end
131
+
132
+ if block_given?
133
+ query.each_statement(&block)
134
+ else
135
+ query.solutions.to_a.extend(RDF::Enumerable, RDF::Queryable)
136
+ end
137
+ end
138
+
139
+ def insert_statements(statements)
140
+ rest_client.insert(statements)
141
+ end
142
+
143
+ def insert_statement(statement)
144
+ rest_client.insert([statement])
145
+ end
146
+ end
147
+ end
@@ -0,0 +1,219 @@
1
+ module RDF::Blazegraph
2
+ ##
3
+ # A wrapper for the NanoSparqlServer REST API. This implements portions of
4
+ # the API that are not parts of SPARQL; such as optimized queries like
5
+ # FastRangeCount.
6
+ #
7
+ # @see https://wiki.blazegraph.com/wiki/index.php/REST_API NanoSparqlServer
8
+ # REST documentation
9
+ class RestClient
10
+ attr_reader :url
11
+
12
+ ##
13
+ # @param [#to_s] uri a uri identifying the Blazegraph API endpoint; e.g.
14
+ # `http://localhost:9999/bigdata/sparql`
15
+ def initialize(url)
16
+ @http = Net::HTTP::Persistent.new(self.class)
17
+ @url = URI(url.to_s)
18
+ @sparql_client = SPARQL::Client.new(@url)
19
+ end
20
+
21
+ ##
22
+ # Send a request to the server
23
+ #
24
+ # @param [String] query
25
+ #
26
+ # @return [Net::HTTP::Response] the server's response
27
+ # @raise [RequestError] if the request returns a non-success response code
28
+ def execute(query)
29
+ response = @http.request(url + ::URI::encode(query))
30
+
31
+ return response if response.is_a? Net::HTTPSuccess
32
+ raise RequestError.new("#{response.code}: #{response.body}\n" \
33
+ "Processing query #{query}")
34
+ end
35
+
36
+ ##
37
+ # Returns a count of the number of triples in the datastore.
38
+ #
39
+ # We use the `exact` option, to ensure that counts are exact regardless
40
+ # of sharding and other configuration conditions.
41
+ #
42
+ # Errors are thrown if a bnode is given for any of the terms or if a
43
+ # literal is given in the subject or predicate place.
44
+ #
45
+ # @param [Boolean] exact whether to insist on an exact range count, or
46
+ # allow approximations e.g. across multiple shards; default: `true`
47
+ #
48
+ # @return [Integer] the count of triples matching the query
49
+ #
50
+ # @raise [RequestError] if the request is invalid or the server throws an
51
+ # error
52
+ def fast_range_count(subject: nil, predicate: nil, object: nil,
53
+ context: nil, exact: true)
54
+ st_query = access_path_query(subject, predicate, object, context)
55
+ resp = execute("?ESTCARD#{st_query}&exact=#{exact}")
56
+ read_xml_response(resp, :rangeCount).to_i
57
+ end
58
+
59
+ ##
60
+ # Returns statements matching the given pattern.
61
+ #
62
+ # Errors are thrown if a bnode is given for any of the terms or if a
63
+ # literal is given in the subject or predicate place.
64
+ #
65
+ # @param [Boolean] include_inferred includes inferred triples if `true',
66
+ # default: `false`
67
+ #
68
+ # @return [RDF::Enumerable] statements parsed from the server response
69
+ #
70
+ # @raise [RequestError] if the request is invalid or the server throws an
71
+ # error
72
+ def get_statements(subject: nil, predicate: nil, object: nil,
73
+ context: nil, include_inferred: false)
74
+ st_query = access_path_query(subject, predicate, object, context)
75
+ query = "?GETSTMTS#{st_query}&include_inferred=#{include_inferred}"
76
+ read_rdf_response(execute(query))
77
+ end
78
+
79
+ ##
80
+ # Checks for existence of a statement matching the pattern.
81
+ #
82
+ # Errors are thrown if a bnode is given for any of the terms or if a
83
+ # literal is given in the subject or predicate place.
84
+ #
85
+ # @param [Boolean] include_inferred includes inferred triples if `true',
86
+ # default: `false`
87
+ #
88
+ # @return [Boolean] true if statement matching the pattern exists
89
+ #
90
+ # @raise [RequestError] if the request is invalid or the server throws an
91
+ # error
92
+ def has_statement?(subject: nil, predicate: nil, object: nil,
93
+ context: nil, include_inferred: false)
94
+ if [subject, predicate, object].compact.find(&:node?)
95
+ sparql_ask_statement([subject, predicate, object], context)
96
+ else
97
+ st_query = access_path_query(subject, predicate, object, context)
98
+ query = "?HASSTMT#{st_query}&include_inferred=#{include_inferred}"
99
+
100
+ read_boolean_response(execute(query))
101
+ end
102
+ end
103
+
104
+ ##
105
+ # @param [RDF::Enumerable] statements
106
+ #
107
+ # @todo handle blank nodes
108
+ def insert(statements)
109
+ send_post_request(url, statements)
110
+ return self
111
+ end
112
+
113
+ ##
114
+ # @param [RDF::Enumerable] statements
115
+ #
116
+ # @todo handle blank nodes
117
+ def delete(statements)
118
+ return self if statements.empty?
119
+
120
+ statements.map! do |s|
121
+ statement = RDF::Statement.from(s)
122
+ statement.context ||= NULL_GRAPH_URI
123
+ statement
124
+ end
125
+
126
+ constant = statements.all? do |statement|
127
+ !statement.respond_to?(:each_statement) && statement.constant? &&
128
+ !statement.has_blank_nodes?
129
+ end
130
+
131
+ if constant
132
+ send_post_request(url + '?delete', statements)
133
+ else
134
+ # delete with blank nodes
135
+ end
136
+
137
+ return self
138
+ end
139
+
140
+ private
141
+
142
+ def sparql_ask_statement(triple, context)
143
+ triple.map! do |term|
144
+ unless term.nil?
145
+ term.node? ? RDF::Query::Variable.new(term.id) : term
146
+ end
147
+ end
148
+
149
+ query = @sparql_client.ask.where(triple)
150
+ query.graph(context) if context
151
+ triple.each do |term|
152
+ query.filter("isBlank(#{term})") if !term.nil? && term.variable?
153
+ end
154
+
155
+ query.true?
156
+ end
157
+
158
+ def send_post_request(request_url, statements)
159
+ io = StringIO.new
160
+ writer = RDF::Writer.for(:nquads)
161
+
162
+ io.rewind
163
+
164
+ request = Net::HTTP::Post.new(request_url)
165
+ request['Content-Type'] =
166
+ RDF::Writer.for(:nquads).format.content_type.last # use text/x-nquads
167
+ request.body = writer.dump(statements)
168
+
169
+ @http.request(url, request)
170
+ end
171
+
172
+ ##
173
+ # @param [Net::HTTPResponse] response
174
+ # @return [RDF::Enumerable]
175
+ def read_rdf_response(response)
176
+ RDF::Reader.for(content_type: response.content_type).new(response.body)
177
+ end
178
+
179
+ ##
180
+ # @param [Net::HTTPResponse] response
181
+ # @return [RDF::Enumerable]
182
+ def read_boolean_response(response)
183
+ read_xml_response(response, :result) == 'true' ? true : false
184
+ end
185
+
186
+ ##
187
+ # @param [Net::HTTPResponse] response
188
+ # @param [Net::HTTPResponse] attr
189
+ # @return [RDF::Enumerable]
190
+ def read_xml_response(response, attr)
191
+ REXML::Document.new(response.body).root.attribute(attr).value
192
+ end
193
+
194
+ ##
195
+ # @param [RDF::URI, RDF::Literal] subject
196
+ # @param [RDF::URI, RDF::Literal] predicate
197
+ # @param [RDF::URI, RDF::Literal] object
198
+ # @param [RDF::URI, RDF::Literal] context
199
+ #
200
+ # @return [String] a query string for "Access Path Operations"
201
+ #
202
+ # @todo: fail fast when given a literal predicate or a bnode? Currently we
203
+ # try the request on Blazegraph and handle the Net::HTTP response.
204
+ #
205
+ # @see https://wiki.blazegraph.com/wiki/index.php/REST_API#Access_Path_Operations
206
+ def access_path_query(subject, predicate, object, context)
207
+ str = {s: subject, p: predicate, o: object, c: context}.map do |k, v|
208
+ v ? "#{k}=#{v.to_base}" : nil
209
+ end.compact.join('&')
210
+ str.empty? ? str : "&#{str}"
211
+ end
212
+
213
+ public
214
+
215
+ ##
216
+ # An error class to capture non-succesful RestClient responses
217
+ class RequestError < RuntimeError; end
218
+ end
219
+ end
metadata ADDED
@@ -0,0 +1,142 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: rdf-blazegraph
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Tom Johnson
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2015-08-13 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rdf
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.1'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.1'
27
+ - !ruby/object:Gem::Dependency
28
+ name: sparql-client
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '1.1'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '1.1'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rdf-spec
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '1.1'
48
+ - - ">="
49
+ - !ruby/object:Gem::Version
50
+ version: 1.1.13
51
+ type: :development
52
+ prerelease: false
53
+ version_requirements: !ruby/object:Gem::Requirement
54
+ requirements:
55
+ - - "~>"
56
+ - !ruby/object:Gem::Version
57
+ version: '1.1'
58
+ - - ">="
59
+ - !ruby/object:Gem::Version
60
+ version: 1.1.13
61
+ - !ruby/object:Gem::Dependency
62
+ name: rspec
63
+ requirement: !ruby/object:Gem::Requirement
64
+ requirements:
65
+ - - "~>"
66
+ - !ruby/object:Gem::Version
67
+ version: '3.0'
68
+ type: :development
69
+ prerelease: false
70
+ version_requirements: !ruby/object:Gem::Requirement
71
+ requirements:
72
+ - - "~>"
73
+ - !ruby/object:Gem::Version
74
+ version: '3.0'
75
+ - !ruby/object:Gem::Dependency
76
+ name: rspec-its
77
+ requirement: !ruby/object:Gem::Requirement
78
+ requirements:
79
+ - - "~>"
80
+ - !ruby/object:Gem::Version
81
+ version: '1.0'
82
+ type: :development
83
+ prerelease: false
84
+ version_requirements: !ruby/object:Gem::Requirement
85
+ requirements:
86
+ - - "~>"
87
+ - !ruby/object:Gem::Version
88
+ version: '1.0'
89
+ - !ruby/object:Gem::Dependency
90
+ name: yard
91
+ requirement: !ruby/object:Gem::Requirement
92
+ requirements:
93
+ - - "~>"
94
+ - !ruby/object:Gem::Version
95
+ version: '0.8'
96
+ type: :development
97
+ prerelease: false
98
+ version_requirements: !ruby/object:Gem::Requirement
99
+ requirements:
100
+ - - "~>"
101
+ - !ruby/object:Gem::Version
102
+ version: '0.8'
103
+ description: A Blazegraph Repository adapter for RDF.rb
104
+ email: public-rdf-ruby@w3.org
105
+ executables: []
106
+ extensions: []
107
+ extra_rdoc_files: []
108
+ files:
109
+ - AUTHORS
110
+ - README.md
111
+ - UNLICENSE
112
+ - VERSION
113
+ - lib/rdf/blazegraph.rb
114
+ - lib/rdf/blazegraph/repository.rb
115
+ - lib/rdf/blazegraph/rest_client.rb
116
+ homepage: http://ruby-rdf.github.com/
117
+ licenses:
118
+ - Public Domain
119
+ metadata: {}
120
+ post_install_message:
121
+ rdoc_options: []
122
+ require_paths:
123
+ - lib
124
+ - app
125
+ required_ruby_version: !ruby/object:Gem::Requirement
126
+ requirements:
127
+ - - ">="
128
+ - !ruby/object:Gem::Version
129
+ version: 1.9.2
130
+ required_rubygems_version: !ruby/object:Gem::Requirement
131
+ requirements:
132
+ - - ">="
133
+ - !ruby/object:Gem::Version
134
+ version: '0'
135
+ requirements: []
136
+ rubyforge_project:
137
+ rubygems_version: 2.2.1
138
+ signing_key:
139
+ specification_version: 4
140
+ summary: A Blazegraph Repository adapter for RDF.rb
141
+ test_files: []
142
+ has_rdoc: false