class RDF::AllegroGraph::AbstractRepository

Features shared by regular AllegroGraph repositories and by persistent backend sessions.

Note that this class does not interoperate well with the Unix ‘fork` command if you’re using blank nodes. See README.md for details.

Attributes

global_query_options[R]

Public Class Methods

new(resource, options={}) click to toggle source

Create a new AllegroGraph repository adapter.

@param [AllegroGraph::Resource] resource

The underlying 'agraph'-based implementation to wrap.

@private

# File lib/rdf/allegro_graph/abstract_repository.rb, line 37
def initialize(resource, options={})
  @resource = resource
  @resource_writable = options[:writable_repository] || resource
  @blank_nodes = []
  @blank_nodes_to_generate = 8
  @blank_nodes_local_to_server = {}
  @blank_nodes_server_to_local = {}
  self.global_query_options = options[:query]
end

Public Instance Methods

build_query(query_options={}, &block) click to toggle source

Construct an AllegroGraph-specific query.

@param [Hash{Symbol => Object}] query_options @option query_options [true,false,String] :infer

The AllegroGraph inference mode to use.  Defaults to `false`.  The
value `true` is equivalent to `'rdfs++'`.

@yield query @yieldparam [Query] The query to build. Use the Query API to add

patterns and functors.

@yieldreturn [void] @return [Query]

@see Query @see RDF::Query

# File lib/rdf/allegro_graph/abstract_repository.rb, line 300
def build_query(query_options={}, &block)
  Query.new(self, query_options, &block)
end
clear(options = {}) click to toggle source

Clear all statements from the repository.

@param [Hash] options @option options [String] :subject Match a specific subject @option options [String] :predicate Match a specific predicate @option options [String] :object Match a specific object @option options [String] :context Match a specific graph name. @return [void]

# File lib/rdf/allegro_graph/abstract_repository.rb, line 372
def clear(options = {})
  @resource_writable.statements.delete(options)
end
each(&block) click to toggle source

Iterate over all statements in the repository. This is used by RDF::Enumerable as a fallback for handling any unimplemented methods.

@yield [statement] @yieldparam [RDF::Statement] statement @yieldreturn [void] @return [void]

# File lib/rdf/allegro_graph/abstract_repository.rb, line 99
def each(&block)
  query_pattern(RDF::Query::Pattern.new, &block)
end
global_query_options=(options) click to toggle source

Set the global query options that will be used at each request. Current supported options are :offset, :limit and :infer.

@param [Hash] options the options to set

www.franz.com/agraph/support/documentation/current/http-protocol.html#get-post-repo

# File lib/rdf/allegro_graph/abstract_repository.rb, line 64
def global_query_options=(options)
  @global_query_options = filter_query_options(options)
end
has_statement?(statement) click to toggle source

Does the repository contain the specified statement?

@param [RDF::Statement] statement @return [Boolean]

# File lib/rdf/allegro_graph/abstract_repository.rb, line 107
def has_statement?(statement)
  found = @resource.statements.find(statement_to_dict(statement))
  !found.empty?
end
prolog_query(query, query_options={}, &block) click to toggle source

Run a raw Prolog query.

@overload prolog_query(query) {|solution| … }

@yield solution
@yieldparam [RDF::Query::Solution] solution
@yieldreturn [void]
@return [void]

@overload prolog_query(pattern)

@return [Enumerator<RDF::Query::Solution>]

@param [String] query The query to run. @param [Hash{Symbol => Object}] query_options

The query options (see build_query).

@return [void] @note This function returns a single-use Enumerator! If you want to

to treat the results as an array, call `to_a` on it, or you will
re-run the query against the server repeatedly.  This curious
decision is made for consistency with RDF.rb.

@see build_query

# File lib/rdf/allegro_graph/abstract_repository.rb, line 255
def prolog_query(query, query_options={}, &block)
  raw_query(:prolog, query, query_options, &block)
end
serialize(value) click to toggle source

Serialize an RDF::Value for transmission to the server. This is exported for low-level libraries that need to access our serialization and deserialization machinery, which has special-case support for RDF nodes.

@param [RDF::Value,RDF::Query::Variable] value @return [String] @see serialize_prolog

# File lib/rdf/allegro_graph/abstract_repository.rb, line 388
def serialize(value)
  case value
  when RDF::Query::Variable then value.to_s
  when false then nil
  else RDF::NTriples::Writer.serialize(map_to_server(value))
  end
end
serialize_prolog(value) click to toggle source

Serialize an RDF::Value for use in a Prolog expression that will be transmitted to the server.

@param [RDF::Value,RDF::Query::Variable] value @return [String] @see serialize

# File lib/rdf/allegro_graph/abstract_repository.rb, line 402
def serialize_prolog(value)
  case value
  when RDF::AllegroGraph::Query::PrologLiteral then value.to_s
  when RDF::Query::Variable then value.to_s
  else "!#{serialize(value)}"
  end
end
size() click to toggle source

Returns the amount of statements in the repository, as an integer

@return [Integer] the number of statements

# File lib/rdf/allegro_graph/abstract_repository.rb, line 71
def size
  @resource.size
end
sparql_query(query, query_options={}, &block) click to toggle source

Run a raw SPARQL query.

@overload sparql_query(query) {|solution| … }

@yield solution
@yieldparam [RDF::Query::Solution] solution
@yieldreturn [void]
@return [void]

@overload sparql_query(pattern)

@return [Enumerator<RDF::Query::Solution>]

@param [String] query The query to run. @param [Hash{Symbol => Object}] query_options

The query options (see build_query).

@return [void] @note This function returns a single-use Enumerator! If you want to

to treat the results as an array, call `to_a` on it, or you will
re-run the query against the server repeatedly.  This curious
decision is made for consistency with RDF.rb.

@see build_query

# File lib/rdf/allegro_graph/abstract_repository.rb, line 229
def sparql_query(query, query_options={}, &block)
  query_options[:type] = query.split(' ').first.downcase.to_sym unless query.empty?
  raw_query(:sparql, query, query_options, &block)
end
supports?(feature) click to toggle source

Returns true if ‘feature` is supported.

