use heromodels::db::postgres::Config; use heromodels::db::{Collection, Db}; use heromodels::models::userexample::user::user_index::{email, username}; use heromodels::models::{Comment, User}; use heromodels_core::Model; // For demonstrating embedded/nested indexes use heromodels::models::grid4::node::{ComputeSlice, DeviceInfo, Node}; use heromodels::models::grid4::node::node_index::{country as node_country, pubkey as node_pubkey}; // Helper function to print user details fn print_user_details(user: &User) { println!("\n--- User Details ---"); println!("ID: {}", user.get_id()); println!("Username: {}", user.username); println!("Email: {}", user.email); println!("Full Name: {}", user.full_name); println!("Active: {}", user.is_active); println!("Created At: {}", user.base_data.created_at); println!("Modified At: {}", user.base_data.modified_at); println!("Comments: {:?}", user.base_data.comments); } // Helper function to print comment details fn print_comment_details(comment: &Comment) { println!("\n--- Comment Details ---"); println!("ID: {}", comment.get_id()); println!("User ID: {}", comment.user_id); println!("Content: {}", comment.content); println!("Created At: {}", comment.base_data.created_at); println!("Modified At: {}", comment.base_data.modified_at); } fn main() { let db = heromodels::db::postgres::Postgres::new( Config::new() .user(Some("postgres".into())) .password(Some("test123".into())) .host(Some("localhost".into())) .port(Some(5432)), ) .expect("Can connect to postgress"); // Unique suffix to avoid collisions with legacy rows from prior runs use std::time::{SystemTime, UNIX_EPOCH}; let ts = SystemTime::now() .duration_since(UNIX_EPOCH) .unwrap() .as_secs(); let user1_name = format!("johndoe_{}", ts); let user2_name = format!("janesmith_{}", ts); let user3_name = format!("willism_{}", ts); let user4_name = format!("carrols_{}", ts); let user1_email = format!("john.doe+{}@example.com", ts); let user2_email = format!("jane.smith+{}@example.com", ts); let user3_email = format!("willis.masters+{}@example.com", ts); let user4_email = format!("carrol.smith+{}@example.com", ts); println!("Hero Models - Basic Usage Example"); println!("================================"); // Clean up any existing data to ensure consistent results println!("Cleaning up existing data..."); let user_collection = db.collection::().expect("can open user collection"); let comment_collection = db .collection::() .expect("can open comment collection"); // Clear all existing users and comments if let Ok(existing_users) = user_collection.get_all() { for user in existing_users { let _ = user_collection.delete_by_id(user.get_id()); } } if let Ok(existing_comments) = comment_collection.get_all() { for comment in existing_comments { let _ = comment_collection.delete_by_id(comment.get_id()); } } println!("Database cleaned.\n"); // Create users with auto-generated IDs // User 1 let user1 = User::new() .username(&user1_name) .email(&user1_email) .full_name("John Doe") .is_active(false) .build(); // User 2 let user2 = User::new() .username(&user2_name) .email(&user2_email) .full_name("Jane Smith") .is_active(true) .build(); // User 3 let user3 = User::new() .username(&user3_name) .email(&user3_email) .full_name("Willis Masters") .is_active(true) .build(); // User 4 let user4 = User::new() .username(&user4_name) .email(&user4_email) .full_name("Carrol Smith") .is_active(false) .build(); // Save all users to database and get their assigned IDs and updated models let (user1_id, db_user1) = db .collection() .expect("can open user collection") .set(&user1) .expect("can set user"); let (user2_id, db_user2) = db .collection() .expect("can open user collection") .set(&user2) .expect("can set user"); let (user3_id, db_user3) = db .collection() .expect("can open user collection") .set(&user3) .expect("can set user"); let (user4_id, db_user4) = db .collection() .expect("can open user collection") .set(&user4) .expect("can set user"); println!("User 1 assigned ID: {user1_id}"); println!("User 2 assigned ID: {user2_id}"); println!("User 3 assigned ID: {user3_id}"); println!("User 4 assigned ID: {user4_id}"); // We already have the updated models from the set method, so we don't need to retrieve them again // Print all users retrieved from database println!("\n--- Users Retrieved from Database ---"); println!("\n1. First user:"); print_user_details(&db_user1); println!("\n2. Second user:"); print_user_details(&db_user2); println!("\n3. Third user:"); print_user_details(&db_user3); println!("\n4. Fourth user:"); print_user_details(&db_user4); // Demonstrate different ways to retrieve users from the database // 1. Retrieve by username index println!("\n--- Retrieving Users by Different Methods ---"); println!("\n1. By Username Index:"); let stored_users = db .collection::() .expect("can open user collection") .get::(&user1_name) .expect("can load stored user"); assert_eq!(stored_users.len(), 1); print_user_details(&stored_users[0]); // 2. Retrieve by email index println!("\n2. By Email Index:"); let by_email = db .collection::() .expect("can open user collection") .get::(&user2_email) .expect("can load stored user by email"); assert_eq!(by_email.len(), 1); print_user_details(&by_email[0]); // 3. Delete a user and show the updated results println!("\n3. After Deleting a User:"); let user_to_delete_id = stored_users[0].get_id(); println!("Deleting user with ID: {user_to_delete_id}"); db.collection::() .expect("can open user collection") .delete_by_id(user_to_delete_id) .expect("can delete existing user"); // Verify deletion by querying the same username again let should_be_empty = db .collection::() .expect("can open user collection") .get::(&user1_name) .expect("can query by username after delete"); println!(" a. Query by username '{}' after delete -> {} results", user1_name, should_be_empty.len()); assert_eq!(should_be_empty.len(), 0); // Delete a user based on an index for good measure db.collection::() .expect("can open user collection") .delete::(&user4_name) .expect("can delete existing user"); // Demonstrate embedded/nested indexes with Grid4 Node println!("\n--- Demonstrating Embedded/Nested Indexes (Grid4::Node) ---"); println!("Node indexed fields: {:?}", Node::indexed_fields()); // Build a minimal node with nested data and persist it let cs = ComputeSlice::new() .nodeid(42) .slice_id(1) .mem_gb(32.0) .storage_gb(512.0) .passmark(6000) .vcores(16) .gpus(1) .price_cc(0.33); let dev = DeviceInfo { vendor: "ACME".into(), ..Default::default() }; let node = Node::new() .nodegroupid(101) .uptime(99) .add_compute_slice(cs) .devices(dev) .country("BE") .pubkey("EX_NODE_PK_1") .build(); let (node_id, _stored_node) = db .collection::() .expect("can open node collection") .set(&node) .expect("can set node"); println!("Stored node id: {}", node_id); // Query by top-level indexes let be_nodes = db .collection::() .expect("can open node collection") .get::("BE") .expect("can query nodes by country"); println!("Nodes in BE (count may include legacy rows): {}", be_nodes.len()); let by_pk = db .collection::() .expect("can open node collection") .get::("EX_NODE_PK_1") .expect("can query node by pubkey"); assert!(by_pk.iter().any(|n| n.get_id() == node_id)); // Note: Nested path indexes (e.g., devices.vendor, computeslices.passmark) are created and used // for DB-side indexing, but are not yet exposed as typed Index keys in the API. They appear in // Node::indexed_fields() and contribute to Model::db_keys(), enabling performant JSONB GIN indexes. println!("\n--- User Model Information ---"); println!("User DB Prefix: {}", User::db_prefix()); // Demonstrate comment creation and association with a user println!("\n--- Working with Comments ---"); // 1. Create and save a comment println!("\n1. Creating a Comment:"); let comment = Comment::new() .user_id(db_user2.get_id()) // commenter's user ID (use an existing user) .content("This is a comment on the user") .build(); // Save the comment and get its assigned ID and updated model let (comment_id, db_comment) = db .collection() .expect("can open comment collection") .set(&comment) .expect("can set comment"); println!("Comment assigned ID: {comment_id}"); println!(" a. Comment Retrieved from Database:"); print_comment_details(&db_comment); // 3. Associate the comment with a user println!("\n2. Associating Comment with User:"); let mut updated_user = db_user2.clone(); updated_user.base_data.add_comment(db_comment.get_id()); // Save the updated user and get the new version let (_, user_with_comment) = db .collection::() .expect("can open user collection") .set(&updated_user) .expect("can set updated user"); println!(" a. User with Associated Comment:"); print_user_details(&user_with_comment); println!("\n--- Model Information ---"); println!("User DB Prefix: {}", User::db_prefix()); println!("Comment DB Prefix: {}", Comment::db_prefix()); }