Compare commits

..

No commits in common. "959dd6ba01b04e2dd628426f173e808227be0fec" and "8d323c2a8c852c2507ead2400464a7c1e8d00e49" have entirely different histories.

4 changed files with 30 additions and 242 deletions

View File

@ -5,11 +5,6 @@
- RAMOnly: do not read/write the `last_entry` file. - RAMOnly: do not read/write the `last_entry` file.
- Use the `#unsafe_add` function when possible. - Use the `#unsafe_add` function when possible.
# Memory management
- When a value is removed, the related partitions (and tags) may be empty, leaving both an empty array
in memory and a directory on the file-system. Should they be removed?
# Documentation # Documentation
- Write the API documentation. - Write the API documentation.

View File

@ -16,8 +16,6 @@ describe "DODB::DataBase" do
end end
db.to_a.sort.should eq(Ship.all_ships.sort) db.to_a.sort.should eq(Ship.all_ships.sort)
db.rm_storage_dir
end end
it "rewrite already stored data" do it "rewrite already stored data" do
@ -30,8 +28,6 @@ describe "DODB::DataBase" do
db[key] = ship db[key] = ship
db[key].should eq(ship) db[key].should eq(ship)
db.rm_storage_dir
end end
it "properly remove data" do it "properly remove data" do
@ -53,8 +49,6 @@ describe "DODB::DataBase" do
db[i]?.should be_nil db[i]?.should be_nil
end end
db.rm_storage_dir
end end
it "preserves data on reopening" do it "preserves data on reopening" do
@ -67,9 +61,6 @@ describe "DODB::DataBase" do
db2 << Ship.mutsuki db2 << Ship.mutsuki
db1.to_a.size.should eq(2) db1.to_a.size.should eq(2)
db1.rm_storage_dir
db2.rm_storage_dir
end end
it "iterates in normal and reversed order" do it "iterates in normal and reversed order" do
@ -90,8 +81,6 @@ describe "DODB::DataBase" do
# Actual reversal is tested here. # Actual reversal is tested here.
db.to_a(reversed: true).should eq db.to_a.reverse db.to_a(reversed: true).should eq db.to_a.reverse
db.rm_storage_dir
end end
it "respects the provided offsets if any" do it "respects the provided offsets if any" do
@ -108,8 +97,6 @@ describe "DODB::DataBase" do
db.to_a(offset: 0, limit: 3).should eq [ db.to_a(offset: 0, limit: 3).should eq [
Ship.mutsuki, Ship.kisaragi, Ship.yayoi Ship.mutsuki, Ship.kisaragi, Ship.yayoi
] ]
db.rm_storage_dir
end end
end end
@ -126,8 +113,6 @@ describe "DODB::DataBase" do
Ship.all_ships.each_with_index 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
db.rm_storage_dir
end end
it "raise on index overload" do it "raise on index overload" do
@ -142,8 +127,6 @@ describe "DODB::DataBase" do
expect_raises(DODB::IndexOverload) do expect_raises(DODB::IndexOverload) do
db << Ship.kisaragi db << Ship.kisaragi
end end
db.rm_storage_dir
end end
it "properly deindex" do it "properly deindex" do
@ -162,8 +145,6 @@ describe "DODB::DataBase" do
Ship.all_ships.each do |ship| Ship.all_ships.each do |ship|
db_ships_by_name.get?(ship.name).should be_nil db_ships_by_name.get?(ship.name).should be_nil
end end
db.rm_storage_dir
end end
it "properly reindex" do it "properly reindex" do
@ -182,8 +163,6 @@ describe "DODB::DataBase" do
db[key].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)
db.rm_storage_dir
end end
it "properly updates" do it "properly updates" do
@ -204,8 +183,6 @@ describe "DODB::DataBase" do
db_ships_by_name.get?("Kisaragi").should be_nil db_ships_by_name.get?("Kisaragi").should be_nil
db_ships_by_name.get?(new_kisaragi.name).should eq new_kisaragi db_ships_by_name.get?(new_kisaragi.name).should eq new_kisaragi
db.rm_storage_dir
end end
end end
@ -236,8 +213,6 @@ describe "DODB::DataBase" do
end end
db_ships_by_class.get?("does-not-exist").should be_nil db_ships_by_class.get?("does-not-exist").should be_nil
db.rm_storage_dir
end end
it "removes select elements from partitions" do it "removes select elements from partitions" do
@ -256,8 +231,6 @@ describe "DODB::DataBase" do
partition.any?(&.name.==("Kisaragi")).should be_false partition.any?(&.name.==("Kisaragi")).should be_false
end end
db.rm_storage_dir
end end
end end
@ -281,8 +254,6 @@ describe "DODB::DataBase" do
# There shouldnt be one in our data about WWII Japanese warships… # There shouldnt be one in our data about WWII Japanese warships…
db_ships_by_tags.get?("starship").should be_nil db_ships_by_tags.get?("starship").should be_nil
db.rm_storage_dir
end end
it "properly removes tags" do it "properly removes tags" do
@ -307,8 +278,6 @@ describe "DODB::DataBase" do
# end # end
db_ships_by_tags.get("flagship").should eq([] of Ship) db_ships_by_tags.get("flagship").should eq([] of Ship)
db.rm_storage_dir
end end
it "gets items that have multiple tags" do it "gets items that have multiple tags" do
@ -328,8 +297,6 @@ describe "DODB::DataBase" do
results = db_ships_by_tags.get(["flagship"]) results = db_ships_by_tags.get(["flagship"])
results.should eq([Ship.yamato]) results.should eq([Ship.yamato])
db.rm_storage_dir
end end
end end
@ -352,8 +319,6 @@ describe "DODB::DataBase" do
results.should eq(ship) results.should eq(ship)
end end
end end
db.rm_storage_dir
end end
end end
@ -375,12 +340,11 @@ describe "DODB::DataBase" do
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.klass).should contain(ship) db_ships_by_class.get(ship.klass).should contain(ship)
end end
db.rm_storage_dir
end end
it "migrates properly" do it "migrates properly" do
old_db = DODB::SpecDataBase(PrimitiveShip).new "-migration-origin" ::FileUtils.rm_rf "test-storage-migration-origin"
old_db = DODB::DataBase(PrimitiveShip).new "test-storage-migration-origin"
old_ships_by_name = old_db.new_index "name", &.name old_ships_by_name = old_db.new_index "name", &.name
old_ships_by_class = old_db.new_partition "class", &.class_name old_ships_by_class = old_db.new_partition "class", &.class_name
@ -420,9 +384,6 @@ describe "DODB::DataBase" do
ship.tags.any?(&.==("name ship")).should be_true if ship.name == ship.klass ship.tags.any?(&.==("name ship")).should be_true if ship.name == ship.klass
end end
old_db.rm_storage_dir
new_db.rm_storage_dir
end end
end end
@ -449,8 +410,6 @@ describe "DODB::DataBase" do
dump = db.to_a dump = db.to_a
dump.size.should eq fork_count * entries_per_fork dump.size.should eq fork_count * entries_per_fork
db.rm_storage_dir
end end
it "works for updating values" do it "works for updating values" do
@ -489,8 +448,6 @@ describe "DODB::DataBase" do
entry.tags.should eq ["updated"] entry.tags.should eq ["updated"]
end end
end end
db.rm_storage_dir
end end
it "does parallel-safe updates" do it "does parallel-safe updates" do
@ -516,8 +473,6 @@ describe "DODB::DataBase" do
processes.each &.wait processes.each &.wait
db_entries_by_name.get("test").klass.should eq((fork_count * entries_per_fork).to_s) db_entries_by_name.get("test").klass.should eq((fork_count * entries_per_fork).to_s)
db.rm_storage_dir
end end
end end
end end

