update grid4 & heroledger models

This commit is contained in:
Timur Gordon
2025-09-16 14:18:08 +02:00
parent cb1fb0f0ec
commit 53e9a2d4f0
31 changed files with 3216 additions and 399 deletions

View File

@@ -1,8 +1,11 @@
use heromodels::db::postgres::Config;
use heromodels::db::{Collection, Db};
use heromodels::models::userexample::user::user_index::{is_active, username};
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) {
@@ -37,6 +40,21 @@ fn main() {
)
.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!("================================");
@@ -64,32 +82,32 @@ fn main() {
// User 1
let user1 = User::new()
.username("johndoe")
.email("john.doe@example.com")
.username(&user1_name)
.email(&user1_email)
.full_name("John Doe")
.is_active(false)
.build();
// User 2
let user2 = User::new()
.username("janesmith")
.email("jane.smith@example.com")
.username(&user2_name)
.email(&user2_email)
.full_name("Jane Smith")
.is_active(true)
.build();
// User 3
let user3 = User::new()
.username("willism")
.email("willis.masters@example.com")
.username(&user3_name)
.email(&user3_email)
.full_name("Willis Masters")
.is_active(true)
.build();
// User 4
let user4 = User::new()
.username("carrols")
.email("carrol.smith@example.com")
.username(&user4_name)
.email(&user4_email)
.full_name("Carrol Smith")
.is_active(false)
.build();
@@ -145,66 +163,95 @@ fn main() {
let stored_users = db
.collection::<User>()
.expect("can open user collection")
.get::<username, _>("johndoe")
.get::<username, _>(&user1_name)
.expect("can load stored user");
assert_eq!(stored_users.len(), 1);
print_user_details(&stored_users[0]);
// 2. Retrieve by active status
println!("\n2. By Active Status (Active = true):");
let active_users = db
// 2. Retrieve by email index
println!("\n2. By Email Index:");
let by_email = db
.collection::<User>()
.expect("can open user collection")
.get::<is_active, _>(&true)
.expect("can load stored users");
assert_eq!(active_users.len(), 2);
for active_user in active_users.iter() {
print_user_details(active_user);
}
.get::<email, _>(&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 = active_users[0].get_id();
let user_to_delete_id = stored_users[0].get_id();
println!("Deleting user with ID: {user_to_delete_id}");
db.collection::<User>()
.expect("can open user collection")
.delete_by_id(user_to_delete_id)
.expect("can delete existing user");
// Show remaining active users
let active_users = db
// Verify deletion by querying the same username again
let should_be_empty = db
.collection::<User>()
.expect("can open user collection")
.get::<is_active, _>(&true)
.expect("can load stored users");
println!(" a. Remaining Active Users:");
assert_eq!(active_users.len(), 1);
for active_user in active_users.iter() {
print_user_details(active_user);
}
// Show inactive users
let inactive_users = db
.collection::<User>()
.expect("can open user collection")
.get::<is_active, _>(&false)
.expect("can load stored users");
println!(" b. Inactive Users:");
assert_eq!(inactive_users.len(), 2);
for inactive_user in inactive_users.iter() {
print_user_details(inactive_user);
}
.get::<username, _>(&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::<User>()
.expect("can open user collection")
.delete::<username, _>("janesmith")
.delete::<username, _>(&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::<Node>()
.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::<Node>()
.expect("can open node collection")
.get::<node_country, _>("BE")
.expect("can query nodes by country");
println!("Nodes in BE (count may include legacy rows): {}", be_nodes.len());
let by_pk = db
.collection::<Node>()
.expect("can open node collection")
.get::<node_pubkey, _>("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());
@@ -214,7 +261,7 @@ fn main() {
// 1. Create and save a comment
println!("\n1. Creating a Comment:");
let comment = Comment::new()
.user_id(db_user1.get_id()) // commenter's user ID
.user_id(db_user2.get_id()) // commenter's user ID (use an existing user)
.content("This is a comment on the user")
.build();
@@ -232,7 +279,7 @@ fn main() {
// 3. Associate the comment with a user
println!("\n2. Associating Comment with User:");
let mut updated_user = db_user1.clone();
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