class Agama::Graph

Public Class Methods

new(params) click to toggle source

Initialises variables and sets the path

# File lib/agama/graph.rb, line 6
def initialize(params)
  @db_path  = params[:path] || "./"
  @db       = params[:db]
end

Public Instance Methods

clean_edge(edge) click to toggle source
# File lib/agama/graph.rb, line 254
def clean_edge(edge)
  new_edge = {}
  edge.each do |key, value|
    next if (key == :type or key == :from or key == :to or key == :directed)
    new_edge[key] = value
  end

  return new_edge
end
clean_node(node) click to toggle source

Methods to seperate key from the value

# File lib/agama/graph.rb, line 244
def clean_node(node)
  new_node = {}
  node.each do |key, value|
    next if (key == :type or key == :name)
    new_node[key] = value
  end

  return new_node
end
close() click to toggle source

Closes the database connection

# File lib/agama/graph.rb, line 29
def close
  @db.close
end
edge_count(type = nil) click to toggle source
# File lib/agama/graph.rb, line 230
def edge_count(type = nil)
  if type
    value = @db.m_get("edge#{type}")
    if value
      etype = Marshal.load(value)
      etype[:count]
    end
  else
    Marshal.load(@db.m_get("m"))
  end
end
get_edge(edge) click to toggle source

Fetches the value assigned to the edge from the from node to the to node

# File lib/agama/graph.rb, line 148
def get_edge(edge)
  return nil unless edge[:from][:name]
  return nil unless edge[:to][:name]

  #Get the type of the edge
  type = edge[:type] || Config::DEFAULT_TYPE
  edge[:type] = type

  #Check if the edge type exists
  etype = Marshal.load(@db.m_get("edge#{type}")) if @db.m_get("edge#{type}")

  #Integrity check: Check whether the edge direction is not contradictory
  if edge[:directed]
    if etype
      if etype[:directed] != edge[:directed]
        return false
      end
    else
      #If there is no edge of that type then pre-empt the result
      return false
    end
  else
    if etype
      edge[:directed] = etype[:directed]
    else
      #If there is no edge of that type then pre-empt the result
      return false
    end
  end

  #Convert the edge into a Key string and fetch the corresponding data
  key, reverse_key = Keyify.edge(edge)
  value = @db.e_get(key)

  if value
    new_edge            = Marshal.load(value)
    new_edge[:from]     = self.get_node(edge[:from])
    new_edge[:to]       = self.get_node(edge[:to])
    new_edge[:type]     = edge[:type]
    new_edge[:directed] = etype[:directed] #Pick direction alone from the meta_db for consistency
    new_edge
  end
end
get_node(node) click to toggle source

Fetches the node requested

# File lib/agama/graph.rb, line 71
def get_node(node)
  return nil unless node[:name]

  #Convert the node into a Key string and fetch the corresponding data
  key = Keyify.node(node)
  value = @db.n_get(key)

  if value
    new_node        = Marshal.load(value)
    new_node[:name] = node[:name]
    new_node[:type] = node[:type] || Config::DEFAULT_TYPE
    new_node
  end
end
neighbours(node) click to toggle source
# File lib/agama/graph.rb, line 192
def neighbours(node)
  traverser = Traverser.new(@db, self)
  traverser.set(:from => node)
end
node_count(type = nil) click to toggle source

Accessors for meta values

# File lib/agama/graph.rb, line 219
def node_count(type = nil)
  if type
    value = @db.m_get("node#{type}")
    if value
      Marshal.load(value)
    end
  else
    Marshal.load(@db.m_get("n"))
  end
end
open() click to toggle source

Opens the database for access

# File lib/agama/graph.rb, line 13
def open
  @db.open(@db_path)

  #Create meta variables if they are absent
  unless @db.m_get("n")
    @db.m_put("n", Marshal.dump(0))             #Total node count
    @db.m_put("m", Marshal.dump(0))             #Total edge count
    @db.m_put("node#{Config::DEFAULT_TYPE}", 
              Marshal.dump(0))                  #Node count for default type
    @db.m_put("edge#{Config::DEFAULT_TYPE}", 
              Marshal.dump({:directed => false, :count => 0})) #Edge count for default type
  end

end
set_edge(edge) click to toggle source

Creates/Updates an edge

# File lib/agama/graph.rb, line 88
def set_edge(edge)
  return false unless edge
  return false unless edge[:from][:name]
  return false unless edge[:to][:name]

  #Get the type of the edge
  type = edge[:type] || Config::DEFAULT_TYPE
  edge[:type] = type

  #Check if the edge type exists
  etype = Marshal.load(@db.m_get("edge#{type}")) if @db.m_get("edge#{type}")

  #Integrity check: Check whether the edge direction is not contradictory
  if edge[:directed]
    if etype
      if etype[:directed] != edge[:directed]
        raise "Edge creation error: edge direction contradicting existing edges"
        return
      end
    end
  else
    if etype
      edge[:directed] = etype[:directed]
    else
      edge[:directed] = false
    end
  end

  #Convert the edge into Key, Reversed Key and Value strings for storage
  key, reverse_key = Keyify.edge(edge)
  value = Marshal.dump(clean_edge(edge))

  #Integrity check: Check if the incident nodes are defined
  unless (self.get_node(edge[:from]) and self.get_node(edge[:to]))
    raise "Edge creation error: node(s) not defined"
    return
  end

  #Check whether the operation is an insert (not an update), if so increment count
  unless @db.e_get(key)
    if etype
      etype[:count] += 1
      @db.m_put("edge#{type}", Marshal.dump(etype))
    else
      @db.m_put("edge#{type}", 
                Marshal.dump({:directed => edge[:directed], :count => 1}))
    end

    #Increment global count
    @db.m_put("m", Marshal.dump(Marshal.load(@db.m_get("m")) + 1))
  end

  #Add the edge and the reversed edge
  if @db.e_put(key, value) and @db.e_put(reverse_key, value)
    return edge
  end
end
set_node(node) click to toggle source

Creates/Updates a node

# File lib/agama/graph.rb, line 35
def set_node(node)
  return nil unless node[:name]

  #Get the type of the node
  type = node[:type] || Config::DEFAULT_TYPE
  node[:type] = type

  #Convert the node into Key and Value strings for storage
  key = Keyify.node(node)
  value = Marshal.dump(self.clean_node(node)) #remove key items from value

  #Check if the node type exists, and if so get its count
  count = Marshal.load(@db.m_get("node#{type}")) if @db.m_get("node#{type}")

  #Check whether the operation is an insert (not an update), if so increment count
  unless @db.n_get(key)
    #Increment type-specific count
    if count
      count += 1
      @db.m_put("node#{type}", Marshal.dump(count))
    else
      @db.m_put("node#{type}", Marshal.dump(1))
    end

    #Increment global count
    @db.m_put("n", Marshal.dump(Marshal.load(@db.m_get("n")) + 1))
  end

  #Store the node
  if @db.n_put(key, value)
    node
  end
end