View File

@ -6,10 +6,6 @@ require "./indexer.cr"
# Basic indexes for 1-to-1 relations. # Basic indexes for 1-to-1 relations.
# Uncached version. # Uncached version.
# #
# ```
# cars_by_name = car_database.new_uncached_index "name", &.name
# ```
#
# This index provides a file-system representation, enabling the administrators to # This index provides a file-system representation, enabling the administrators to
# select a value based on its index. The following example presents an index named "id" # select a value based on its index. The following example presents an index named "id"
# with some data indexed by an UUID attribute. # with some data indexed by an UUID attribute.
@ -31,27 +27,16 @@ require "./indexer.cr"
# NOTE: see `CachedIndex` for a cached version, faster for retrieval. # NOTE: see `CachedIndex` for a cached version, faster for retrieval.
# NOTE: for fast operations without fs representation, see `RAMOnlyIndex`. # NOTE: for fast operations without fs representation, see `RAMOnlyIndex`.
class DODB::Index(V) < DODB::Indexer(V) class DODB::Index(V) < DODB::Indexer(V)
# Name of the index, such as *id* or *color* for example.
# This is an arbitrary value, mostly to create the index directory.
#
# NOTE: used for internal operations.
property name : String property name : String
property key_proc : Proc(V, String | NoIndex) | Proc(V, String)
# Procedure to retrieve the index attribute from the value, used for **internal operations**.
property key_proc : Proc(V, String | NoIndex)
# Root database directory, used for **internal operations**.
getter storage_root : String getter storage_root : String
# Reference to the database instance, used for **internal operations**.
@storage : DODB::Storage(V) @storage : DODB::Storage(V)
# To create an index from a database, use `DODB::Storage#new_index` to create # To create an index from a database, use `DODB::Storage#new_index` to create
# a cached index, `DODB::Storage#new_uncached_index` for an uncached index or # a cached index, `DODB::Storage#new_uncached_index` for an uncached index or
# `DODB::Storage#new_RAM_index` for a RAM-only index. # `DODB::Storage#new_RAM_index` for a RAM-only index.
# def initialize(@storage, @storage_root, @name, @key_proc)
# WARNING: this is an internal operation, do not instanciate an index by hand.
def initialize(@storage : DODB::Storage(V), @storage_root : String, @name : String, @key_proc : Proc(V, String | NoIndex))
Dir.mkdir_p indexing_directory Dir.mkdir_p indexing_directory
end end
@ -99,7 +84,7 @@ class DODB::Index(V) < DODB::Indexer(V)
end end
end end
# Gets the key (ex: 343) for an entry in the DB from an indexed value, used for **internal operations**. # Gets the key (ex: 343) for an entry in the DB from an indexed value.
# #
# Reads the link in `db/indices/by_#{name}/<index>`. # Reads the link in `db/indices/by_#{name}/<index>`.
# #
@ -174,7 +159,7 @@ class DODB::Index(V) < DODB::Indexer(V)
yield nil yield nil
end end
# Reads the indexed symlink to find its related key, used for **internal operations**. # Reads the indexed symlink to find its related key.
# #
# For example, for a car indexed by its name: # For example, for a car indexed by its name:
# #
@ -190,8 +175,6 @@ class DODB::Index(V) < DODB::Indexer(V)
# `#get_key_on_fs` reads the *storage/indices/by_name/Corvet* symlink and gets # `#get_key_on_fs` reads the *storage/indices/by_name/Corvet* symlink and gets
# the name of the data file ("000000343") and converts it in an integer, # the name of the data file ("000000343") and converts it in an integer,
# which is the key in the database. # which is the key in the database.
#
# NOTE: used for internal operations.
def get_key_on_fs(index : String) : Int32 def get_key_on_fs(index : String) : Int32
file_path = file_path_index index file_path = file_path_index index
raise MissingEntry.new(@name, index) unless ::File.symlink? file_path raise MissingEntry.new(@name, index) unless ::File.symlink? file_path
@ -284,10 +267,6 @@ end
# Basic indexes for 1-to-1 relations. # Basic indexes for 1-to-1 relations.
# Cached version. # Cached version.
# #
# ```
# cars_by_name = car_database.new_index "name", &.name
# ```
#
# The cache makes this index fast and since the index doesn't contain # The cache makes this index fast and since the index doesn't contain
# the full value but just an attribute and a key, memory usage is still reasonable. # the full value but just an attribute and a key, memory usage is still reasonable.
# #
@ -312,8 +291,7 @@ end
# NOTE: see `Index` for an uncached version, even less memory-hungry. # NOTE: see `Index` for an uncached version, even less memory-hungry.
# NOTE: for fast operations without fs representation, see `RAMOnlyIndex`. # NOTE: for fast operations without fs representation, see `RAMOnlyIndex`.
class DODB::CachedIndex(V) < DODB::Index(V) class DODB::CachedIndex(V) < DODB::Index(V)
# This hash contains the relation between the index key and the data key, used for # This hash contains the relation between the index key and the data key.
# **internal operations**.
# #
# WARNING: used for internal operations, do not change its content or access it directly. # WARNING: used for internal operations, do not change its content or access it directly.
property data = Hash(String, Int32).new property data = Hash(String, Int32).new
@ -334,7 +312,8 @@ class DODB::CachedIndex(V) < DODB::Index(V)
end end
end end
# Clears the cache and removes the `#indexing_directory`. # Clears the cache.
# :inherit:
def nuke_index def nuke_index
super super
data.clear data.clear
@ -351,8 +330,7 @@ class DODB::CachedIndex(V) < DODB::Index(V)
@data[index_key] = key.to_i @data[index_key] = key.to_i
end end
# Removes the index of a value on the file-system as `DODB::Index#deindex` but also from # Removes the index of a value on the file-system as `DODB::Index#deindex` but also from the cache.
# the cache, used for **internal operations**.
# #
# NOTE: used for internal operations. # NOTE: used for internal operations.
def deindex(key, value) def deindex(key, value)
@ -382,10 +360,6 @@ end
# Basic indexes for 1-to-1 relations. # Basic indexes for 1-to-1 relations.
# RAM-only version, no file-system representation. # RAM-only version, no file-system representation.
# #
# ```
# cars_by_name = car_database.new_RAM_index "name", &.name
# ```
#
# Since there is no file-system operations, all the operations are fast. # Since there is no file-system operations, all the operations are fast.
# `DODB::RAMOnlyIndex` enables the flexibility of indexes without a file-system representation # `DODB::RAMOnlyIndex` enables the flexibility of indexes without a file-system representation
# for absolute efficiency. # for absolute efficiency.
@ -423,9 +397,4 @@ class DODB::RAMOnlyIndex(V) < DODB::CachedIndex(V)
raise MissingEntry.new(@name, index) raise MissingEntry.new(@name, index)
end end
end end
# Clears the index.
def nuke_index
data.clear
end
end end

