forked from Baguette/dodb.cr
DataBase(K, V) becomes DataBase(V).
parent
c4030d4179
commit
3f53034b5a
111
spec/test.cr
111
spec/test.cr
|
@ -13,11 +13,16 @@ class Ship
|
||||||
def_clone
|
def_clone
|
||||||
|
|
||||||
property id : String
|
property id : String
|
||||||
property class : String
|
property klass : String
|
||||||
property name : String
|
property name : String
|
||||||
property tags : Array(String)
|
property tags : Array(String)
|
||||||
|
|
||||||
def initialize(@name, @class = "<unknown>", @id = UUID.random.to_s, @tags = [] of String)
|
def initialize(@name, @klass = "<unknown>", @id = UUID.random.to_s, @tags = [] of String)
|
||||||
|
end
|
||||||
|
|
||||||
|
# Makes testing arrays of this class easier.
|
||||||
|
def <=>(other)
|
||||||
|
@name <=> other.name
|
||||||
end
|
end
|
||||||
|
|
||||||
# Common, reusable test data.
|
# Common, reusable test data.
|
||||||
|
@ -54,7 +59,7 @@ class Ship
|
||||||
|
|
||||||
# Equality is true if every property is identical.
|
# Equality is true if every property is identical.
|
||||||
def ==(other)
|
def ==(other)
|
||||||
@id == other.id && @class == other.class && @name == other.name &&
|
@id == other.id && @klass == other.klass && @name == other.name &&
|
||||||
@tags == other.tags
|
@tags == other.tags
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -99,42 +104,42 @@ describe "DODB::DataBase" do
|
||||||
db = DODB::SpecDataBase.new
|
db = DODB::SpecDataBase.new
|
||||||
|
|
||||||
Ship.all_ships.each do |ship|
|
Ship.all_ships.each do |ship|
|
||||||
db[ship.id] = ship
|
db << ship
|
||||||
end
|
end
|
||||||
|
|
||||||
Ship.all_ships.each do |ship|
|
db.to_a.sort.should eq(Ship.all_ships.sort)
|
||||||
db[ship.id].should eq(ship)
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
it "rewrite already stored data" do
|
it "rewrite already stored data" do
|
||||||
db = DODB::SpecDataBase.new
|
db = DODB::SpecDataBase.new
|
||||||
ship = Ship.all_ships[0]
|
ship = Ship.all_ships[0]
|
||||||
|
|
||||||
db[ship.id] = Ship.new "broken", id: ship.id
|
key = db << ship
|
||||||
db[ship.id] = ship
|
|
||||||
|
|
||||||
db[ship.id].should eq(ship)
|
db[key] = Ship.new "broken"
|
||||||
|
db[key] = ship
|
||||||
|
|
||||||
|
db[key].should eq(ship)
|
||||||
end
|
end
|
||||||
|
|
||||||
it "properly remove data" do
|
it "properly remove data" do
|
||||||
db = DODB::SpecDataBase.new
|
db = DODB::SpecDataBase.new
|
||||||
|
|
||||||
Ship.all_ships.each do |ship|
|
Ship.all_ships.each do |ship|
|
||||||
db[ship.id] = ship
|
db << ship
|
||||||
end
|
end
|
||||||
|
|
||||||
Ship.all_ships.each do |ship|
|
Ship.all_ships.each do |ship|
|
||||||
db.delete ship.id
|
db.pop
|
||||||
end
|
end
|
||||||
|
|
||||||
Ship.all_ships.each do |ship|
|
Ship.all_ships.each_with_index do |ship, i|
|
||||||
# FIXME: Should it raise a particular exception?
|
# FIXME: Should it raise a particular exception?
|
||||||
expect_raises DODB::MissingEntry do
|
expect_raises DODB::MissingEntry do
|
||||||
db[ship.id]
|
db[i]
|
||||||
end
|
end
|
||||||
|
|
||||||
db[ship.id]?.should be_nil
|
db[i]?.should be_nil
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -146,10 +151,10 @@ describe "DODB::DataBase" do
|
||||||
db_ships_by_name = db.new_index "name", &.name
|
db_ships_by_name = db.new_index "name", &.name
|
||||||
|
|
||||||
Ship.all_ships.each do |ship|
|
Ship.all_ships.each do |ship|
|
||||||
db[ship.id] = ship
|
db << ship
|
||||||
end
|
end
|
||||||
|
|
||||||
Ship.all_ships.each do |ship|
|
Ship.all_ships.each_with_index do |ship|
|
||||||
db_ships_by_name.get?(ship.name).should eq(ship)
|
db_ships_by_name.get?(ship.name).should eq(ship)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -159,14 +164,12 @@ describe "DODB::DataBase" do
|
||||||
|
|
||||||
db_ships_by_name = db.new_index "name", &.name
|
db_ships_by_name = db.new_index "name", &.name
|
||||||
|
|
||||||
some_ship = Ship.kisaragi
|
db << Ship.kisaragi
|
||||||
|
|
||||||
db[some_ship.id] = some_ship
|
|
||||||
|
|
||||||
# Should not be allowed to store an entry whose “name” field
|
# Should not be allowed to store an entry whose “name” field
|
||||||
# already exists.
|
# already exists.
|
||||||
expect_raises(DODB::IndexOverload) do
|
expect_raises(DODB::IndexOverload) do
|
||||||
db["another id"] = some_ship
|
db << Ship.kisaragi
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -176,11 +179,11 @@ describe "DODB::DataBase" do
|
||||||
db_ships_by_name = db.new_index "name", &.name
|
db_ships_by_name = db.new_index "name", &.name
|
||||||
|
|
||||||
Ship.all_ships.each do |ship|
|
Ship.all_ships.each do |ship|
|
||||||
db[ship.id] = ship
|
db << ship
|
||||||
end
|
end
|
||||||
|
|
||||||
Ship.all_ships.each do |ship|
|
Ship.all_ships.each_with_index do |ship, i|
|
||||||
db.delete ship.id
|
db.delete i
|
||||||
end
|
end
|
||||||
|
|
||||||
Ship.all_ships.each do |ship|
|
Ship.all_ships.each do |ship|
|
||||||
|
@ -193,18 +196,15 @@ describe "DODB::DataBase" do
|
||||||
|
|
||||||
db_ships_by_name = db.new_index "name", &.name
|
db_ships_by_name = db.new_index "name", &.name
|
||||||
|
|
||||||
some_ship = Ship.kisaragi
|
key = db << Ship.kisaragi
|
||||||
|
|
||||||
db[some_ship.id] = some_ship
|
|
||||||
|
|
||||||
# We give the old id to the new ship, to get it replaced in
|
# We give the old id to the new ship, to get it replaced in
|
||||||
# the database.
|
# the database.
|
||||||
some_new_ship = Ship.all_ships[2].clone
|
some_new_ship = Ship.all_ships[2].clone
|
||||||
some_new_ship.id = some_ship.id
|
|
||||||
|
|
||||||
db[some_new_ship.id] = some_new_ship
|
db[key] = some_new_ship
|
||||||
|
|
||||||
db[some_new_ship.id].should eq(some_new_ship)
|
db[key].should eq(some_new_ship)
|
||||||
|
|
||||||
db_ships_by_name.get?(some_new_ship.name).should eq(some_new_ship)
|
db_ships_by_name.get?(some_new_ship.name).should eq(some_new_ship)
|
||||||
end
|
end
|
||||||
|
@ -214,24 +214,24 @@ describe "DODB::DataBase" do
|
||||||
it "do basic partitioning" do
|
it "do basic partitioning" do
|
||||||
db = DODB::SpecDataBase.new
|
db = DODB::SpecDataBase.new
|
||||||
|
|
||||||
db_ships_by_class = db.new_partition "class", &.class
|
db_ships_by_class = db.new_partition "class", &.klass
|
||||||
|
|
||||||
Ship.all_ships.each do |ship|
|
Ship.all_ships.each do |ship|
|
||||||
db[ship.id] = ship
|
db << ship
|
||||||
end
|
end
|
||||||
|
|
||||||
Ship.all_ships.each do |ship|
|
Ship.all_ships.each do |ship|
|
||||||
db_ships_by_class.get(ship.class).should contain(ship)
|
db_ships_by_class.get(ship.klass).should contain(ship)
|
||||||
end
|
end
|
||||||
|
|
||||||
# We extract the possible classes to do test on them.
|
# We extract the possible classes to do test on them.
|
||||||
ship_classes = Ship.all_ships.map(&.class).uniq
|
ship_classes = Ship.all_ships.map(&.klass).uniq
|
||||||
ship_classes.each do |klass|
|
ship_classes.each do |klass|
|
||||||
partition = db_ships_by_class.get klass
|
partition = db_ships_by_class.get klass
|
||||||
|
|
||||||
# A partition on “class” should contain entries that all
|
# A partition on “class” should contain entries that all
|
||||||
# share the same value of “class”.
|
# share the same value of “class”.
|
||||||
partition.map(&.class.==(klass)).reduce { |a, b|
|
partition.map(&.klass.==(klass)).reduce { |a, b|
|
||||||
a && b
|
a && b
|
||||||
}.should be_true
|
}.should be_true
|
||||||
end
|
end
|
||||||
|
@ -245,7 +245,7 @@ describe "DODB::DataBase" do
|
||||||
db_ships_by_tags = db.new_tags "tags", &.tags
|
db_ships_by_tags = db.new_tags "tags", &.tags
|
||||||
|
|
||||||
Ship.all_ships.each do |ship|
|
Ship.all_ships.each do |ship|
|
||||||
db[ship.id] = ship
|
db << ship
|
||||||
end
|
end
|
||||||
|
|
||||||
db_ships_by_tags.get("flagship").should eq([Ship.flagship])
|
db_ships_by_tags.get("flagship").should eq([Ship.flagship])
|
||||||
|
@ -266,13 +266,20 @@ describe "DODB::DataBase" do
|
||||||
db_ships_by_tags = db.new_tags "tags", &.tags
|
db_ships_by_tags = db.new_tags "tags", &.tags
|
||||||
|
|
||||||
Ship.all_ships.each do |ship|
|
Ship.all_ships.each do |ship|
|
||||||
db[ship.id] = ship
|
db << ship
|
||||||
end
|
end
|
||||||
|
|
||||||
# Removing the “flagship” tag, brace for impact.
|
# Removing the “flagship” tag, brace for impact.
|
||||||
flagship = db_ships_by_tags.get("flagship")[0].clone
|
flagship, index = db_ships_by_tags.get_with_indices("flagship")[0]
|
||||||
flagship.tags = [] of String
|
flagship.tags = [] of String
|
||||||
db[flagship.id] = flagship
|
db[index] = flagship
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# ship, index = db_ships_by_tags.update(tag: "flagship") do |ship, index|
|
||||||
|
# ship.tags = [] of String
|
||||||
|
# db[index] = ship
|
||||||
|
# end
|
||||||
|
|
||||||
db_ships_by_tags.get("flagship").should eq([] of Ship)
|
db_ships_by_tags.get("flagship").should eq([] of Ship)
|
||||||
end
|
end
|
||||||
|
@ -283,18 +290,18 @@ describe "DODB::DataBase" do
|
||||||
db = DODB::SpecDataBase.new
|
db = DODB::SpecDataBase.new
|
||||||
|
|
||||||
db_ships_by_name = db.new_index "name", &.name
|
db_ships_by_name = db.new_index "name", &.name
|
||||||
db_ships_by_class = db.new_partition "class", &.class
|
db_ships_by_class = db.new_partition "class", &.klass
|
||||||
db_ships_by_tags = db.new_tags "tags", &.tags
|
db_ships_by_tags = db.new_tags "tags", &.tags
|
||||||
|
|
||||||
Ship.all_ships.each do |ship|
|
Ship.all_ships.each do |ship|
|
||||||
db[ship.id] = ship
|
db << ship
|
||||||
end
|
end
|
||||||
|
|
||||||
db.reindex_everything!
|
db.reindex_everything!
|
||||||
|
|
||||||
Ship.all_ships.each do |ship|
|
Ship.all_ships.each do |ship|
|
||||||
db_ships_by_name.get?(ship.name).should eq(ship)
|
db_ships_by_name.get?(ship.name).should eq(ship)
|
||||||
db_ships_by_class.get(ship.class).should contain(ship)
|
db_ships_by_class.get(ship.klass).should contain(ship)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -305,7 +312,7 @@ describe "DODB::DataBase" do
|
||||||
old_ships_by_class = old_db.new_partition "class", &.class_name
|
old_ships_by_class = old_db.new_partition "class", &.class_name
|
||||||
|
|
||||||
PrimitiveShip.all_ships.each do |ship|
|
PrimitiveShip.all_ships.each do |ship|
|
||||||
old_db[ship.id] = ship
|
old_db << ship
|
||||||
end
|
end
|
||||||
|
|
||||||
# At this point, the “old” DB is filled. Now we need to convert
|
# At this point, the “old” DB is filled. Now we need to convert
|
||||||
|
@ -313,31 +320,31 @@ describe "DODB::DataBase" do
|
||||||
|
|
||||||
new_db = DODB::SpecDataBase.new "-migration-target"
|
new_db = DODB::SpecDataBase.new "-migration-target"
|
||||||
|
|
||||||
new_ships_by_class = new_db.new_partition "class", &.class
|
new_ships_by_name = new_db.new_index "name", &.name
|
||||||
new_ships_by_tags = new_db.new_tags "tags", &.tags
|
new_ships_by_class = new_db.new_partition "class", &.klass
|
||||||
new_ships_by_tags = new_db.new_tags "tags", &.tags
|
new_ships_by_tags = new_db.new_tags "tags", &.tags
|
||||||
|
|
||||||
old_db.each do |id, ship|
|
old_db.each_with_index do |ship, index|
|
||||||
new_ship = Ship.new ship.name,
|
new_ship = Ship.new ship.name,
|
||||||
class: ship.class_name,
|
klass: ship.class_name,
|
||||||
id: ship.id,
|
id: ship.id,
|
||||||
tags: Array(String).new.tap { |tags|
|
tags: Array(String).new.tap { |tags|
|
||||||
tags << "name ship" if ship.name == ship.class_name
|
tags << "name ship" if ship.name == ship.class_name
|
||||||
}
|
}
|
||||||
|
|
||||||
new_db[new_ship.id] = new_ship
|
new_db[index] = new_ship
|
||||||
end
|
end
|
||||||
|
|
||||||
# At this point, the conversion is done, so… we’re making a few
|
# At this point, the conversion is done, so… we’re making a few
|
||||||
# arbitrary tests on the new data.
|
# arbitrary tests on the new data.
|
||||||
|
|
||||||
old_db.each do |old_id, old_ship|
|
old_db.each_with_index do |old_ship, old_index|
|
||||||
ship = new_db[old_id]
|
ship = new_db[old_index]
|
||||||
|
|
||||||
ship.id.should eq(old_ship.id)
|
ship.id.should eq(old_ship.id)
|
||||||
ship.class.should eq(old_ship.class_name)
|
ship.klass.should eq(old_ship.class_name)
|
||||||
|
|
||||||
ship.tags.any?(&.==("name ship")).should be_true if ship.name == ship.class
|
ship.tags.any?(&.==("name ship")).should be_true if ship.name == ship.klass
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
145
src/dodb.cr
145
src/dodb.cr
|
@ -8,6 +8,35 @@ class DODB::DataBase(K, V)
|
||||||
|
|
||||||
def initialize(@directory_name : String)
|
def initialize(@directory_name : String)
|
||||||
Dir.mkdir_p data_path
|
Dir.mkdir_p data_path
|
||||||
|
|
||||||
|
self.last_index = -1
|
||||||
|
end
|
||||||
|
|
||||||
|
private def index_file
|
||||||
|
"#{@directory_name}/last-index"
|
||||||
|
end
|
||||||
|
def last_index : Int32
|
||||||
|
File.read(index_file).to_i
|
||||||
|
end
|
||||||
|
def last_index=(x : Int32)
|
||||||
|
file = File.open(index_file, "w")
|
||||||
|
|
||||||
|
file << stringify_key(x)
|
||||||
|
|
||||||
|
file.close
|
||||||
|
|
||||||
|
x
|
||||||
|
rescue
|
||||||
|
raise Exception.new "could not update index file"
|
||||||
|
end
|
||||||
|
|
||||||
|
def stringify_key(key : Int32)
|
||||||
|
# Negative numbers give strange results with Crystal’s printf.
|
||||||
|
if key >= 0
|
||||||
|
"%010i" % key
|
||||||
|
else
|
||||||
|
key.to_s
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
##
|
##
|
||||||
|
@ -51,48 +80,82 @@ class DODB::DataBase(K, V)
|
||||||
partition.not_nil!.as(DODB::Tags).get name, key
|
partition.not_nil!.as(DODB::Tags).get name, key
|
||||||
end
|
end
|
||||||
|
|
||||||
def []?(key : K) : V?
|
def <<(item : V)
|
||||||
|
index = last_index + 1
|
||||||
|
|
||||||
|
self[index] = item
|
||||||
|
|
||||||
|
self.last_index = index
|
||||||
|
end
|
||||||
|
|
||||||
|
def []?(key : Int32) : V?
|
||||||
self[key]
|
self[key]
|
||||||
rescue MissingEntry
|
rescue MissingEntry
|
||||||
# FIXME: Only rescue JSON and “no such file” errors.
|
# FIXME: Only rescue JSON and “no such file” errors.
|
||||||
return nil
|
return nil
|
||||||
end
|
end
|
||||||
|
|
||||||
def [](key : K) : V
|
def [](key : Int32) : V
|
||||||
raise MissingEntry.new(key) unless ::File.exists? file_path key
|
raise MissingEntry.new(key) unless ::File.exists? file_path key
|
||||||
|
|
||||||
read file_path key
|
read file_path key
|
||||||
end
|
end
|
||||||
|
|
||||||
def []=(key : K, value : V)
|
def []=(index : Int32, value : V)
|
||||||
old_value = self.[key]?
|
old_value = self.[index]?
|
||||||
|
|
||||||
check_collisions! key, value, old_value
|
check_collisions! index, value, old_value
|
||||||
|
|
||||||
# Removes any old indices or partitions pointing to a value about
|
# Removes any old indices or partitions pointing to a value about
|
||||||
# to be replaced.
|
# to be replaced.
|
||||||
if old_value
|
if old_value
|
||||||
remove_partitions key, old_value
|
remove_partitions index, old_value
|
||||||
end
|
end
|
||||||
|
|
||||||
# Avoids corruption in case the application crashes while writing.
|
# Avoids corruption in case the application crashes while writing.
|
||||||
file_path(key).tap do |path|
|
file_path(index).tap do |path|
|
||||||
::File.write "#{path}.new", value.to_json
|
::File.write "#{path}.new", value.to_json
|
||||||
::FileUtils.mv "#{path}.new", path
|
::FileUtils.mv "#{path}.new", path
|
||||||
end
|
end
|
||||||
|
|
||||||
write_partitions key, value
|
write_partitions index, value
|
||||||
|
|
||||||
|
if index > last_index
|
||||||
|
self.last_index = index
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def check_collisions!(key : K, value : V, old_value : V?)
|
def check_collisions!(key : Int32, value : V, old_value : V?)
|
||||||
@indexers.each &.check!(key, value, old_value)
|
@indexers.each &.check!(stringify_key(key), value, old_value)
|
||||||
end
|
end
|
||||||
|
|
||||||
def write_partitions(key : K, value : V)
|
def write_partitions(key : Int32, value : V)
|
||||||
@indexers.each &.index(key, value)
|
@indexers.each &.index(stringify_key(key), value)
|
||||||
end
|
end
|
||||||
|
|
||||||
def delete(key : K)
|
def pop
|
||||||
|
index = last_index
|
||||||
|
|
||||||
|
# Some entries may have been removed. We’ll skip over those.
|
||||||
|
# Not the most efficient if a large number of indices are empty.
|
||||||
|
while index >= 0 && self[index]?.nil?
|
||||||
|
index = index - 1
|
||||||
|
end
|
||||||
|
|
||||||
|
if index < 0
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
|
poped = self[index]
|
||||||
|
|
||||||
|
self.delete index
|
||||||
|
|
||||||
|
last_index = index - 1
|
||||||
|
|
||||||
|
poped
|
||||||
|
end
|
||||||
|
|
||||||
|
def delete(key : Int32)
|
||||||
value = self[key]?
|
value = self[key]?
|
||||||
|
|
||||||
return if value.nil?
|
return if value.nil?
|
||||||
|
@ -108,14 +171,14 @@ class DODB::DataBase(K, V)
|
||||||
value
|
value
|
||||||
end
|
end
|
||||||
|
|
||||||
def remove_partitions(key : K, value : V)
|
def remove_partitions(key : Int32, value : V)
|
||||||
@indexers.each &.deindex(key, value)
|
@indexers.each &.deindex(stringify_key(key), value)
|
||||||
end
|
end
|
||||||
|
|
||||||
##
|
##
|
||||||
# CAUTION: Very slow. Try not to use.
|
# CAUTION: Very slow. Try not to use.
|
||||||
# Can be useful for making dumps or to restore a database, however.
|
# Can be useful for making dumps or to restore a database, however.
|
||||||
def each
|
def each_with_index
|
||||||
dirname = data_path
|
dirname = data_path
|
||||||
Dir.each_child dirname do |child|
|
Dir.each_child dirname do |child|
|
||||||
next if child.match /^\./
|
next if child.match /^\./
|
||||||
|
@ -129,20 +192,36 @@ class DODB::DataBase(K, V)
|
||||||
next
|
next
|
||||||
end
|
end
|
||||||
|
|
||||||
# FIXME: Will only work for String. :(
|
key = child.gsub(/\.json$/, "").to_i
|
||||||
key = child.gsub /\.json$/, ""
|
|
||||||
|
|
||||||
yield key, field
|
yield field, key
|
||||||
|
end
|
||||||
|
end
|
||||||
|
def each
|
||||||
|
each_with_index do |item, index|
|
||||||
|
yield item
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
##
|
##
|
||||||
# CAUTION: Very slow. Try not to use.
|
# CAUTION: Very slow. Try not to use.
|
||||||
def to_h
|
def to_a
|
||||||
hash = ::Hash(K, V).new
|
array = ::Array(V).new
|
||||||
|
|
||||||
each do |key, value|
|
each do |value|
|
||||||
hash[key] = value
|
array << value
|
||||||
|
end
|
||||||
|
|
||||||
|
array
|
||||||
|
end
|
||||||
|
|
||||||
|
##
|
||||||
|
# CAUTION: Very slow. Try not to use.
|
||||||
|
def to_h
|
||||||
|
hash = ::Hash(Int32, V).new
|
||||||
|
|
||||||
|
each_with_index do |element, index|
|
||||||
|
hash[index] = element
|
||||||
end
|
end
|
||||||
|
|
||||||
hash
|
hash
|
||||||
|
@ -152,25 +231,29 @@ class DODB::DataBase(K, V)
|
||||||
"#{@directory_name}/data"
|
"#{@directory_name}/data"
|
||||||
end
|
end
|
||||||
|
|
||||||
private def file_path(key : K)
|
private def file_path(key : Int32)
|
||||||
"#{data_path}/#{key.to_s}.json"
|
"#{data_path}/%010i.json" % key
|
||||||
end
|
end
|
||||||
|
|
||||||
private def read(file_path : String)
|
private def read(file_path : String)
|
||||||
V.from_json ::File.read file_path
|
V.from_json ::File.read file_path
|
||||||
end
|
end
|
||||||
|
|
||||||
|
private def remove_data!
|
||||||
|
FileUtils.rm_rf data_path
|
||||||
|
Dir.mkdir_p data_path
|
||||||
|
end
|
||||||
|
|
||||||
# A very slow operation that removes all indices and then rewrites
|
# A very slow operation that removes all indices and then rewrites
|
||||||
# them all.
|
# them all.
|
||||||
|
# FIXME: Is this really useful in its current form? We should remove the
|
||||||
|
# index directories, not the indices based on our current (and
|
||||||
|
# possiblly different from what’s stored) data.
|
||||||
def reindex_everything!
|
def reindex_everything!
|
||||||
old_data = to_h
|
old_data = to_h
|
||||||
|
|
||||||
old_data.each do |key, value|
|
old_data.each do |index, item|
|
||||||
self.delete key
|
self[index] = item
|
||||||
end
|
|
||||||
|
|
||||||
old_data.each do |key, value|
|
|
||||||
self[key] = value
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
|
|
||||||
class DODB::MissingEntry < Exception
|
class DODB::MissingEntry < Exception
|
||||||
getter index : String?
|
getter index : String?
|
||||||
getter key : String
|
getter key : String | Int32
|
||||||
|
|
||||||
def initialize(@index, @key)
|
def initialize(@index, @key)
|
||||||
super "no entry in index '#{@index}' for key '#{@key}''"
|
super "no entry in index '#{@index}' for key '#{@key}''"
|
||||||
|
|
|
@ -14,7 +14,7 @@ class DODB::Tags(V) < DODB::Indexer(V)
|
||||||
indices = key_proc.call value
|
indices = key_proc.call value
|
||||||
|
|
||||||
indices.each do |index|
|
indices.each do |index|
|
||||||
symlink = get_tagged_entry_path(key.to_s, index)
|
symlink = get_tagged_entry_path(key, index)
|
||||||
|
|
||||||
Dir.mkdir_p ::File.dirname symlink
|
Dir.mkdir_p ::File.dirname symlink
|
||||||
|
|
||||||
|
@ -38,20 +38,27 @@ class DODB::Tags(V) < DODB::Indexer(V)
|
||||||
return true # Tags don’t have collisions or overloads.
|
return true # Tags don’t have collisions or overloads.
|
||||||
end
|
end
|
||||||
|
|
||||||
def get(key) : Array(V)
|
def get_with_indices(key) : Array(Tuple(V, Int32))
|
||||||
r_value = Array(V).new
|
r_value = Array(Tuple(V, Int32)).new
|
||||||
|
|
||||||
partition_directory = "#{get_tag_directory}/#{key}"
|
partition_directory = "#{get_tag_directory}/#{key}"
|
||||||
|
|
||||||
return r_value unless Dir.exists? partition_directory
|
return r_value unless Dir.exists? partition_directory
|
||||||
|
|
||||||
Dir.each_child partition_directory do |child|
|
Dir.each_child partition_directory do |child|
|
||||||
r_value << V.from_json ::File.read "#{partition_directory}/#{child}"
|
r_value << {
|
||||||
|
V.from_json(::File.read("#{partition_directory}/#{child}")),
|
||||||
|
File.basename(child).gsub(/\.json$/, "").to_i
|
||||||
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
r_value
|
r_value
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def get(key) : Array(V)
|
||||||
|
get_with_indices(key).map &.[0]
|
||||||
|
end
|
||||||
|
|
||||||
private def get_tag_directory
|
private def get_tag_directory
|
||||||
"#{@storage_root}/by_tags/by_#{@name}"
|
"#{@storage_root}/by_tags/by_#{@name}"
|
||||||
end
|
end
|
||||||
|
@ -60,7 +67,7 @@ class DODB::Tags(V) < DODB::Indexer(V)
|
||||||
"#{get_tag_directory}/#{index_key}/#{key}.json"
|
"#{get_tag_directory}/#{index_key}/#{key}.json"
|
||||||
end
|
end
|
||||||
|
|
||||||
private def get_data_symlink(key)
|
private def get_data_symlink(key : String)
|
||||||
"../../../data/#{key}.json"
|
"../../../data/#{key}.json"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
Loading…
Reference in New Issue