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
|
Dir.mkdir_p indexing_directory
|
||||||
end
|
end
|
||||||
|
|
||||||
# :inherit:
|
|
||||||
def check!(key : String, value : V, old_value : V?)
|
def check!(key : String, value : V, old_value : V?)
|
||||||
index_key = key_proc.call value
|
index_key = key_proc.call value
|
||||||
|
|
||||||
@ -135,16 +134,17 @@ class DODB::Index(V) < DODB::Indexer(V)
|
|||||||
internal_key = get_key(index).to_s
|
internal_key = get_key(index).to_s
|
||||||
@storage.request_lock internal_key
|
@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 internal_key
|
||||||
@storage.release_lock @name, index
|
@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
|
end
|
||||||
|
|
||||||
# Same as `#safe_get` but doesn't throw an exception on a missing value
|
# 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
|
yield nil
|
||||||
end
|
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
|
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
|
||||||
::File.readlink(file_path).sub(/^.*\//, "").to_i
|
::File.readlink(file_path).sub(/^.*\//, "").to_i
|
||||||
end
|
end
|
||||||
|
|
||||||
def get_with_key(index : String) : Tuple(V, Int32)
|
# Updates a value based on its indexed attribute (which must not have changed).
|
||||||
key = get_key index
|
#
|
||||||
|
# ```
|
||||||
value = @storage[key]
|
# # Update the car "corvet" in the database.
|
||||||
|
# car_by_name.update corvet
|
||||||
{value, key}
|
# ```
|
||||||
end
|
# WARNING: in case the indexed attribute has changed, use `#update(index, value)`.
|
||||||
|
|
||||||
# in case new_value hasn't changed its index
|
|
||||||
def update(new_value : V)
|
def update(new_value : V)
|
||||||
index = key_proc.call new_value
|
index = key_proc.call new_value
|
||||||
|
|
||||||
@ -182,30 +196,59 @@ class DODB::Index(V) < DODB::Indexer(V)
|
|||||||
update index, new_value
|
update index, new_value
|
||||||
end
|
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)
|
def update(index : String, new_value : V)
|
||||||
key = get_key index
|
key = get_key index
|
||||||
|
|
||||||
@storage[key] = new_value
|
@storage[key] = new_value
|
||||||
end
|
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)
|
def update_or_create(new_value : V)
|
||||||
update new_value
|
update new_value
|
||||||
rescue MissingEntry
|
rescue MissingEntry
|
||||||
@storage << new_value
|
@storage << new_value
|
||||||
end
|
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)
|
def update_or_create(index : String, new_value : V)
|
||||||
update index, new_value
|
update index, new_value
|
||||||
rescue MissingEntry
|
rescue MissingEntry
|
||||||
@storage << new_value
|
@storage << new_value
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Deletes a value based on its index.
|
||||||
|
#
|
||||||
|
# ```
|
||||||
|
# # Deletes the car named "Corvet".
|
||||||
|
# car_by_name.delete "Corvet"
|
||||||
|
# ```
|
||||||
def delete(index : String)
|
def delete(index : String)
|
||||||
key = get_key index
|
key = get_key index
|
||||||
|
|
||||||
@storage.delete key
|
@storage.delete key
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# :inherit:
|
||||||
def indexing_directory : String
|
def indexing_directory : String
|
||||||
"#{@storage_root}/indices/by_#{@name}"
|
"#{@storage_root}/indices/by_#{@name}"
|
||||||
end
|
end
|
||||||
@ -215,6 +258,7 @@ class DODB::Index(V) < DODB::Indexer(V)
|
|||||||
"#{indexing_directory}/#{index_key}"
|
"#{indexing_directory}/#{index_key}"
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Creates the relative path to the data from the indexing directory.
|
||||||
private def get_data_symlink_index(key : String)
|
private def get_data_symlink_index(key : String)
|
||||||
"../../data/#{key}"
|
"../../data/#{key}"
|
||||||
end
|
end
|
||||||
@ -247,9 +291,11 @@ end
|
|||||||
# 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.
|
# 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
|
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
|
index_key = key_proc.call value
|
||||||
return if index_key.is_a? NoIndex
|
return if index_key.is_a? NoIndex
|
||||||
|
|
||||||
@ -265,11 +311,16 @@ class DODB::CachedIndex(V) < DODB::Index(V)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Clears the cache.
|
||||||
|
# :inherit:
|
||||||
def nuke_index
|
def nuke_index
|
||||||
super
|
super
|
||||||
data.clear
|
data.clear
|
||||||
end
|
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)
|
def index(key, value)
|
||||||
index_key = key_proc.call value
|
index_key = key_proc.call value
|
||||||
return if index_key.is_a? NoIndex
|
return if index_key.is_a? NoIndex
|
||||||
@ -278,6 +329,9 @@ 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 the cache.
|
||||||
|
#
|
||||||
|
# NOTE: used for internal operations.
|
||||||
def deindex(key, value)
|
def deindex(key, value)
|
||||||
index_key = key_proc.call value
|
index_key = key_proc.call value
|
||||||
return if index_key.is_a? NoIndex
|
return if index_key.is_a? NoIndex
|
||||||
@ -286,8 +340,10 @@ class DODB::CachedIndex(V) < DODB::Index(V)
|
|||||||
@data.delete index_key
|
@data.delete index_key
|
||||||
end
|
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.
|
# 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
|
def get_key(index : String) : Int32
|
||||||
if k = @data[index]?
|
if k = @data[index]?
|
||||||
k
|
k
|
||||||
@ -311,20 +367,28 @@ end
|
|||||||
# NOTE: reasonable amount of memory used since it's just an index.
|
# NOTE: reasonable amount of memory used since it's just an index.
|
||||||
# NOTE: fast for all operations, but no file-system representation.
|
# NOTE: fast for all operations, but no file-system representation.
|
||||||
class DODB::RAMOnlyIndex(V) < DODB::CachedIndex(V)
|
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)
|
def index(key, value)
|
||||||
index_key = key_proc.call value
|
index_key = key_proc.call value
|
||||||
return if index_key.is_a? NoIndex
|
return if index_key.is_a? NoIndex
|
||||||
@data[index_key] = key.to_i
|
@data[index_key] = key.to_i
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Removes the index of a value in RAM, no file-system operation.
|
||||||
|
#
|
||||||
|
# NOTE: used for internal operations.
|
||||||
def deindex(key, value)
|
def deindex(key, value)
|
||||||
index_key = key_proc.call value
|
index_key = key_proc.call value
|
||||||
return if index_key.is_a? NoIndex
|
return if index_key.is_a? NoIndex
|
||||||
@data.delete index_key
|
@data.delete index_key
|
||||||
end
|
end
|
||||||
|
|
||||||
# Get the key (ex: 343) for an entry in the DB.
|
# Gets the key (ex: 343) for an entry in the DB.
|
||||||
# With a RAM only index, the key is necessarily stored in the hash.
|
# With a RAM-only index, the key is necessarily stored in the hash.
|
||||||
|
#
|
||||||
|
# NOTE: used for internal operations.
|
||||||
def get_key(index : String) : Int32
|
def get_key(index : String) : Int32
|
||||||
if k = @data[index]?
|
if k = @data[index]?
|
||||||
k
|
k
|
||||||
|
Loading…
Reference in New Issue
Block a user