forked from Baguette/dodb.cr
Proper replacement of values.
It’s no longer needed to `hash.delete old_data`, `hash[id] = new_data`. Previously existing data are properly de-indexed, then replaced by the new data, which are then properly re-indexed. As you’d expect from a database thingy.master
parent
7a46fa3872
commit
2f00b56f0c
24
src/fs.cr
24
src/fs.cr
|
@ -104,14 +104,22 @@ class FS::Hash(K, V)
|
||||||
end
|
end
|
||||||
|
|
||||||
def []=(key : K, value : V)
|
def []=(key : K, value : V)
|
||||||
# FIXME: Update partitions pointing to previous value (if any)
|
# Removes any old indices or partitions pointing to a value about
|
||||||
|
# to be replaced.
|
||||||
|
self.[key]?.try do |old_value|
|
||||||
|
remove_partitions key, old_value
|
||||||
|
end
|
||||||
|
|
||||||
# avoid corruption in case of crash during file writing
|
# Avoids corruption in case the application crashes while writing.
|
||||||
file_path(key).tap do |path|
|
file_path(key).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
|
||||||
|
end
|
||||||
|
|
||||||
|
def write_partitions(key : K, value : V)
|
||||||
@partitions.each do |index|
|
@partitions.each do |index|
|
||||||
index_key = index.key_proc.call value
|
index_key = index.key_proc.call value
|
||||||
|
|
||||||
|
@ -156,13 +164,20 @@ class FS::Hash(K, V)
|
||||||
def delete(key : K)
|
def delete(key : K)
|
||||||
value = self[key]?
|
value = self[key]?
|
||||||
|
|
||||||
|
return if value.nil?
|
||||||
|
|
||||||
begin
|
begin
|
||||||
::File.delete file_path key
|
::File.delete file_path key
|
||||||
rescue
|
rescue
|
||||||
# FIXME: Only intercept “no such file" errors
|
# FIXME: Only intercept “no such file" errors
|
||||||
end
|
end
|
||||||
|
|
||||||
unless value.nil?
|
remove_partitions key, value
|
||||||
|
|
||||||
|
value
|
||||||
|
end
|
||||||
|
|
||||||
|
def remove_partitions(key : K, value : V)
|
||||||
@partitions.each do |index|
|
@partitions.each do |index|
|
||||||
index_key = index.key_proc.call value
|
index_key = index.key_proc.call value
|
||||||
|
|
||||||
|
@ -189,9 +204,6 @@ class FS::Hash(K, V)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
value
|
|
||||||
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.
|
||||||
|
|
|
@ -0,0 +1,61 @@
|
||||||
|
require "json"
|
||||||
|
require "./src/fs.cr"
|
||||||
|
require "uuid"
|
||||||
|
|
||||||
|
# This test basically works if no data is obtained when fetching "broken"
|
||||||
|
# partitions/indices/tags.
|
||||||
|
|
||||||
|
class Ship
|
||||||
|
JSON.mapping({
|
||||||
|
id: String,
|
||||||
|
class: String,
|
||||||
|
name: String,
|
||||||
|
tags: Array(String)
|
||||||
|
})
|
||||||
|
|
||||||
|
def initialize(@name, @class = @name, @tags = [] of String)
|
||||||
|
@id = UUID.random.to_s
|
||||||
|
end
|
||||||
|
|
||||||
|
getter name
|
||||||
|
getter id
|
||||||
|
end
|
||||||
|
|
||||||
|
ships = FS::Hash(String, Ship).new "test-edit"
|
||||||
|
by_name = ships.new_index "name", &.name
|
||||||
|
by_class = ships.new_partition "class", &.class
|
||||||
|
by_id = ships.new_index "id", &.id
|
||||||
|
by_tags = ships.new_nn_partition "tags", &.tags
|
||||||
|
|
||||||
|
ship = Ship.new "Satsuki", "Mutsuki", tags: ["kuchikukan"]
|
||||||
|
ships[ship.id] = ship
|
||||||
|
|
||||||
|
ship = Ship.new "Mutsuki", "Mutsuki", tags: ["kuchikukan"]
|
||||||
|
ships[ship.id] = ship
|
||||||
|
|
||||||
|
ship = Ship.new "Kisaragi", "broken", tags: ["broken"]
|
||||||
|
kisaragi = ship
|
||||||
|
ships[ship.id] = ship
|
||||||
|
|
||||||
|
ship = Ship.new "Kisaragi", "Mutsuki", tags: ["kuchikukan"]
|
||||||
|
ship.id = kisaragi.id # Overwriting the “broken” Kisaragi entry.
|
||||||
|
ships[ship.id] = ship
|
||||||
|
|
||||||
|
puts "Database entries"
|
||||||
|
ships.each do |id, ship|
|
||||||
|
p "#{ship.name} (#{ship.class}) [#{ship.tags.join ", "}]"
|
||||||
|
end
|
||||||
|
|
||||||
|
no_broken = Array(Array(Ship)).new
|
||||||
|
puts
|
||||||
|
puts "Partitions/indices"
|
||||||
|
pp! ships.get_partition("class", "Mutsuki").map &.name
|
||||||
|
pp! ships.get_nn_partition("tags", "kuchikukan").map &.name
|
||||||
|
|
||||||
|
pp! no_broken << ships.get_partition("class", "broken")
|
||||||
|
pp! no_broken << ships.get_nn_partition("tags", "broken")
|
||||||
|
|
||||||
|
if no_broken.flatten.size > 0
|
||||||
|
puts "ERROR: the test failed"
|
||||||
|
end
|
||||||
|
|
Loading…
Reference in New Issue