class RDF::Blazegraph::RestClient

A wrapper for the NanoSparqlServer REST API. This implements portions of the API that are not parts of SPARQL; such as optimized queries like FastRangeCount.

@see wiki.blazegraph.com/wiki/index.php/REST_API NanoSparqlServer

REST documentation

Attributes

url[R]

Public Class Methods

new(url) click to toggle source

@param [#to_s] uri a uri identifying the Blazegraph API endpoint; e.g.

`http://localhost:9999/bigdata/sparql`
# File lib/rdf/blazegraph/rest_client.rb, line 15
def initialize(url)
  @http = Net::HTTP::Persistent.new(self.class)
  @url = URI(url.to_s)
  @sparql_client = SPARQL::Client.new(@url)
end

Public Instance Methods

clear_statements() click to toggle source

Deletes all statements from the server

# File lib/rdf/blazegraph/rest_client.rb, line 23
def clear_statements
  send_delete_request('')
end
delete(statements) click to toggle source

@param [RDF::Enumerable] statements

@todo handle blank nodes

# File lib/rdf/blazegraph/rest_client.rb, line 130
def delete(statements)
  return self if statements.empty?

  statements.map! do |s| 
    statement = RDF::Statement.from(s).dup
    statement.graph_name ||= NULL_GRAPH_URI 
    statement
  end

  if statements.count == 1 && !statements.first.has_blank_nodes?
    st = statements.first
    query = access_path_query(st.subject, st.predicate, st.object, st.graph_name)
    send_delete_request(query)
  end

  constant = statements.all? do |statement|
    !statement.respond_to?(:each_statement) && statement.constant? &&
      !statement.has_blank_nodes?
  end

  if constant
    send_post_request(url + '?delete', statements)
  else
    # delete with blank nodes
  end

  return self
end
delete_insert(deletes, ins) click to toggle source

@param [RDF::Enumerable<RDF::Statement>, Array<RDF::Statement>] dels @param [RDF::Enumerable<RDF::Statement>, Array<RDF::Statement>] ins

@return [RDF::Blazegraph::RestClient] self

# File lib/rdf/blazegraph/rest_client.rb, line 164
def delete_insert(deletes, ins)
  delete(deletes)
  insert(ins)
end
execute(query) click to toggle source

Send a request to the server

@param [String] query

@return [Net::HTTP::Response] the server’s response @raise [RequestError] if the request returns a non-success response code

# File lib/rdf/blazegraph/rest_client.rb, line 34
def execute(query)
  response = @http.request(url + ::URI::encode(query))

  return response if response.is_a? Net::HTTPSuccess
  raise RequestError.new("#{response.code}: #{response.body}\n" \
                         "Processing query #{query}") 
end
fast_range_count(subject: nil, predicate: nil, object: nil, context: nil, exact: true) click to toggle source

Returns a count of the number of triples in the datastore.

We use the ‘exact` option, to ensure that counts are exact regardless of sharding and other configuration conditions.

Errors are thrown if a bnode is given for any of the terms or if a literal is given in the subject or predicate place.

@param [Boolean] exact whether to insist on an exact range count, or

allow approximations e.g. across multiple shards; default: `true`

@return [Integer] the count of triples matching the query

@raise [RequestError] if the request is invalid or the server throws an

error
# File lib/rdf/blazegraph/rest_client.rb, line 58
def fast_range_count(subject: nil, predicate: nil, object: nil, 
                     context: nil, exact: true)
  st_query = access_path_query(subject, predicate, object, context)
  resp = execute("?ESTCARD#{st_query}&exact=#{exact}")
  read_xml_response(resp, :rangeCount).to_i
end
get_statements(subject: nil, predicate: nil, object: nil, context: nil, include_inferred: false) click to toggle source

Returns statements matching the given pattern.

Errors are thrown if a bnode is given for any of the terms or if a literal is given in the subject or predicate place.

@param [Boolean] include_inferred includes inferred triples if ‘true’,

default: `false`

@return [RDF::Enumerable] statements parsed from the server response

@raise [RequestError] if the request is invalid or the server throws an

error
# File lib/rdf/blazegraph/rest_client.rb, line 78
def get_statements(subject: nil, predicate: nil, object: nil, 
                   context: nil, include_inferred: false)
  st_query = access_path_query(subject, predicate, object, context)
  query = "?GETSTMTS#{st_query}&include_inferred=#{include_inferred}"
  read_rdf_response(execute(query))
end
has_statement?(subject: nil, predicate: nil, object: nil, context: nil, include_inferred: false) click to toggle source

Checks for existence of a statement matching the pattern.

Errors are thrown if a bnode is given for any of the terms or if a literal is given in the subject or predicate place.

@param [Boolean] include_inferred includes inferred triples if ‘true’,

default: `false`

@return [Boolean] true if statement matching the pattern exists

@raise [RequestError] if the request is invalid or the server throws an

error
# File lib/rdf/blazegraph/rest_client.rb, line 98
def has_statement?(subject: nil, predicate: nil, object: nil, 
                   context: nil, include_inferred: false)
  if [subject, predicate, object].compact.find(&:node?)
    sparql_ask_statement([subject, predicate, object], context)
  else
    st_query = access_path_query(subject, predicate, object, context)
    query = "?HASSTMT#{st_query}&include_inferred=#{include_inferred}"

    read_boolean_response(execute(query))
  end
end
insert(statements) click to toggle source

@param [RDF::Enumerable] statements

@raise [ArugmentError] when a statement is invalid @todo handle blank nodes

# File lib/rdf/blazegraph/rest_client.rb, line 115
def insert(statements)
  sts = statements.lazy.map do |st|
    raise ArgumentError, "Invalid statement #{st}" if 
      st.respond_to?(:invalid?) && st.invalid?
    st
  end

  send_post_request(url, sts)
  return self
end

Private Instance Methods

access_path_query(subject, predicate, object, context) click to toggle source

@param [RDF::URI, RDF::Literal] subject @param [RDF::URI, RDF::Literal] predicate @param [RDF::URI, RDF::Literal] object @param [RDF::URI, RDF::Literal] context

@return [String] a query string for “Access Path Operations”

@todo: fail fast when given a literal predicate or a bnode? Currently we

try the request on Blazegraph and handle the Net::HTTP response.

@see wiki.blazegraph.com/wiki/index.php/REST_API#Access_Path_Operations

# File lib/rdf/blazegraph/rest_client.rb, line 238
def access_path_query(subject, predicate, object, context)
  str = {s: subject, p: predicate, o: object, c: context}.map do |k, v|
    v ? "#{k}=#{v.to_base}" : nil
  end.compact.join('&')
  str.empty? ? str : "&#{str}" 
end
read_boolean_response(response) click to toggle source

@param [Net::HTTPResponse] response @return [RDF::Enumerable]

# File lib/rdf/blazegraph/rest_client.rb, line 214
def read_boolean_response(response)
  read_xml_response(response, :result) == 'true' ? true : false
end
read_rdf_response(response) click to toggle source

@param [Net::HTTPResponse] response @return [RDF::Enumerable]

# File lib/rdf/blazegraph/rest_client.rb, line 207
def read_rdf_response(response)
  RDF::Reader.for(content_type: 'application/n-quads').new(response.body)
end
read_xml_response(response, attr) click to toggle source

@param [Net::HTTPResponse] response @param [Net::HTTPResponse] attr @return [RDF::Enumerable]

# File lib/rdf/blazegraph/rest_client.rb, line 222
def read_xml_response(response, attr)
  REXML::Document.new(response.body).root.attribute(attr).value
end
send_delete_request(query) click to toggle source
# File lib/rdf/blazegraph/rest_client.rb, line 197
def send_delete_request(query)
  query = "#{query[1..-1]}" if query.start_with? '&'
  request = Net::HTTP::Delete.new(url + "?#{::URI::encode(query)}")
  
  @http.request(url, request)
end
send_post_request(request_url, statements) click to toggle source
# File lib/rdf/blazegraph/rest_client.rb, line 187
def send_post_request(request_url, statements)
  writer = RDF::Writer.for(:nquads)
  
  request = Net::HTTP::Post.new(request_url)
  request['Content-Type'] = 'text/x-nquads'
  request.body = writer.dump(statements)
  
  @http.request(url, request)
end
sparql_ask_statement(triple, context) click to toggle source
# File lib/rdf/blazegraph/rest_client.rb, line 171
def sparql_ask_statement(triple, context)
  triple.map! do |term|
    unless term.nil?
      term.node? ? RDF::Query::Variable.new(term.id) : term
    end
  end

  query = @sparql_client.ask.where(triple)
  query.graph(context) if context
  triple.each do |term| 
    query.filter("isBlank(#{term})") if !term.nil? && term.variable?
  end

  query.true?
end