View File

@ -5,10 +5,6 @@ require "./indexer.cr"
# Partitions for 1-to-n relations. # Partitions for 1-to-n relations.
# Uncached version. # Uncached version.
# #
# ```
# cars_by_color = car_database.new_uncached_partition "color", &.color
# ```
#
# This (partition) index provides a file-system representation, enabling the administrators to # This (partition) index provides a file-system representation, enabling the administrators to
# select a value based on its index. The following example presents an index named "color" # select a value based on its index. The following example presents an index named "color"
# with some data indexed by a color attribute. # with some data indexed by a color attribute.
@ -32,35 +28,22 @@ require "./indexer.cr"
# NOTE: see `CachedPartition` for a cached version, faster for retrieval. # NOTE: see `CachedPartition` for a cached version, faster for retrieval.
# NOTE: for fast operations without fs representation, see `RAMOnlyPartition`. # NOTE: for fast operations without fs representation, see `RAMOnlyPartition`.
class DODB::Partition(V) < DODB::Indexer(V) class DODB::Partition(V) < DODB::Indexer(V)
# Name of the index, such as *id* or *color* for example.
# This is an arbitrary value, mostly to create the index directory.
#
# NOTE: used for internal operations.
property name : String property name : String
property key_proc : Proc(V, String | NoIndex) | Proc(V, String)
# Procedure to retrieve the index attribute from the value.
property key_proc : Proc(V, String | NoIndex)
# Root database directory.
getter storage_root : String getter storage_root : String
# Reference to the database instance. # Required to remove an entry in the DB.
@storage : DODB::Storage(V) @storage : DODB::Storage(V)
# To create a partition from a database, use `DODB::Storage#new_partition` to create def initialize(@storage, @storage_root, @name, @key_proc)
# a cached partition, `DODB::Storage#new_uncached_partition` for an uncached partition or
# `DODB::Storage#new_RAM_partition` for a RAM-only partition.
#
# WARNING: this is an internal operation, do not instanciate a partition by hand.
def initialize(@storage : DODB::Storage(V), @storage_root : String, @name : String, @key_proc : Proc(V, String | NoIndex))
::Dir.mkdir_p indexing_directory ::Dir.mkdir_p indexing_directory
end end
def check!(key : String, value : V, old_value : V?) def check!(key, value, old_value)
return true # Partitions dont have collisions or overloads. return true # Partitions dont have collisions or overloads.
end end
def index(key : String, value : V) def index(key, value)
partition = key_proc.call value partition = key_proc.call value
return if partition.is_a? NoIndex return if partition.is_a? NoIndex
@ -71,7 +54,7 @@ class DODB::Partition(V) < DODB::Indexer(V)
::File.symlink get_data_symlink(key), symlink ::File.symlink get_data_symlink(key), symlink
end end
def deindex (key : String, value : V) def deindex(key, value)
partition = key_proc.call value partition = key_proc.call value
return if partition.is_a? NoIndex return if partition.is_a? NoIndex
@ -83,15 +66,7 @@ class DODB::Partition(V) < DODB::Indexer(V)
end end
end end
# Gets data from an indexed value (throws an exception on a missing entry). def get(partition) : Array(V)
#
# ```
# red_cars = cars_by_color.get "red" # No red cars = MissingEntry exception
# ```
#
# WARNING: throws an exception if no value is found.
# NOTE: for a safe version, use `#get?`.
def get(partition : String) : Array(V)
partition_directory = indexing_directory partition partition_directory = indexing_directory partition
raise MissingEntry.new(@name, partition) unless Dir.exists? partition_directory raise MissingEntry.new(@name, partition) unless Dir.exists? partition_directory
@ -104,36 +79,16 @@ class DODB::Partition(V) < DODB::Indexer(V)
r_value r_value
end end
# Safe version of `#get`, gets data and returns a *nil* value in case of def get?(partition) : Array(V)?
# a missing entry instead of an exception.
#
# ```
# red_cars = cars_by_color.get? "red" # No red cars = nil
# ```
def get?(partition : String) : Array(V)?
get partition get partition
rescue MissingEntry rescue MissingEntry
nil nil
end end
# Deletes all entries within the provided partition. def delete(partition)
#
# ```
# cars_by_color.delete "red" # Deletes all red cars
# ```
def delete(partition : String)
delete partition, do true end delete partition, do true end
end end
# Deletes entries within the provided partition and matching the provided block of code.
#
# ```
# # Deletes all red Corvets.
# cars_by_color.delete "red", do |car|
# car.name == "Corvet"
# end
# ```
# TODO: in case the partition is left empty, should the partition's directory be removed?
def delete(partition, &matcher : Proc(V, Bool)) def delete(partition, &matcher : Proc(V, Bool))
partition_directory = indexing_directory partition partition_directory = indexing_directory partition
@ -149,7 +104,6 @@ class DODB::Partition(V) < DODB::Indexer(V)
end end
end end
# :inherit:
def indexing_directory : String def indexing_directory : String
"#{@storage_root}/partitions/by_#{@name}" "#{@storage_root}/partitions/by_#{@name}"
end end
@ -174,10 +128,6 @@ end
# Partitions for 1-to-n relations. # Partitions for 1-to-n relations.
# Cached version. # Cached version.
# #
# ```
# cars_by_color = car_database.new_partition "color", &.color
# ```
#
# This (partition) index provides a file-system representation, enabling the administrators to # This (partition) index provides a file-system representation, enabling the administrators to
# select a value based on its index. The following example presents an index named "color" # select a value based on its index. The following example presents an index named "color"
# with some data indexed by a color attribute. # with some data indexed by a color attribute.
@ -202,22 +152,15 @@ end
# NOTE: see `Partition` for an uncached version, even less memory-hungry. # NOTE: see `Partition` for an uncached version, even less memory-hungry.
# NOTE: for fast operations without fs representation, see `RAMOnlyPartition`. # NOTE: for fast operations without fs representation, see `RAMOnlyPartition`.
class DODB::CachedPartition(V) < DODB::Partition(V) class DODB::CachedPartition(V) < DODB::Partition(V)
# This hash contains the relation between the index key and the data key, used for # This hash contains the relation between the index key and the data keys.
# **internal operations**.
#
# WARNING: used for internal operations, do not change its content or access it directly.
property data = Hash(String, Array(Int32)).new property data = Hash(String, Array(Int32)).new
# Clears the cache and removes the `#indexing_directory`.
def nuke_index def nuke_index
super super
data.clear data.clear
end end
# Indexes the value on the file-system as `DODB::Partition#index` but also puts the index in a cache. def index(key, value)
#
# NOTE: used for internal operations.
def index(key : String, value : V)
partition = key_proc.call value partition = key_proc.call value
return if partition.is_a? NoIndex return if partition.is_a? NoIndex
super(key, value) super(key, value)
@ -232,11 +175,7 @@ class DODB::CachedPartition(V) < DODB::Partition(V)
@data[partition] = array @data[partition] = array
end end
# Removes the index of a value on the file-system as `DODB::Partition#deindex` but also from def deindex(key, value)
# the cache, used for **internal operations**.
#
# NOTE: used for internal operations.
def deindex(key : String, value : V)
partition = key_proc.call value partition = key_proc.call value
return if partition.is_a? NoIndex return if partition.is_a? NoIndex
super(key, value) super(key, value)
@ -247,21 +186,9 @@ class DODB::CachedPartition(V) < DODB::Partition(V)
end end
end end
# Gets a partition entries and the database key for each entry. def get_with_indexes(partition) : Array(Tuple(V, Int32))
#
# ```
# # For example, get all red cars.
# cars_by_color.get_with_indexes "red"
# # Will return something like:
# # [ (@storage[42], 42)
# # , (@storage[91], 91)
# # ]
# # Each tuple is composed of a car and its key in the database.
# ```
def get_with_indexes(partition : String) : Array(Tuple(V, Int32))
r_value = Array(Tuple(V, Int32)).new r_value = Array(Tuple(V, Int32)).new
# In case the partition is cached.
if keys = @data[partition]? if keys = @data[partition]?
keys.each do |data_key| keys.each do |data_key|
r_value << { @storage[data_key], data_key } r_value << { @storage[data_key], data_key }
@ -280,51 +207,11 @@ class DODB::CachedPartition(V) < DODB::Partition(V)
r_value r_value
end end
# Gets a partition entries. def get(partition) : Array(V)
# get_with_indexes(partition).map &.[0]
# ```
# # For example, get all red cars.
# cars_by_color.get "red"
# ```
# NOTE: returns an empty list on empty or non-existing partition.
def get(partition : String) : Array(V)
r_value = Array(V).new
# In case the partition is cached.
if keys = @data[partition]?
keys.each do |data_key|
r_value << @storage[data_key]
end
else
# The keys to put in the partition.
p_value = Array(Int32).new
# Get the key from the database representation on the file-system.
partition_directory = indexing_directory partition
raise MissingEntry.new(@name, partition) unless Dir.exists? partition_directory
Dir.each_child partition_directory do |child|
key = get_key child
r_value << @storage[key]
p_value << key
end end
@data[partition] = p_value def delete(partition, &matcher : Proc(V, Bool))
end
r_value
end
# Deletes entries within the provided partition and matching the provided block of code,
# both from the file-system representation and from the cache.
#
# ```
# # Deletes all red Corvets.
# cars_by_color.delete "red", do |car|
# car.name == "Corvet"
# end
# ```
# TODO: in case the partition is left empty, should the partition be removed from the cache?
def delete(partition : String, &matcher : Proc(V, Bool))
# Use `get_with_indexes` to retrieve data on-disk, if necessary. # Use `get_with_indexes` to retrieve data on-disk, if necessary.
new_partition = get_with_indexes(partition).map(&.[1]).select do |key| new_partition = get_with_indexes(partition).map(&.[1]).select do |key|
item = @storage[key] item = @storage[key]
@ -340,10 +227,6 @@ end
# Partitions for 1-to-n relations. # Partitions for 1-to-n relations.
# RAM-only version. # RAM-only version.
# #
# ```
# cars_by_color = car_database.new_RAM_partition "color", &.color
# ```
#
# Since there is no file-system operations, all the operations are fast. # Since there is no file-system operations, all the operations are fast.
# `DODB::RAMOnlyPartition` enables the flexibility of partitions without a file-system representation. # `DODB::RAMOnlyPartition` enables the flexibility of partitions without a file-system representation.
# Absolute efficiency, exactly as easy to use as the other partition implementations. # Absolute efficiency, exactly as easy to use as the other partition implementations.
@ -353,7 +236,7 @@ end
# NOTE: see `Partition` for an uncached version, even less memory-hungry. # NOTE: see `Partition` for an uncached version, even less memory-hungry.
# NOTE: for an fs representation but still fast for retrieval, see `CachedPartition`. # NOTE: for an fs representation but still fast for retrieval, see `CachedPartition`.
class DODB::RAMOnlyPartition(V) < DODB::CachedPartition(V) class DODB::RAMOnlyPartition(V) < DODB::CachedPartition(V)
def index(key : String, value : V) def index(key, value)
partition = key_proc.call value partition = key_proc.call value
return if partition.is_a? NoIndex return if partition.is_a? NoIndex
@ -367,7 +250,7 @@ class DODB::RAMOnlyPartition(V) < DODB::CachedPartition(V)
@data[partition] = array @data[partition] = array
end end
def deindex(key : String, value : V) def deindex(key, value)
partition = key_proc.call value partition = key_proc.call value
return if partition.is_a? NoIndex return if partition.is_a? NoIndex
@ -377,7 +260,7 @@ class DODB::RAMOnlyPartition(V) < DODB::CachedPartition(V)
end end
end end
def get_with_indexes(partition : String) : Array(Tuple(V, Int32)) def get_with_indexes(partition) : Array(Tuple(V, Int32))
r_value = Array(Tuple(V, Int32)).new r_value = Array(Tuple(V, Int32)).new
if keys = @data[partition]? if keys = @data[partition]?
@ -388,15 +271,6 @@ class DODB::RAMOnlyPartition(V) < DODB::CachedPartition(V)
r_value r_value
end end
# Deletes entries within the provided partition and matching the provided block of code.
#
# ```
# # Deletes all red Corvets.
# cars_by_color.delete "red", do |car|
# car.name == "Corvet"
# end
# ```
# TODO: in case the partition is left empty, should the partition be removed from the cache?
def delete(partition, &matcher : Proc(V, Bool)) def delete(partition, &matcher : Proc(V, Bool))
if keys = @data[partition]? if keys = @data[partition]?
new_partition = keys.select do |key| new_partition = keys.select do |key|
@ -406,9 +280,4 @@ class DODB::RAMOnlyPartition(V) < DODB::CachedPartition(V)
@data[partition] = new_partition @data[partition] = new_partition
end end
end end
# Clears the cache.
def nuke_index
data.clear
end
end end