@param [Symbol] feature @return [Boolean]

Calls superclass method
# File lib/rdf/allegro_graph/abstract_repository.rb, line 51
def supports?(feature)
  case feature.to_sym
  when :context then true
  else super
  end
end

Protected Instance Methods

allocate_blank_node() click to toggle source

Allocate an “official” AllegroGraph blank node, which should maintain its identity across requests.

# File lib/rdf/allegro_graph/abstract_repository.rb, line 524
def allocate_blank_node
  if @blank_nodes.empty?
    @blank_nodes = generate_blank_nodes(@blank_nodes_to_generate).reverse
    @blank_nodes_to_generate *= 2
  end
  @blank_nodes.pop
end
blank_node?(value) click to toggle source

Return true if this a blank RDF node.

# File lib/rdf/allegro_graph/abstract_repository.rb, line 510
def blank_node?(value)
  !value.nil? && value.anonymous?
end
delete_statement(statement) click to toggle source

Delete a single statement from the repository.

@param [RDF::Statement] statement @return [void]

# File lib/rdf/allegro_graph/abstract_repository.rb, line 343
def delete_statement(statement)
  # TODO: Do we need to handle invalid statements here by turning them
  # into queries and deleting all matching statements?
  delete_statements([statement])
end
delete_statements(statements) click to toggle source

Delete multiple statements from the repository.

@param [Array<RDF::Statement>] statements @return [void]

# File lib/rdf/allegro_graph/abstract_repository.rb, line 354
def delete_statements(statements)
  json = statements_to_json(statements)
  @resource_writable.request_json(:post, path_writable('statements/delete'),
                     :body => json, :expected_status_code => 204)
end
filter_query_options(options) click to toggle source

@private

# File lib/rdf/allegro_graph/abstract_repository.rb, line 571
def filter_query_options(options)
  options ||= {}
  filtered_options = {}
  [ :limit, :infer, :offset ].each do |key|
    filtered_options.merge! key => options[key] if options.has_key?(key)
  end
  filtered_options
end
generate_blank_nodes(amount) click to toggle source

Ask AllegroGraph to generate a series of blank node IDs.

# File lib/rdf/allegro_graph/abstract_repository.rb, line 515
def generate_blank_nodes(amount)
  response = @resource.request_http(:post, path(:blankNodes),
                                :parameters => { :amount => amount },
                                :expected_status_code => 200)
  response.chomp.split("\n").map {|i| i.gsub(/^_:/, '') }
end
insert_statement(statement) click to toggle source

Insert a single statement into the repository.

@param [RDF::Statement] statement @return [void]

# File lib/rdf/allegro_graph/abstract_repository.rb, line 312
def insert_statement(statement)
  insert_statements([statement])
