BasicUsage.vala 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290
  1. /**
  2. * Basic usage example for Implexus
  3. *
  4. * This example demonstrates the unified API facade that allows
  5. * applications to seamlessly switch between embedded and remote modes.
  6. *
  7. * Features demonstrated:
  8. * - Mode selection (embedded vs remote)
  9. * - Entity creation (Container, Document, Category, Index)
  10. * - Property management
  11. * - Path-based access
  12. * - Transactions
  13. * - Querying by type and expression
  14. * - Entity deletion
  15. *
  16. * Note: All I/O operations are async. This example uses MainLoop
  17. * to run async operations from the synchronous main() function.
  18. */
  19. using Implexus;
  20. using Implexus.Core;
  21. using Implexus.Engine;
  22. // Global engine reference for async operations
  23. Core.Engine engine;
  24. public static int main(string[] args) {
  25. print("Implexus Basic Usage Example\n");
  26. print("=============================\n\n");
  27. // Parse command line arguments to select mode
  28. bool use_remote = false;
  29. string host = "localhost";
  30. uint16 port = 9876;
  31. string storage_path = "./example_data";
  32. for (int i = 1; i < args.length; i++) {
  33. if (args[i] == "--remote") {
  34. use_remote = true;
  35. } else if (args[i] == "--host" && i + 1 < args.length) {
  36. host = args[++i];
  37. } else if (args[i] == "--port" && i + 1 < args.length) {
  38. port = (uint16) int.parse(args[++i]);
  39. } else if (args[i] == "--path" && i + 1 < args.length) {
  40. storage_path = args[++i];
  41. } else if (args[i] == "--help") {
  42. print("Usage: basicusage [OPTIONS]\n");
  43. print("Options:\n");
  44. print(" --remote Use remote mode (default: embedded)\n");
  45. print(" --host HOST Remote server host (default: localhost)\n");
  46. print(" --port PORT Remote server port (default: 9876)\n");
  47. print(" --path PATH Storage path for embedded mode (default: ./example_data)\n");
  48. print(" --help Show this help message\n");
  49. return 0;
  50. }
  51. }
  52. // Run the async example
  53. var loop = new MainLoop();
  54. int result = 0;
  55. Error? error = null;
  56. run_example.begin(use_remote, host, port, storage_path, (obj, res) => {
  57. try {
  58. result = run_example.end(res);
  59. } catch (Error e) {
  60. error = e;
  61. result = 1;
  62. }
  63. loop.quit();
  64. });
  65. loop.run();
  66. if (error != null) {
  67. stderr.printf("Error: %s\n", ((!)error).message);
  68. }
  69. return result;
  70. }
  71. async int run_example(bool use_remote, string host, uint16 port, string storage_path) throws Error {
  72. // ============================================================
  73. // MODE SELECTION
  74. // The only difference between modes is how you create the configuration!
  75. // ============================================================
  76. EngineConfiguration config;
  77. if (use_remote) {
  78. print("Mode: REMOTE (%s:%u)\n\n", host, port);
  79. config = EngineConfiguration.remote(host, port);
  80. } else {
  81. print("Mode: EMBEDDED (path: %s)\n\n", storage_path);
  82. config = EngineConfiguration.embedded(storage_path);
  83. }
  84. // Create the engine - same API regardless of mode
  85. engine = EngineFactory.create(config);
  86. print("Engine created successfully!\n\n");
  87. // ============================================================
  88. // WORKING WITH CONTAINERS AND DOCUMENTS
  89. // ============================================================
  90. print("--- Creating Containers and Documents ---\n");
  91. // Get the root container (always exists)
  92. var root = yield engine.get_root_async();
  93. print("Got root entity: %s\n", root.path.to_string());
  94. // Create a container hierarchy for organizing users
  95. var users = yield root.create_container_async("users");
  96. print("Created container: %s\n", users.path.to_string());
  97. // Create documents with type labels for querying
  98. var john = yield users.create_document_async("john", "User");
  99. print("Created document: %s (type: %s)\n", john.path.to_string(), john.type_label);
  100. // Set various property types on the document
  101. yield john.set_entity_property_async("email", new Invercargill.NativeElement<string>("john@example.com"));
  102. yield john.set_entity_property_async("age", new Invercargill.NativeElement<int>(30));
  103. yield john.set_entity_property_async("active", new Invercargill.NativeElement<bool?>(true));
  104. print("Set properties: email, age, active\n\n");
  105. // ============================================================
  106. // QUERYING ENTITIES
  107. // ============================================================
  108. print("--- Querying Entities ---\n");
  109. // Query by type label
  110. print("Users (by type 'User'):\n");
  111. foreach (var entity in yield engine.query_by_type_async("User")) {
  112. print(" - %s\n", entity.name);
  113. // Access properties
  114. var email = yield entity.get_entity_property_async("email");
  115. if (email != null) {
  116. print(" email: %s\n", ((!) email).to_string());
  117. }
  118. }
  119. print("\n");
  120. // Query by expression (using Invercargill.Expressions)
  121. print("Active users (expression 'active==true'):\n");
  122. foreach (var entity in yield engine.query_by_expression_async("User", "active==true")) {
  123. print(" - %s\n", entity.name);
  124. }
  125. print("\n");
  126. // ============================================================
  127. // PATH-BASED ACCESS
  128. // ============================================================
  129. print("--- Path-Based Access ---\n");
  130. // Access entity by path
  131. var path = new EntityPath("/users/john");
  132. var entity = yield engine.get_entity_async(path);
  133. print("Retrieved entity by path: %s\n", entity.path.to_string());
  134. // Check if entity exists
  135. var exists = yield engine.entity_exists_async(new EntityPath("/users/john"));
  136. print("Entity /users/john exists: %s\n".printf(exists ? "true" : "false"));
  137. var not_exists = yield engine.entity_exists_async(new EntityPath("/users/jane"));
  138. print("Entity /users/jane exists: %s\n".printf(not_exists ? "true" : "false"));
  139. print("\n");
  140. // ============================================================
  141. // TRANSACTIONS
  142. // ============================================================
  143. print("--- Transactions ---\n");
  144. // Create multiple entities atomically in a transaction
  145. var tx = yield engine.begin_transaction_async();
  146. print("Transaction started\n");
  147. try {
  148. var jane = yield users.create_document_async("jane", "User");
  149. yield jane.set_entity_property_async("email", new Invercargill.NativeElement<string>("jane@example.com"));
  150. yield jane.set_entity_property_async("age", new Invercargill.NativeElement<int>(25));
  151. yield jane.set_entity_property_async("active", new Invercargill.NativeElement<bool?>(true));
  152. print("Created jane in transaction\n");
  153. var bob = yield users.create_document_async("bob", "User");
  154. yield bob.set_entity_property_async("email", new Invercargill.NativeElement<string>("bob@example.com"));
  155. yield bob.set_entity_property_async("age", new Invercargill.NativeElement<int>(35));
  156. yield bob.set_entity_property_async("active", new Invercargill.NativeElement<bool?>(false));
  157. print("Created bob in transaction\n");
  158. yield tx.commit_async();
  159. print("Transaction committed!\n\n");
  160. } catch (Error e) {
  161. yield tx.rollback_async();
  162. print("Transaction rolled back: %s\n", e.message);
  163. }
  164. // ============================================================
  165. // CATEGORIES (Dynamic Grouping)
  166. // ============================================================
  167. print("--- Categories (Dynamic Grouping) ---\n");
  168. // Create a category that auto-populates with matching entities
  169. var active_users = yield root.create_category_async("active_users", "User", "active==true");
  170. print("Created category: %s\n", active_users.path.to_string());
  171. print(" Type filter: User\n");
  172. print(" Expression: active==true\n");
  173. // The category's children are automatically populated
  174. // with documents matching the expression
  175. print(" Active users in category:\n");
  176. foreach (var child in yield active_users.get_children_async()) {
  177. print(" - %s\n", child.name);
  178. }
  179. print("\n");
  180. // ============================================================
  181. // INDEXES (Text Search)
  182. // ============================================================
  183. print("--- Indexes (Text Search) ---\n");
  184. // Create an index for text search results
  185. // Parameters: name, type_label, expression
  186. var search_index = yield root.create_index_async("email_search", "User", "email");
  187. print("Created index: %s\n", search_index.path.to_string());
  188. print(" Type filter: User\n");
  189. print(" Expression: email\n");
  190. // Index contains documents matching the search terms
  191. print(" Documents in index:\n");
  192. foreach (var result in yield search_index.get_children_async()) {
  193. print(" - %s\n", result.name);
  194. }
  195. print("\n");
  196. // ============================================================
  197. // FINAL STATE
  198. // ============================================================
  199. print("--- Final State ---\n");
  200. print("All users:\n");
  201. foreach (var user in yield engine.query_by_type_async("User")) {
  202. var user_email = yield user.get_entity_property_async("email");
  203. var age = yield user.get_entity_property_async("age");
  204. var active = yield user.get_entity_property_async("active");
  205. print(" - %s (email: %s, age: %s, active: %s)\n",
  206. user.name,
  207. user_email != null ? ((!) user_email).to_string() : "N/A",
  208. age != null ? ((!) age).to_string() : "N/A",
  209. active != null ? ((!) active).to_string() : "N/A"
  210. );
  211. }
  212. print("\n");
  213. // ============================================================
  214. // CLEANUP (Optional)
  215. // ============================================================
  216. print("--- Cleanup ---\n");
  217. print("Deleting test entities...\n");
  218. // Delete category and index first
  219. yield active_users.delete_async();
  220. print("Deleted active_users category\n");
  221. yield search_index.delete_async();
  222. print("Deleted email_search index\n");
  223. // Delete all users
  224. foreach (var user in yield engine.query_by_type_async("User")) {
  225. string name = user.name;
  226. yield user.delete_async();
  227. print("Deleted %s\n", name);
  228. }
  229. // Delete the users container
  230. yield users.delete_async();
  231. print("Deleted users container\n");
  232. print("\nExample completed successfully!\n");
  233. print("\nTip: To test remote mode, start implexusd first:\n");
  234. print(" ./build/tools/implexusd/implexusd --port 9876 --storage ./server_data\n");
  235. print(" Then run: ./build/examples/basicusage --remote\n");
  236. return 0;
  237. }