/** * Basic usage example for Implexus * * This example demonstrates the unified API facade that allows * applications to seamlessly switch between embedded and remote modes. * * Features demonstrated: * - Mode selection (embedded vs remote) * - Entity creation (Container, Document, Category, Index) * - Property management * - Path-based access * - Transactions * - Querying by type and expression * - Entity deletion * * Note: All I/O operations are async. This example uses MainLoop * to run async operations from the synchronous main() function. */ using Implexus; using Implexus.Core; using Implexus.Engine; // Global engine reference for async operations Core.Engine engine; public static int main(string[] args) { print("Implexus Basic Usage Example\n"); print("=============================\n\n"); // Parse command line arguments to select mode bool use_remote = false; string host = "localhost"; uint16 port = 9876; string storage_path = "./example_data"; for (int i = 1; i < args.length; i++) { if (args[i] == "--remote") { use_remote = true; } else if (args[i] == "--host" && i + 1 < args.length) { host = args[++i]; } else if (args[i] == "--port" && i + 1 < args.length) { port = (uint16) int.parse(args[++i]); } else if (args[i] == "--path" && i + 1 < args.length) { storage_path = args[++i]; } else if (args[i] == "--help") { print("Usage: basicusage [OPTIONS]\n"); print("Options:\n"); print(" --remote Use remote mode (default: embedded)\n"); print(" --host HOST Remote server host (default: localhost)\n"); print(" --port PORT Remote server port (default: 9876)\n"); print(" --path PATH Storage path for embedded mode (default: ./example_data)\n"); print(" --help Show this help message\n"); return 0; } } // Run the async example var loop = new MainLoop(); int result = 0; Error? error = null; run_example.begin(use_remote, host, port, storage_path, (obj, res) => { try { result = run_example.end(res); } catch (Error e) { error = e; result = 1; } loop.quit(); }); loop.run(); if (error != null) { stderr.printf("Error: %s\n", ((!)error).message); } return result; } async int run_example(bool use_remote, string host, uint16 port, string storage_path) throws Error { // ============================================================ // MODE SELECTION // The only difference between modes is how you create the configuration! // ============================================================ EngineConfiguration config; if (use_remote) { print("Mode: REMOTE (%s:%u)\n\n", host, port); config = EngineConfiguration.remote(host, port); } else { print("Mode: EMBEDDED (path: %s)\n\n", storage_path); config = EngineConfiguration.embedded(storage_path); } // Create the engine - same API regardless of mode engine = EngineFactory.create(config); print("Engine created successfully!\n\n"); // ============================================================ // WORKING WITH CONTAINERS AND DOCUMENTS // ============================================================ print("--- Creating Containers and Documents ---\n"); // Get the root container (always exists) var root = yield engine.get_root_async(); print("Got root entity: %s\n", root.path.to_string()); // Create a container hierarchy for organizing users var users = yield root.create_container_async("users"); print("Created container: %s\n", users.path.to_string()); // Create documents with type labels for querying var john = yield users.create_document_async("john", "User"); print("Created document: %s (type: %s)\n", john.path.to_string(), john.type_label); // Set various property types on the document yield john.set_entity_property_async("email", new Invercargill.NativeElement("john@example.com")); yield john.set_entity_property_async("age", new Invercargill.NativeElement(30)); yield john.set_entity_property_async("active", new Invercargill.NativeElement(true)); print("Set properties: email, age, active\n\n"); // ============================================================ // QUERYING ENTITIES // ============================================================ print("--- Querying Entities ---\n"); // Query by type label print("Users (by type 'User'):\n"); foreach (var entity in yield engine.query_by_type_async("User")) { print(" - %s\n", entity.name); // Access properties var email = yield entity.get_entity_property_async("email"); if (email != null) { print(" email: %s\n", ((!) email).to_string()); } } print("\n"); // Query by expression (using Invercargill.Expressions) print("Active users (expression 'active==true'):\n"); foreach (var entity in yield engine.query_by_expression_async("User", "active==true")) { print(" - %s\n", entity.name); } print("\n"); // ============================================================ // PATH-BASED ACCESS // ============================================================ print("--- Path-Based Access ---\n"); // Access entity by path var path = new EntityPath("/users/john"); var entity = yield engine.get_entity_async(path); print("Retrieved entity by path: %s\n", entity.path.to_string()); // Check if entity exists var exists = yield engine.entity_exists_async(new EntityPath("/users/john")); print("Entity /users/john exists: %s\n".printf(exists ? "true" : "false")); var not_exists = yield engine.entity_exists_async(new EntityPath("/users/jane")); print("Entity /users/jane exists: %s\n".printf(not_exists ? "true" : "false")); print("\n"); // ============================================================ // TRANSACTIONS // ============================================================ print("--- Transactions ---\n"); // Create multiple entities atomically in a transaction var tx = yield engine.begin_transaction_async(); print("Transaction started\n"); try { var jane = yield users.create_document_async("jane", "User"); yield jane.set_entity_property_async("email", new Invercargill.NativeElement("jane@example.com")); yield jane.set_entity_property_async("age", new Invercargill.NativeElement(25)); yield jane.set_entity_property_async("active", new Invercargill.NativeElement(true)); print("Created jane in transaction\n"); var bob = yield users.create_document_async("bob", "User"); yield bob.set_entity_property_async("email", new Invercargill.NativeElement("bob@example.com")); yield bob.set_entity_property_async("age", new Invercargill.NativeElement(35)); yield bob.set_entity_property_async("active", new Invercargill.NativeElement(false)); print("Created bob in transaction\n"); yield tx.commit_async(); print("Transaction committed!\n\n"); } catch (Error e) { yield tx.rollback_async(); print("Transaction rolled back: %s\n", e.message); } // ============================================================ // CATEGORIES (Dynamic Grouping) // ============================================================ print("--- Categories (Dynamic Grouping) ---\n"); // Create a category that auto-populates with matching entities var active_users = yield root.create_category_async("active_users", "User", "active==true"); print("Created category: %s\n", active_users.path.to_string()); print(" Type filter: User\n"); print(" Expression: active==true\n"); // The category's children are automatically populated // with documents matching the expression print(" Active users in category:\n"); foreach (var child in yield active_users.get_children_async()) { print(" - %s\n", child.name); } print("\n"); // ============================================================ // INDEXES (Text Search) // ============================================================ print("--- Indexes (Text Search) ---\n"); // Create an index for text search results // Parameters: name, type_label, expression var search_index = yield root.create_index_async("email_search", "User", "email"); print("Created index: %s\n", search_index.path.to_string()); print(" Type filter: User\n"); print(" Expression: email\n"); // Index contains documents matching the search terms print(" Documents in index:\n"); foreach (var result in yield search_index.get_children_async()) { print(" - %s\n", result.name); } print("\n"); // ============================================================ // FINAL STATE // ============================================================ print("--- Final State ---\n"); print("All users:\n"); foreach (var user in yield engine.query_by_type_async("User")) { var user_email = yield user.get_entity_property_async("email"); var age = yield user.get_entity_property_async("age"); var active = yield user.get_entity_property_async("active"); print(" - %s (email: %s, age: %s, active: %s)\n", user.name, user_email != null ? ((!) user_email).to_string() : "N/A", age != null ? ((!) age).to_string() : "N/A", active != null ? ((!) active).to_string() : "N/A" ); } print("\n"); // ============================================================ // CLEANUP (Optional) // ============================================================ print("--- Cleanup ---\n"); print("Deleting test entities...\n"); // Delete category and index first yield active_users.delete_async(); print("Deleted active_users category\n"); yield search_index.delete_async(); print("Deleted email_search index\n"); // Delete all users foreach (var user in yield engine.query_by_type_async("User")) { string name = user.name; yield user.delete_async(); print("Deleted %s\n", name); } // Delete the users container yield users.delete_async(); print("Deleted users container\n"); print("\nExample completed successfully!\n"); print("\nTip: To test remote mode, start implexusd first:\n"); print(" ./build/tools/implexusd/implexusd --port 9876 --storage ./server_data\n"); print(" Then run: ./build/examples/basicusage --remote\n"); return 0; }