Experimental DODB::DirectedGraph feature.

remotes/1701789573479317192/master
Luka Vandervelden 2020-10-29 04:30:56 +01:00
parent a1d1fd99d4
commit 0599e8bfe5
3 changed files with 182 additions and 1 deletions

View File

@ -159,6 +159,12 @@ abstract class DODB::Storage(V)
partition.not_nil!.as(DODB::Tags).get name, key
end
def new_directed_graph(name : String, index : DODB::Index(V), &block : Proc(V, Array(String))) : DirectedGraph(V)
DirectedGraph(V).new(self, @directory_name, index, name, block).tap do |table|
@indexers << table
end
end
def check_collisions!(key : Int32, value : V, old_value : V?)
@indexers.each &.check!(stringify_key(key), value, old_value)
end

174
src/dodb/directed_graph.cr Normal file
View File

@ -0,0 +1,174 @@
require "file_utils"
require "json"
require "./indexer.cr"
class DODB::DirectedGraph(V) < DODB::Indexer(V)
property name : String
property key_proc : Proc(V, Array(String))
getter storage_root : String
getter index : Index(V)
@storage : DODB::Storage(V)
def initialize(@storage, @storage_root, @index, @name, @key_proc)
::Dir.mkdir_p indexing_directory
end
def check!(key, value, old_value)
return true # Graphs dont have collisions or overloads.
end
def index(key, value)
outgoing_nodes = key_proc.call value
current_node = @index.key_proc.call value
# FIXME: Not 100% completely sure what to do with those.
# Lets ignore them for now and assume theyre undefined
# behavior.
return if current_node.is_a? NoIndex
outgoing_links_directory = get_outgoing_links_directory current_node
# TODO: Update old incoming links, if any, before removing?
::FileUtils.rm_r outgoing_links_directory if ::Dir.exists? outgoing_links_directory
::Dir.mkdir_p outgoing_links_directory
outgoing_nodes.each do |node|
outgoing_symlink = get_outgoing_symlink current_node, node
incoming_symlink = get_incoming_symlink current_node, node
# FIXME: How necessary is this?
::File.delete outgoing_symlink if ::File.exists? outgoing_symlink
::File.delete incoming_symlink if ::File.exists? incoming_symlink
# Updates outgoing links.
::File.symlink get_cross_index_data_symlink(node), outgoing_symlink
# Updates incoming links.
::Dir.mkdir_p get_incoming_links_directory node
::File.symlink get_data_symlink(key), incoming_symlink
end
end
def deindex(key, value)
outgoing_nodes = key_proc.call value
current_node = @index.key_proc.call value
::FileUtils.rm_r get_outgoing_links_directory current_node
outgoing_nodes.each do |node|
symlink = get_incoming_symlink current_node, node
::File.delete symlink if ::File.exists? symlink
end
::FileUtils.rm_r indexing_directory current_node
end
# FIXME: Heavy duplication down below.
# FIXME: references to missing (eg. removed or never added) nodes will
# raise File::NotFoundError. Should be caught and handled.
def get_incoming_values(node)
r_value = Array(V).new
incoming_links_directory = get_incoming_links_directory node
return r_value unless Dir.exists? incoming_links_directory
Dir.each_child incoming_links_directory do |child|
r_value << V.from_json ::File.read "#{incoming_links_directory}/#{child}"
end
r_value
end
def get_incoming_keys(node)
r_value = Array(String).new
incoming_links_directory = get_incoming_links_directory node
return r_value unless Dir.exists? incoming_links_directory
Dir.each_child incoming_links_directory do |child|
r_value << child.sub /.json$/, ""
end
r_value
end
# FIXME: references to missing (eg. removed or never added) nodes will
# raise File::NotFoundError. Should be caught and handled.
def get_outgoing_values(node)
r_value = Array(V).new
outgoing_links_directory = get_outgoing_links_directory node
return r_value unless Dir.exists? outgoing_links_directory
Dir.each_child outgoing_links_directory do |child|
r_value << V.from_json ::File.read "#{outgoing_links_directory}/#{child}"
end
r_value
end
def get_outgoing_keys(node)
r_value = Array(String).new
outgoing_links_directory = get_outgoing_links_directory node
return r_value unless Dir.exists? outgoing_links_directory
Dir.each_child outgoing_links_directory do |child|
r_value << child.sub /.json$/, ""
end
r_value
end
def indexing_directory : String
"#{@storage_root}/graphs/by_#{@name}"
end
private def get_key(path : String) : Int32
::File.readlink(path)
.sub(/\.json$/, "")
.sub(/^.*\//, "")
.to_i
end
private def indexing_directory(node)
"#{indexing_directory}/#{node}"
end
private def get_node_symlink(node : String, key : String)
"#{indexing_directory node}/#{key}.json"
end
private def get_outgoing_links_directory(node)
"#{indexing_directory node}/outgoing"
end
private def get_outgoing_symlink(node, link)
"#{get_outgoing_links_directory node}/#{link}"
end
private def get_incoming_links_directory(node)
"#{indexing_directory node}/incoming"
end
private def get_incoming_symlink(node, link)
"#{get_incoming_links_directory link}/#{node}"
end
private def get_data_symlink(key : String)
"../../../../data/#{key}.json"
end
private def get_cross_index_data_symlink(node : String)
"../../../../../#{@index.file_path_index node}"
end
end

View File

@ -140,7 +140,8 @@ class DODB::Index(V) < DODB::Indexer(V)
"#{@storage_root}/indices/by_#{@name}"
end
private def file_path_index(index_key : String)
# FIXME: Now that its being used outside of this class, name it properly.
def file_path_index(index_key : String)
"#{indexing_directory}/#{index_key}.json"
end