end
insert_statements(statements) click to toggle source

Insert multiple statements at once.

@param [Array<RDF::Statement>] statements @return [void]

# File lib/rdf/allegro_graph/abstract_repository.rb, line 321
def insert_statements(statements)
  # FIXME: RDF.rb expects duplicate statements to be ignored if
  # inserted into a mutable store, but AllegoGraph allows duplicate
  # statements.  We work around this in our other methods, but we
  # need to either use transactions, find appropriate AllegroGraph
  # documentation, or talk to the RDF.rb folks.
  #
  # A discussion of duplicate RDF statements:
  # http://lists.w3.org/Archives/Public/www-rdf-interest/2004Oct/0091.html
  #
  # Note that specifying deleteDuplicates on repository creation doesn't
  # seem to affect this.
  json = statements_to_json(statements)
  @resource_writable.request_json(:post, path_writable(:statements), :body => json,
                     :expected_status_code => 204)
end
json_to_graph(json) click to toggle source

Convert a JSON triples list to a RDF::Graph object.

# File lib/rdf/allegro_graph/abstract_repository.rb, line 502
def json_to_graph(json)
  statements = json.map {|t| RDF::Statement.new(unserialize(t[0]), unserialize(t[1]), unserialize(t[2]))}
  graph = RDF::Graph.new
  graph.insert_statements(statements)
  graph
end
json_to_query_solutions(json) click to toggle source

Convert a JSON query solution to a list of RDF::Query::Solution objects.

# File lib/rdf/allegro_graph/abstract_repository.rb, line 487
def json_to_query_solutions(json)
  names = json['names'].map {|n| n.to_sym }
  json['values'].map do |match|
    hash = {}
    names.each_with_index do |name, i|
      # TODO: I'd like to include nil values, too, but
      # RDF::Query#execute does not yet do so, so we'll filter them for
      # now.
      hash[name] = unserialize(match[i]) unless match[i].nil?
    end
    RDF::Query::Solution.new(hash)
  end
end
map_blank_node(local_id, server_id) click to toggle source

Create a mapping between a local blank node ID and a server-side blank node ID.

# File lib/rdf/allegro_graph/abstract_repository.rb, line 534
def map_blank_node(local_id, server_id)
  #puts "Mapping #{local_id} -> #{server_id}"
  @blank_nodes_local_to_server[local_id] = server_id
  @blank_nodes_server_to_local[server_id] = local_id
end
map_from_server(value) click to toggle source

Translate this value to a client-specific representation, taking care to handle blank nodes correctly.

# File lib/rdf/allegro_graph/abstract_repository.rb, line 553
def map_from_server(value)
  return value unless blank_node?(value)
  if @blank_nodes_server_to_local.has_key?(value.id)
    RDF::Node.new(@blank_nodes_server_to_local[value.id])
  else
    # We didn't generate this node ID, so we want to pass it back to
    # the server unchanged.
    map_blank_node(value.id, value.id)
    value
  end
end
map_to_server(value) click to toggle source

Translate this value to a server-specific representation, taking care to handle blank nodes correctly.

# File lib/rdf/allegro_graph/abstract_repository.rb, line 542
def map_to_server(value)
  return value unless blank_node?(value)
  unless @blank_nodes_local_to_server.has_key?(value.id)
    new_id = allocate_blank_node
    map_blank_node(value.id, new_id)
  end
  RDF::Node.new(@blank_nodes_local_to_server[value.id])
end
path(relative_path=nil) click to toggle source

Build a repository-relative path.

# File lib/rdf/allegro_graph/abstract_repository.rb, line 414
def path(relative_path=nil)
  if relative_path
    "#{@resource.path}/#{relative_path}"
  else
    @resource.path
  end
end
path_writable(relative_path=nil) click to toggle source

Build a repository-relative path for the writable mirror

# File lib/rdf/allegro_graph/abstract_repository.rb, line 423
def path_writable(relative_path=nil)
  if relative_path
    "#{@resource_writable.path}/#{relative_path}"
  else
    @resource_writable.path
  end
end
query_execute(query, &block) click to toggle source

Run an RDF::Query on the server.

@param [RDF::Query] query The query to execute. @yield solution @yieldparam [RDF::Query::Solution] solution @yieldreturn [void]

@see RDF::Queryable#query @see RDF::Query#execute

