dodb.cr/src/dodb/tags.cr

175 lines
3.5 KiB
Crystal
Raw Permalink Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

require "file_utils"
class DODB::Tags(V) < DODB::Indexer(V)
property name : String
property key_proc : Proc(V, Array(String))
getter storage_root : String
# Required to remove an entry in the DB.
@storage : DODB::Storage(V)
def initialize(@storage, @storage_root, @name, @key_proc)
::Dir.mkdir_p indexing_directory
end
def check!(key, value, old_value)
return true # Tags dont have collisions or overloads.
end
def index(key, value)
indices = key_proc.call(value)
indices.each do |i|
symlink = get_tagged_entry_path(i, key)
Dir.mkdir_p ::File.dirname symlink
# FIXME: Should not happen anymore. Should we remove this?
::File.delete symlink if ::File.exists? symlink
::File.symlink get_data_symlink(key), symlink
end
end
def deindex(key, value)
indices = key_proc.call(value)
indices.each do |i|
symlink = get_tagged_entry_path(i, key)
begin
::File.delete symlink
rescue File::NotFoundError
end
end
end
def get_with_indice(tag : String) : Array(Tuple(V, Int32))
r_value = Array(Tuple(V, Int32)).new
tag_directory = indexing_directory tag
return r_value unless Dir.exists? tag_directory
Dir.each_child tag_directory do |child|
key = get_key child
r_value << { @storage[key], key }
end
r_value
end
def get_with_indices(keys : Array(String)) : Array(Tuple(V, Int32))
r_value = Array(Tuple(V, Int32)).new
keys.each do |tag|
r_value.concat get_with_indice tag
end
r_value
end
def get(tag : String) : Array(V)
get_with_indice(tag).map &.[0]
end
def get?(tag : String) : Array(V)?
get tag
rescue MissingEntry
nil
end
def get(keys : Array(String)) : Array(V)
get_with_indices(keys.sort).map &.[0]
end
def delete(tag)
delete tag, do true end
end
def delete(tag, &matcher)
tag_directory = indexing_directory tag
return unless Dir.exists? tag_directory
Dir.each_child tag_directory do |child|
key = get_key child
item = @storage[key]
if yield item
@storage.delete key
end
end
end
private def get_key(path : String) : Int32
path.sub(/^.*\//, "").to_i
end
def indexing_directory : String
"#{@storage_root}/tags/by_#{@name}"
end
private def indexing_directory(tag)
"#{indexing_directory}/#{tag}"
end
private def get_tagged_entry_path(tag : String, key : String)
"#{indexing_directory}/#{tag}/#{key}"
end
private def get_data_symlink(key : String)
"../../../data/#{key}"
end
end
class DODB::CachedTags(V) < DODB::Tags(V)
# This hash contains the relation between the index key and the data keys.
property data = Hash(String, Array(Int32)).new
def index(key, value)
super(key, value)
indices = key_proc.call value
indices.each do |tag|
array = if v = @data[tag]?
v
else
Array(Int32).new
end
array << key.to_i
@data[tag] = array
end
end
def deindex(key, value)
super(key, value)
indices = key_proc.call value
indices.each do |tag|
if v = @data[tag]?
v.delete key.to_i
@data[tag] = v
end
end
end
def get_with_indice(tag : String) : Array(Tuple(V, Int32))
r_value = Array(Tuple(V, Int32)).new
if keys = @data[tag]?
keys.each do |data_key|
r_value << { @storage[data_key], data_key }
end
else
# Get the key from the database representation on the file-system.
tag_directory = indexing_directory tag
raise MissingEntry.new(@name, tag) unless Dir.exists? tag_directory
Dir.each_child tag_directory do |child|
r_value << { @storage[get_key child], get_key child }
end
@data[tag] = r_value.map &.[1]
end
r_value
end
end