Documentation for basic indexes.
This commit is contained in:
parent
3c0e1b7608
commit
be1ec6a1db
@ -40,7 +40,6 @@ class DODB::Index(V) < DODB::Indexer(V)
|
||||
Dir.mkdir_p indexing_directory
|
||||
end
|
||||
|
||||
# :inherit:
|
||||
def check!(key : String, value : V, old_value : V?)
|
||||
index_key = key_proc.call value
|
||||
|
||||
@ -135,16 +134,17 @@ class DODB::Index(V) < DODB::Indexer(V)
|
||||
internal_key = get_key(index).to_s
|
||||
@storage.request_lock internal_key
|
||||
|
||||
yield get index
|
||||
begin
|
||||
yield get index
|
||||
rescue e
|
||||
# On exception, returns the exception after releasing locks.
|
||||
@storage.release_lock internal_key
|
||||
@storage.release_lock @name, index
|
||||
raise e
|
||||
end
|
||||
|
||||
@storage.release_lock internal_key
|
||||
@storage.release_lock @name, index
|
||||
|
||||
rescue e
|
||||
# On exception, returns the exception after releasing locks.
|
||||
@storage.release_lock internal_key
|
||||
@storage.release_lock @name, index
|
||||
raise e
|
||||
end
|
||||
|
||||
# Same as `#safe_get` but doesn't throw an exception on a missing value
|
||||
@ -159,21 +159,35 @@ class DODB::Index(V) < DODB::Indexer(V)
|
||||
yield nil
|
||||
end
|
||||
|
||||
# Reads the indexed symlink to find its related key.
|
||||
#
|
||||
# For example, for a car indexed by its name:
|
||||
#
|
||||
# ```
|
||||
# storage
|
||||
# ├── data
|
||||
# │ └── 0000000343
|
||||
# └── indices
|
||||
# └── by_name
|
||||
# └── Corvet -> ../../data/0000000343
|
||||
# ```
|
||||
#
|
||||
# `#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,
|
||||
# which is the key in the database.
|
||||
def get_key_on_fs(index : String) : Int32
|
||||
file_path = file_path_index index
|
||||
raise MissingEntry.new(@name, index) unless ::File.symlink? file_path
|
||||
::File.readlink(file_path).sub(/^.*\//, "").to_i
|
||||
end
|
||||
|
||||
def get_with_key(index : String) : Tuple(V, Int32)
|
||||
key = get_key index
|
||||
|
||||
value = @storage[key]
|
||||
|
||||
{value, key}
|
||||
end
|
||||
|
||||
# in case new_value hasn't changed its index
|
||||
# Updates a value based on its indexed attribute (which must not have changed).
|
||||
#
|
||||
# ```
|
||||
# # Update the car "corvet" in the database.
|
||||
# car_by_name.update corvet
|
||||
# ```
|
||||
# WARNING: in case the indexed attribute has changed, use `#update(index, value)`.
|
||||
def update(new_value : V)
|
||||
index = key_proc.call new_value
|
||||
|
||||
@ -182,30 +196,59 @@ class DODB::Index(V) < DODB::Indexer(V)
|
||||
update index, new_value
|
||||
end
|
||||
|
||||
# Updates a value based on its indexed attribute (which may have changed).
|
||||
#
|
||||
# ```
|
||||
# # Update the car "corvet" in the database.
|
||||
# car_by_name.update "Corvet", corvet
|
||||
# ```
|
||||
# NOTE: in case the indexed attribute hasn't changed, you may prefer `#update(value)`.
|
||||
def update(index : String, new_value : V)
|
||||
key = get_key index
|
||||
|
||||
@storage[key] = new_value
|
||||
end
|
||||
|
||||
# Updates a value. Creates it if necessary.
|
||||
#
|
||||
# ```
|
||||
# # Update or create the car "corvet" in the database.
|
||||
# car_by_name.update_or_create corvet
|
||||
# ```
|
||||
# WARNING: use `#update_or_create(index, value)` if the indexed value may have changed.
|
||||
def update_or_create(new_value : V)
|
||||
update new_value
|
||||
rescue MissingEntry
|
||||
@storage << new_value
|
||||
end
|
||||
|
||||
# Same as `#update_or_create(value)` but handles changed indexes.
|
||||
#
|
||||
# ```
|
||||
# # Update or create the car named "Corvet" in the database.
|
||||
# # Its name may have changed in the object "corvet".
|
||||
# car_by_name.update_or_create "Corvet", corvet
|
||||
# ```
|
||||
# NOTE: safe version in case the index has changed.
|
||||
def update_or_create(index : String, new_value : V)
|
||||
update index, new_value
|
||||
rescue MissingEntry
|
||||
@storage << new_value
|
||||
end
|
||||
|
||||
# Deletes a value based on its index.
|
||||
#
|
||||
# ```
|
||||
# # Deletes the car named "Corvet".
|
||||
# car_by_name.delete "Corvet"
|
||||
# ```
|
||||
def delete(index : String)
|
||||
key = get_key index
|
||||
|
||||
@storage.delete key
|
||||
end
|
||||
|
||||
# :inherit:
|
||||
def indexing_directory : String
|
||||
"#{@storage_root}/indices/by_#{@name}"
|
||||
end
|
||||
@ -215,6 +258,7 @@ class DODB::Index(V) < DODB::Indexer(V)
|
||||
"#{indexing_directory}/#{index_key}"
|
||||
end
|
||||
|
||||
# Creates the relative path to the data from the indexing directory.
|
||||
private def get_data_symlink_index(key : String)
|
||||
"../../data/#{key}"
|
||||
end
|
||||
@ -247,9 +291,11 @@ end
|
||||
# NOTE: for fast operations without fs representation, see `RAMOnlyIndex`.
|
||||
class DODB::CachedIndex(V) < DODB::Index(V)
|
||||
# This hash contains the relation between the index key and the data key.
|
||||
#
|
||||
# WARNING: used for internal operations, do not change its content or access it directly.
|
||||
property data = Hash(String, Int32).new
|
||||
|
||||
def check!(key, value, old_value)
|
||||
def check!(key : String, value : V, old_value : V?)
|
||||
index_key = key_proc.call value
|
||||
return if index_key.is_a? NoIndex
|
||||
|
||||
@ -265,11 +311,16 @@ class DODB::CachedIndex(V) < DODB::Index(V)
|
||||
end
|
||||
end
|
||||
|
||||
# Clears the cache.
|
||||
# :inherit:
|
||||
def nuke_index
|
||||
super
|
||||
data.clear
|
||||
end
|
||||
|
||||
# Indexes the value on the file-system as `DODB::Index#index` but also puts the index in a cache.
|
||||
#
|
||||
# NOTE: used for internal operations.
|
||||
def index(key, value)
|
||||
index_key = key_proc.call value
|
||||
return if index_key.is_a? NoIndex
|
||||
@ -278,6 +329,9 @@ class DODB::CachedIndex(V) < DODB::Index(V)
|
||||
@data[index_key] = key.to_i
|
||||
end
|
||||
|
||||
# Removes the index of a value on the file-system as `DODB::Index#deindex` but also from the cache.
|
||||
#
|
||||
# NOTE: used for internal operations.
|
||||
def deindex(key, value)
|
||||
index_key = key_proc.call value
|
||||
return if index_key.is_a? NoIndex
|
||||
@ -286,8 +340,10 @@ class DODB::CachedIndex(V) < DODB::Index(V)
|
||||
@data.delete index_key
|
||||
end
|
||||
|
||||
# Get the key (ex: 343) for an entry in the DB.
|
||||
# Gets the key (ex: 343) for an entry in the DB.
|
||||
# With caching, the key is probably stored in a hash, or we'll search in the FS.
|
||||
#
|
||||
# NOTE: used for internal operations.
|
||||
def get_key(index : String) : Int32
|
||||
if k = @data[index]?
|
||||
k
|
||||
@ -311,20 +367,28 @@ end
|
||||
# NOTE: reasonable amount of memory used since it's just an index.
|
||||
# NOTE: fast for all operations, but no file-system representation.
|
||||
class DODB::RAMOnlyIndex(V) < DODB::CachedIndex(V)
|
||||
# Indexes a value in RAM, no file-system operation.
|
||||
#
|
||||
# NOTE: used for internal operations.
|
||||
def index(key, value)
|
||||
index_key = key_proc.call value
|
||||
return if index_key.is_a? NoIndex
|
||||
@data[index_key] = key.to_i
|
||||
end
|
||||
|
||||
# Removes the index of a value in RAM, no file-system operation.
|
||||
#
|
||||
# NOTE: used for internal operations.
|
||||
def deindex(key, value)
|
||||
index_key = key_proc.call value
|
||||
return if index_key.is_a? NoIndex
|
||||
@data.delete index_key
|
||||
end
|
||||
|
||||
# Get the key (ex: 343) for an entry in the DB.
|
||||
# With a RAM only index, the key is necessarily stored in the hash.
|
||||
# Gets the key (ex: 343) for an entry in the DB.
|
||||
# With a RAM-only index, the key is necessarily stored in the hash.
|
||||
#
|
||||
# NOTE: used for internal operations.
|
||||
def get_key(index : String) : Int32
|
||||
if k = @data[index]?
|
||||
k
|
||||
|
Loading…
Reference in New Issue
Block a user