# File lib/rdf/allegro_graph/abstract_repository.rb, line 189
def query_execute(query, &block)
  query_options =
    if query.respond_to?(:query_options)
      query.query_options
    else
      {}
    end
  if query.respond_to?(:requires_prolog?) && query.requires_prolog?
    prolog_query(query.to_prolog(self), query_options, &block)
  else
    sparql_query(query_to_sparql(query), query_options, &block)
  end
end
query_pattern(pattern) { |statement| ... } click to toggle source

Find all RDF statements matching a pattern.

@overload query_pattern(pattern) {|statement| … }

@yield statement
@yieldparam [RDF::Statement] statement
@yieldreturn [void]
@return [void]

@overload query_pattern(pattern)

@return [Enumerator]

@param [RDF::Query::Pattern] pattern A simple pattern to match. @return [void]

# File lib/rdf/allegro_graph/abstract_repository.rb, line 155
def query_pattern(pattern)
  if block_given?
    seen = {}
    dict = statement_to_dict(pattern)
    dict.delete(:context) if dict[:context] == 'null'
    @resource.statements.find(dict).each do |statement|
      unless seen.has_key?(statement)
        seen[statement] = true
        s,p,o,c = statement.map {|v| unserialize(v) }
        if c.nil?
          yield RDF::Statement.new(s,p,o)
        else
          yield RDF::Statement.new(s,p,o, :context => c)
        end
      end
    end
  else
    enum_for(:query_pattern, pattern)
  end
end
query_to_sparql(query) click to toggle source

Convert a query to SPARQL.

# File lib/rdf/allegro_graph/abstract_repository.rb, line 469
def query_to_sparql(query)
  variables = []
  patterns = []
  query.patterns.each do |p|
    variables.concat(p.variables.values)
    triple = [p.subject, p.predicate, p.object]
    str = triple.map {|v| serialize(v) }.join(" ")
    # TODO: Wrap in graph block for context!
    if p.optional?
      str = "OPTIONAL { #{str} }"
    end
    patterns << "#{str} ."
  end
  "SELECT #{variables.uniq.join(" ")}\nWHERE {\n  #{patterns.join("\n  ")} }"
end
raw_query(language, query, query_options={}) { |s| ... } click to toggle source

Run a raw query in the specified language.

# File lib/rdf/allegro_graph/abstract_repository.rb, line 260
def raw_query(language, query, query_options={}, &block)
  # Build our query parameters.
  params = {
    :query => query,
    :queryLn => language.to_s
  }.merge!(@global_query_options).merge!(filter_query_options(query_options))

  # Run the query and process the results.
  json = @resource.request_json(:get, path, :parameters => params,
                            :expected_status_code => 200)

  # Parse the result (depends on the type of the query)
  if language == :sparql and query_options[:type] == :construct
    results = json_to_graph(json)
  else
    results = json_to_query_solutions(json)
    results = enum_for(:raw_query, language, query) unless block_given?
  end
  if block_given?
    results.each {|s| yield s }
  else
    results
  end
end
statement_to_dict(statement) click to toggle source

Translate a RDF::Statement into a dictionary the we can pass directly to the ‘agraph’ gem.

# File lib/rdf/allegro_graph/abstract_repository.rb, line 456
def statement_to_dict(statement)
  {
    :subject => serialize(statement.subject),
    :predicate => serialize(statement.predicate),
    :object => serialize(statement.object),
    # We have to pass the null context explicitly if we only want
    # to operate a single statement.  Otherwise, we will operate
    # on all matching s,p,o triples regardless of context.
    :context => serialize(statement.context) || 'null'
  }.merge!(@global_query_options)
end
statements_to_json(statements) click to toggle source

Convert a list of statements to a JSON-compatible array.

# File lib/rdf/allegro_graph/abstract_repository.rb, line 446
def statements_to_json(statements)
  statements.map do |s|
    tuple = [s.subject, s.predicate, s.object]
    tuple << s.context if s.context
    tuple.map {|v| serialize(v) }
  end
end
unserialize(str_or_array) click to toggle source

Deserialize an RDF::Value received from the server, or an array of such values when working with Prolog queries.

@param [String,Array] str_or_array

A string, or a possibly-nested array of strings.

@return [RDF::Value] @see serialize

# File lib/rdf/allegro_graph/abstract_repository.rb, line 438
def unserialize(str_or_array)
  case str_or_array
  when Array then str_or_array.map {|v| unserialize(v) }
  else map_from_server(RDF::NTriples::Reader.unserialize(str_or_array))
  end
end