Documentation, again.
This commit is contained in:
parent
78d6db2cc4
commit
3c0e1b7608
@ -3,6 +3,29 @@ require "file_utils"
|
|||||||
require "./exceptions.cr"
|
require "./exceptions.cr"
|
||||||
require "./indexer.cr"
|
require "./indexer.cr"
|
||||||
|
|
||||||
|
# Basic indexes for 1-to-1 relations.
|
||||||
|
# Uncached version.
|
||||||
|
#
|
||||||
|
# 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"
|
||||||
|
# with some data indexed by an UUID attribute.
|
||||||
|
#
|
||||||
|
# ```plain
|
||||||
|
# storage
|
||||||
|
# ├── data
|
||||||
|
# │ ├── 0000000000
|
||||||
|
# │ ├── 0000000001
|
||||||
|
# │ └── 0000000002
|
||||||
|
# ├── indices
|
||||||
|
# │ └── by_id <- this is an example of index named "id"
|
||||||
|
# │ ├── 6e109b82-25de-4250-9c67-e7e8415ad5a7 -> ../../data/0000000000
|
||||||
|
# │ ├── 2080131b-97d7-4300-afa9-55b93cdfd124 -> ../../data/0000000001
|
||||||
|
# │ └── 8b4e83e3-ef95-40dc-a6e5-e6e697ce6323 -> ../../data/0000000002
|
||||||
|
# ```
|
||||||
|
#
|
||||||
|
# NOTE: no cache, thus considered as *slow* for creation, deletion **and retrieval**.
|
||||||
|
# NOTE: see `CachedIndex` for a cached version, faster for retrieval.
|
||||||
|
# NOTE: for fast operations without fs representation, see `RAMOnlyIndex`.
|
||||||
class DODB::Index(V) < DODB::Indexer(V)
|
class DODB::Index(V) < DODB::Indexer(V)
|
||||||
property name : String
|
property name : String
|
||||||
property key_proc : Proc(V, String | NoIndex) | Proc(V, String)
|
property key_proc : Proc(V, String | NoIndex) | Proc(V, String)
|
||||||
@ -10,11 +33,15 @@ class DODB::Index(V) < DODB::Indexer(V)
|
|||||||
|
|
||||||
@storage : DODB::Storage(V)
|
@storage : DODB::Storage(V)
|
||||||
|
|
||||||
|
# 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
|
||||||
|
# `DODB::Storage#new_RAM_index` for a RAM-only index.
|
||||||
def initialize(@storage, @storage_root, @name, @key_proc)
|
def initialize(@storage, @storage_root, @name, @key_proc)
|
||||||
Dir.mkdir_p indexing_directory
|
Dir.mkdir_p indexing_directory
|
||||||
end
|
end
|
||||||
|
|
||||||
def check!(key, value, old_value)
|
# :inherit:
|
||||||
|
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
|
||||||
@ -33,7 +60,8 @@ class DODB::Index(V) < DODB::Indexer(V)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def index(key, value)
|
# :inherit:
|
||||||
|
def index(key : String, 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
|
||||||
|
|
||||||
@ -44,7 +72,8 @@ class DODB::Index(V) < DODB::Indexer(V)
|
|||||||
::File.symlink get_data_symlink_index(key), symlink
|
::File.symlink get_data_symlink_index(key), symlink
|
||||||
end
|
end
|
||||||
|
|
||||||
def deindex(key, value)
|
# :inherit:
|
||||||
|
def deindex (key : String, 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
|
||||||
|
|
||||||
@ -56,23 +85,51 @@ class DODB::Index(V) < DODB::Indexer(V)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
# Get the key (ex: 343) for an entry in the DB.
|
# Gets the key (ex: 343) for an entry in the DB from an indexed value.
|
||||||
# Without caching, it translates to walk the file-system in `db/indices/by_#{name}/<index>`.
|
#
|
||||||
|
# Reads the link in `db/indices/by_#{name}/<index>`.
|
||||||
|
#
|
||||||
|
# Useful for internal purposes, to retrieve a value use `#get`.
|
||||||
|
#
|
||||||
|
# ```
|
||||||
|
# internal_database_key_for_the_corvet = cars_by_name.get_key "Corvet"
|
||||||
|
# ```
|
||||||
|
#
|
||||||
|
# NOTE: used for internal operations.
|
||||||
def get_key(index : String) : Int32
|
def get_key(index : String) : Int32
|
||||||
get_key_on_fs index
|
get_key_on_fs index
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Gets data from an indexed value (throws an exception on a missing entry).
|
||||||
|
#
|
||||||
|
# ```
|
||||||
|
# corvet = cars_by_name.get "Corvet"
|
||||||
|
# ```
|
||||||
|
#
|
||||||
|
# WARNING: throws an exception if the value isn't found.
|
||||||
|
# NOTE: for a safe version, use `#get?`.
|
||||||
def get(index : String) : V
|
def get(index : String) : V
|
||||||
@storage[get_key index]
|
@storage[get_key index]
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Gets data from an indexed value without throwing an exception on a missing entry.
|
||||||
|
#
|
||||||
|
# ```
|
||||||
|
# corvet = cars_by_name.get? "Corvet"
|
||||||
|
# ```
|
||||||
|
#
|
||||||
|
# NOTE: safe version of `#get`, returns a *nil* value in case of a missing entry instead of an exception.
|
||||||
def get?(index : String) : V?
|
def get?(index : String) : V?
|
||||||
get index
|
get index
|
||||||
rescue MissingEntry
|
rescue MissingEntry
|
||||||
nil
|
nil
|
||||||
end
|
end
|
||||||
|
|
||||||
# FIXME: Unlock on exception.
|
# Gets data from an indexed value (thread-safe via two file locks) and gives it to a provided block of code.
|
||||||
|
#
|
||||||
|
# WARNING: should be thread-safe only between other `#safe_get` and `#safe_get?` calls,
|
||||||
|
# index creations and deletions do not use the same locks!
|
||||||
|
# NOTE: on exception, releases all locks.
|
||||||
def safe_get(index : String) : Nil
|
def safe_get(index : String) : Nil
|
||||||
@storage.request_lock @name, index
|
@storage.request_lock @name, index
|
||||||
internal_key = get_key(index).to_s
|
internal_key = get_key(index).to_s
|
||||||
@ -82,8 +139,20 @@ class DODB::Index(V) < DODB::Indexer(V)
|
|||||||
|
|
||||||
@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
|
||||||
|
# (provided block of code receives a *nil* value).
|
||||||
|
#
|
||||||
|
# WARNING: should be thread-safe only between other `#safe_get` and `#safe_get?` calls,
|
||||||
|
# index creations and deletions do not use the same locks!
|
||||||
|
# NOTE: on exception, releases all locks.
|
||||||
def safe_get?(index : String, &block : Proc(V | Nil, Nil)) : Nil
|
def safe_get?(index : String, &block : Proc(V | Nil, Nil)) : Nil
|
||||||
safe_get index, &block
|
safe_get index, &block
|
||||||
rescue MissingEntry
|
rescue MissingEntry
|
||||||
@ -151,6 +220,31 @@ class DODB::Index(V) < DODB::Indexer(V)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Basic indexes for 1-to-1 relations.
|
||||||
|
# Cached version.
|
||||||
|
#
|
||||||
|
# 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.
|
||||||
|
#
|
||||||
|
# A few file-system operations are required on index creation and deletion,
|
||||||
|
# thus this version still is slow for both these operations.
|
||||||
|
#
|
||||||
|
# ```plain
|
||||||
|
# storage
|
||||||
|
# ├── data
|
||||||
|
# │ ├── 0000000000
|
||||||
|
# │ ├── 0000000001
|
||||||
|
# │ └── 0000000002
|
||||||
|
# ├── indices
|
||||||
|
# │ └── by_id <- this is an example of index named "id"
|
||||||
|
# │ ├── 6e109b82-25de-4250-9c67-e7e8415ad5a7 -> ../../data/0000000000
|
||||||
|
# │ ├── 2080131b-97d7-4300-afa9-55b93cdfd124 -> ../../data/0000000001
|
||||||
|
# │ └── 8b4e83e3-ef95-40dc-a6e5-e6e697ce6323 -> ../../data/0000000002
|
||||||
|
# ```
|
||||||
|
#
|
||||||
|
# NOTE: cached, reasonable amount of memory used since it's just an index.
|
||||||
|
# NOTE: fast for retrieval, slow for index creation and deletion (fs operations).
|
||||||
|
# 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.
|
||||||
property data = Hash(String, Int32).new
|
property data = Hash(String, Int32).new
|
||||||
@ -206,9 +300,16 @@ class DODB::CachedIndex(V) < DODB::Index(V)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
# `DODB::RAMOnlyIndex` enables the flexibility of indexes without a file-system representation.
|
# Basic indexes for 1-to-1 relations.
|
||||||
# Absolute efficiency, exactly as easy to use as the other index implementations.
|
# RAM-only version, no file-system representation.
|
||||||
|
#
|
||||||
|
# Since there is no file-system operations, all the operations are fast.
|
||||||
|
# `DODB::RAMOnlyIndex` enables the flexibility of indexes without a file-system representation
|
||||||
|
# for absolute efficiency.
|
||||||
|
# Exactly as easy to use as the other index implementations.
|
||||||
|
#
|
||||||
|
# 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)
|
class DODB::RAMOnlyIndex(V) < DODB::CachedIndex(V)
|
||||||
def index(key, value)
|
def index(key, value)
|
||||||
index_key = key_proc.call value
|
index_key = key_proc.call value
|
||||||
|
@ -2,24 +2,36 @@
|
|||||||
# the indexes (basic indexes, partitions, tags, etc.).
|
# the indexes (basic indexes, partitions, tags, etc.).
|
||||||
abstract class DODB::Indexer(V)
|
abstract class DODB::Indexer(V)
|
||||||
|
|
||||||
# Indexes a value.
|
# Indexes a value, used for **internal operations**.
|
||||||
|
#
|
||||||
|
# NOTE: used for internal operations.
|
||||||
abstract def index (key : String, value : V)
|
abstract def index (key : String, value : V)
|
||||||
|
|
||||||
# Removes the index of a value.
|
# Removes the index of a value, used for **internal operations**.
|
||||||
|
#
|
||||||
|
# NOTE: used for internal operations.
|
||||||
abstract def deindex (key : String, value : V)
|
abstract def deindex (key : String, value : V)
|
||||||
|
|
||||||
# Verifies whether a new value will create a collision with the index of currently stored value.
|
# Verifies whether a new value will create a collision with the index of
|
||||||
|
# currently stored value, used for **internal operations**.
|
||||||
|
#
|
||||||
|
# NOTE: used for internal operations.
|
||||||
abstract def check! (key : String, value : V, old_value : V?)
|
abstract def check! (key : String, value : V, old_value : V?)
|
||||||
|
|
||||||
# Name of the index, such as *id* or *color* for example.
|
# Name of the index, such as *id* or *color* for example.
|
||||||
# This is an arbitrary value, mostly to create the index directory.
|
# This is an arbitrary value, mostly to create the index directory.
|
||||||
|
#
|
||||||
|
# NOTE: used for internal operations.
|
||||||
abstract def name : String
|
abstract def name : String
|
||||||
|
|
||||||
# Directory where the values will be written.
|
# Directory where the values will be written.
|
||||||
|
#
|
||||||
|
# NOTE: used for internal operations.
|
||||||
abstract def indexing_directory : String
|
abstract def indexing_directory : String
|
||||||
|
|
||||||
# Removes all the index entries.
|
# Removes all the index entries, removes the `#indexing_directory` by default.
|
||||||
# By default, removes the `#indexing_directory`.
|
#
|
||||||
|
# NOTE: used for internal operations.
|
||||||
def nuke_index
|
def nuke_index
|
||||||
FileUtils.rm_rf indexing_directory
|
FileUtils.rm_rf indexing_directory
|
||||||
end
|
end
|
||||||
|
Loading…
Reference in New Issue
Block a user