| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540 |
- /**
- * High-level storage interface and implementation for Implexus.
- *
- * Provides entity persistence operations built on top of the Dbm layer.
- */
- namespace Implexus.Storage {
- /**
- * Configuration for a Category/Index entity.
- */
- public class CategoryConfig : Object {
- /**
- * The type label for entities in this category.
- */
- public string type_label { get; set; }
- /**
- * The expression used to generate the index.
- */
- public string expression { get; set; }
- /**
- * Creates a new CategoryConfig.
- */
- public CategoryConfig(string type_label, string expression) {
- this.type_label = type_label;
- this.expression = expression;
- }
- }
- /**
- * Configuration for a Catalogue entity.
- */
- public class CatalogueConfig : Object {
- /**
- * The type label for documents to catalogue.
- */
- public string type_label { get; set; }
- /**
- * The expression used to extract the grouping key.
- */
- public string expression { get; set; }
- /**
- * Creates a new CatalogueConfig.
- */
- public CatalogueConfig(string type_label, string expression) {
- this.type_label = type_label;
- this.expression = expression;
- }
- }
- /**
- * Interface for high-level entity storage operations.
- */
- public interface Storage : Object {
- /**
- * Stores entity metadata.
- *
- * @param path The entity path
- * @param type The entity type
- * @param type_label Optional type label for Categories
- */
- public abstract void store_entity_metadata(Core.EntityPath path, Core.EntityType type, string? type_label = null) throws StorageError;
- /**
- * Gets the entity type for a path.
- *
- * @param path The entity path
- * @return The entity type, or null if not found
- */
- public abstract Core.EntityType? get_entity_type(Core.EntityPath path) throws StorageError;
- /**
- * Gets the entity type label for a path.
- *
- * @param path The entity path
- * @return The type label, or null if not found
- */
- public abstract string? get_entity_type_label(Core.EntityPath path) throws StorageError;
- /**
- * Checks if an entity exists at the given path.
- *
- * @param path The entity path
- * @return True if the entity exists
- */
- public abstract bool entity_exists(Core.EntityPath path);
- /**
- * Deletes an entity and all its data.
- *
- * @param path The entity path
- */
- public abstract void delete_entity(Core.EntityPath path) throws StorageError;
- /**
- * Stores document properties for an entity.
- *
- * @param path The entity path
- * @param properties The properties to store
- */
- public abstract void store_properties(Core.EntityPath path, Invercargill.Properties properties) throws StorageError;
- /**
- * Loads document properties for an entity.
- *
- * @param path The entity path
- * @return The properties, or null if not found
- */
- public abstract Invercargill.Properties? load_properties(Core.EntityPath path) throws StorageError;
- /**
- * Adds a child name to a category.
- *
- * @param parent The parent category path
- * @param child_name The name of the child
- */
- public abstract void add_child(Core.EntityPath parent, string child_name) throws StorageError;
- /**
- * Removes a child name from a category.
- *
- * @param parent The parent category path
- * @param child_name The name of the child
- */
- public abstract void remove_child(Core.EntityPath parent, string child_name) throws StorageError;
- /**
- * Gets the children of a category.
- *
- * @param parent The parent category path
- * @return Enumerable of child names
- */
- public abstract Invercargill.Enumerable<string> get_children(Core.EntityPath parent) throws StorageError;
- /**
- * Checks if a category has a specific child.
- *
- * @param parent The parent category path
- * @param child_name The name of the child
- * @return True if the child exists
- */
- public abstract bool has_child(Core.EntityPath parent, string child_name) throws StorageError;
- /**
- * Stores category configuration.
- *
- * @param path The category path
- * @param type_label The type label for entities
- * @param expression The index expression
- */
- public abstract void store_category_config(Core.EntityPath path, string type_label, string expression) throws StorageError;
- /**
- * Gets category configuration.
- *
- * @param path The category path
- * @return The configuration, or null if not found
- */
- public abstract CategoryConfig? get_category_config(Core.EntityPath path) throws StorageError;
- /**
- * Stores catalogue configuration.
- *
- * @param path The catalogue path
- * @param type_label The type label for documents to catalogue
- * @param expression The expression to extract the grouping key
- */
- public abstract void store_catalogue_config(Core.EntityPath path, string type_label, string expression) throws StorageError;
- /**
- * Gets catalogue configuration.
- *
- * @param path The catalogue path
- * @return The configuration, or null if not found
- */
- public abstract CatalogueConfig? get_catalogue_config(Core.EntityPath path) throws StorageError;
- }
- /**
- * Basic implementation of Storage using Dbm.
- */
- public class BasicStorage : Object, Storage {
-
- /**
- * The underlying Dbm backend.
- *
- * This is exposed to allow IndexManager to share the same storage.
- */
- public Dbm dbm { get { return _dbm; } }
-
- private Dbm _dbm;
- // Key prefixes for different data types
- private const string ENTITY_PREFIX = "entity:";
- private const string PROPS_PREFIX = "props:";
- private const string CHILDREN_PREFIX = "children:";
- private const string CONFIG_PREFIX = "config:";
- /**
- * Creates a new BasicStorage with the given Dbm backend.
- *
- * @param dbm The Dbm backend to use
- */
- public BasicStorage(Dbm dbm) {
- _dbm = dbm;
- }
- /**
- * Creates a new BasicStorage with a file-based Dbm.
- *
- * @param data_dir Directory to store data in
- */
- public BasicStorage.with_directory(string data_dir) {
- _dbm = new FilesystemDbm(data_dir);
- }
- /**
- * {@inheritDoc}
- */
- public void store_entity_metadata(Core.EntityPath path, Core.EntityType type, string? type_label = null) throws StorageError {
- string key = ENTITY_PREFIX + path.to_string();
- var writer = new ElementWriter();
- // Use write_element to include type code, so read_element can read it back
- writer.write_element(new Invercargill.NativeElement<int64?>((int64) type));
- writer.write_element(new Invercargill.NativeElement<string>(type_label ?? ""));
- _dbm.set(key, writer.to_binary_data());
- }
- /**
- * {@inheritDoc}
- */
- public Core.EntityType? get_entity_type(Core.EntityPath path) throws StorageError {
- string key = ENTITY_PREFIX + path.to_string();
- var data = _dbm.get(key);
- if (data == null) {
- return null;
- }
- var reader = new ElementReader((!) data);
- try {
- var element = reader.read_element();
- if (element.is_null()) {
- return null;
- }
- int64? type_val = element.as<int64?>();
- return (Core.EntityType) (type_val == null ? 0 : (!) type_val);
- } catch (Invercargill.ElementError e) {
- throw new StorageError.CORRUPT_DATA("Failed to read entity type: %s".printf(e.message));
- }
- }
- /**
- * {@inheritDoc}
- */
- public string? get_entity_type_label(Core.EntityPath path) throws StorageError {
- string key = ENTITY_PREFIX + path.to_string();
- var data = _dbm.get(key);
- if (data == null) {
- return null;
- }
- var reader = new ElementReader((!) data);
- try {
- reader.read_element(); // Skip type
- var label_element = reader.read_element();
- if (label_element.is_null()) {
- return null;
- }
- string label = label_element.as<string>();
- return label == "" ? null : label;
- } catch (Invercargill.ElementError e) {
- throw new StorageError.CORRUPT_DATA("Failed to read entity type label: %s".printf(e.message));
- }
- }
- /**
- * {@inheritDoc}
- */
- public bool entity_exists(Core.EntityPath path) {
- string key = ENTITY_PREFIX + path.to_string();
- return _dbm.has_key(key);
- }
- /**
- * {@inheritDoc}
- */
- public void delete_entity(Core.EntityPath path) throws StorageError {
- string path_str = path.to_string();
- // Delete entity metadata
- _dbm.delete(ENTITY_PREFIX + path_str);
- // Delete properties
- _dbm.delete(PROPS_PREFIX + path_str);
- // Delete children (for categories)
- _dbm.delete(CHILDREN_PREFIX + path_str);
- // Delete category config (for categories)
- _dbm.delete(CONFIG_PREFIX + path_str);
- }
- /**
- * {@inheritDoc}
- */
- public void store_properties(Core.EntityPath path, Invercargill.Properties properties) throws StorageError {
- string key = PROPS_PREFIX + path.to_string();
- var writer = new ElementWriter();
- // Use write_element to include type code, so read_element can read it back
- writer.write_element(new Invercargill.NativeElement<Invercargill.Properties>(properties));
- _dbm.set(key, writer.to_binary_data());
- }
- /**
- * {@inheritDoc}
- */
- public Invercargill.Properties? load_properties(Core.EntityPath path) throws StorageError {
- string key = PROPS_PREFIX + path.to_string();
- var data = _dbm.get(key);
- if (data == null) {
- return null;
- }
- var reader = new ElementReader((!) data);
- try {
- var element = reader.read_element();
- if (element.is_null()) {
- return null;
- }
- return element.as<Invercargill.Properties>();
- } catch (Invercargill.ElementError e) {
- throw new StorageError.CORRUPT_DATA("Failed to read properties: %s".printf(e.message));
- }
- }
- /**
- * {@inheritDoc}
- */
- public void add_child(Core.EntityPath parent, string child_name) throws StorageError {
- var children = load_children_set(parent);
- try {
- children.set(child_name, true);
- } catch (Invercargill.IndexError e) {
- throw new StorageError.IO_ERROR("Failed to add child: %s".printf(e.message));
- }
- save_children_set(parent, children);
- }
- /**
- * {@inheritDoc}
- */
- public void remove_child(Core.EntityPath parent, string child_name) throws StorageError {
- var children = load_children_set(parent);
- try {
- children.remove(child_name);
- } catch (Invercargill.IndexError e) {
- // Child not in set, that's fine
- }
- save_children_set(parent, children);
- }
- /**
- * {@inheritDoc}
- */
- public Invercargill.Enumerable<string> get_children(Core.EntityPath parent) throws StorageError {
- var children = load_children_set(parent);
- return children.keys;
- }
- /**
- * {@inheritDoc}
- */
- public bool has_child(Core.EntityPath parent, string child_name) throws StorageError {
- var children = load_children_set(parent);
- return children.has(child_name);
- }
- /**
- * Loads the children set for a category.
- */
- private Invercargill.DataStructures.Dictionary<string, bool> load_children_set(Core.EntityPath parent) throws StorageError {
- string key = CHILDREN_PREFIX + parent.to_string();
- var data = _dbm.get(key);
- var result = new Invercargill.DataStructures.Dictionary<string, bool>();
- if (data == null) {
- return result;
- }
- var reader = new ElementReader((!) data);
- try {
- var element = reader.read_element();
- if (element.is_null()) {
- return result;
- }
- // The children set is stored as an array of strings
- var array = element.as<Invercargill.Enumerable<Invercargill.Element>>();
- foreach (var child_element in array) {
- if (!child_element.is_null()) {
- string child_name = child_element.as<string>();
- try {
- result.set(child_name, true);
- } catch (Invercargill.IndexError e) {
- // Skip
- }
- }
- }
- } catch (Invercargill.ElementError e) {
- throw new StorageError.CORRUPT_DATA("Failed to read children: %s".printf(e.message));
- }
- return result;
- }
- /**
- * Saves the children set for a category.
- */
- private void save_children_set(Core.EntityPath parent, Invercargill.DataStructures.Dictionary<string, bool> children) throws StorageError {
- string key = CHILDREN_PREFIX + parent.to_string();
- uint count = children.count();
- if (count == 0) {
- _dbm.delete(key);
- return;
- }
- // Store as array of strings
- var array = new Invercargill.DataStructures.Vector<Invercargill.Element>();
- foreach (var child_name in children.keys) {
- var element = new Invercargill.NativeElement<string>(child_name);
- array.add(element);
- }
- var writer = new ElementWriter();
- // Use write_element to include type code, so read_element can read it back
- writer.write_element(new Invercargill.NativeElement<Invercargill.Enumerable<Invercargill.Element>>(array));
- _dbm.set(key, writer.to_binary_data());
- }
- /**
- * {@inheritDoc}
- */
- public void store_category_config(Core.EntityPath path, string type_label, string expression) throws StorageError {
- string key = CONFIG_PREFIX + path.to_string();
- var writer = new ElementWriter();
- // Use write_element to include type code, so read_element can read it back
- writer.write_element(new Invercargill.NativeElement<string>(type_label));
- writer.write_element(new Invercargill.NativeElement<string>(expression));
- _dbm.set(key, writer.to_binary_data());
- }
- /**
- * {@inheritDoc}
- */
- public CategoryConfig? get_category_config(Core.EntityPath path) throws StorageError {
- string key = CONFIG_PREFIX + path.to_string();
- var data = _dbm.get(key);
- if (data == null) {
- return null;
- }
- var reader = new ElementReader((!) data);
- try {
- var label_element = reader.read_element();
- var expr_element = reader.read_element();
- if (label_element.is_null() || expr_element.is_null()) {
- return null;
- }
- string type_label = label_element.as<string>();
- string expression = expr_element.as<string>();
- return new CategoryConfig(type_label, expression);
- } catch (Invercargill.ElementError e) {
- throw new StorageError.CORRUPT_DATA("Failed to read category config: %s".printf(e.message));
- }
- }
- // Key prefix for catalogue configuration
- private const string CAT_CONFIG_PREFIX = "catcfg:";
- /**
- * {@inheritDoc}
- */
- public void store_catalogue_config(Core.EntityPath path, string type_label, string expression) throws StorageError {
- string key = CAT_CONFIG_PREFIX + path.to_string();
- var writer = new ElementWriter();
- writer.write_element(new Invercargill.NativeElement<string>(type_label));
- writer.write_element(new Invercargill.NativeElement<string>(expression));
- _dbm.set(key, writer.to_binary_data());
- }
- /**
- * {@inheritDoc}
- */
- public CatalogueConfig? get_catalogue_config(Core.EntityPath path) throws StorageError {
- string key = CAT_CONFIG_PREFIX + path.to_string();
- var data = _dbm.get(key);
- if (data == null) {
- return null;
- }
- var reader = new ElementReader((!) data);
- try {
- var label_element = reader.read_element();
- var expr_element = reader.read_element();
- if (label_element.is_null() || expr_element.is_null()) {
- return null;
- }
- string type_label = label_element.as<string>();
- string expression = expr_element.as<string>();
- return new CatalogueConfig(type_label, expression);
- } catch (Invercargill.ElementError e) {
- throw new StorageError.CORRUPT_DATA("Failed to read catalogue config: %s".printf(e.message));
- }
- }
- }
- }
|