use chrono::Utc;
use rat_logger::{LevelFilter, LoggerBuilder, handler::term::TermConfig};
use rat_quickdb::types::*;
use rat_quickdb::*;
use rat_quickdb::{
ModelManager, ModelOperations, array_field, boolean_field, datetime_field, float_field,
integer_field, json_field, string_field, uuid_field,
};
const DATABASE_ALIAS: &str = "main";
use std::collections::HashMap;
use std::time::Instant;
use tokio::join;
define_model! {
struct User {
id: String,
username: String,
email: String,
password_hash: String,
full_name: String,
age: Option<i32>,
is_active: bool,
created_at: chrono::DateTime<chrono::Utc>,
tags: Option<Vec<String>>,
}
collection = "users",
database = DATABASE_ALIAS,
fields = {
id: uuid_field().required().unique(),
username: string_field(None, None, None).required().unique(),
email: string_field(None, None, None).required().unique(),
password_hash: string_field(None, None, None).required(),
full_name: string_field(None, None, None).required(),
age: integer_field(None, None),
is_active: boolean_field().required(),
created_at: datetime_field().required(),
tags: array_field(field_types!(string), None, None),
}
indexes = [
{ fields: ["username"], unique: true, name: "idx_username" },
{ fields: ["email"], unique: true, name: "idx_email" },
{ fields: ["is_active", "created_at"], unique: false, name: "idx_active_created" },
],
}
define_model! {
struct Employee {
id: String,
employee_id: String,
name: String,
department: String,
salary: f64,
is_active: bool,
created_at: chrono::DateTime<chrono::Utc>,
}
collection = "employees",
database = DATABASE_ALIAS,
fields = {
id: uuid_field().required().unique(),
employee_id: string_field(None, None, None).required().unique(),
name: string_field(None, None, None).required(),
department: string_field(None, None, None).required(),
salary: float_field(None, None).required(),
is_active: boolean_field().required(),
created_at: datetime_field().required(),
}
indexes = [
{ fields: ["employee_id"], unique: true, name: "idx_employee_id" },
{ fields: ["department"], unique: false, name: "idx_department" },
{ fields: ["salary"], unique: false, name: "idx_salary" },
],
}
#[derive(Debug)]
struct SimpleStats {
total_operations: u64,
successful_operations: u64,
total_time_ms: u64,
}
impl SimpleStats {
fn new() -> Self {
Self {
total_operations: 0,
successful_operations: 0,
total_time_ms: 0,
}
}
fn add_operation(&mut self, duration_ms: u64, success: bool) {
self.total_operations += 1;
self.total_time_ms += duration_ms;
if success {
self.successful_operations += 1;
}
}
fn average_time(&self) -> f64 {
if self.total_operations == 0 {
0.0
} else {
self.total_time_ms as f64 / self.total_operations as f64
}
}
fn success_rate(&self) -> f64 {
if self.total_operations == 0 {
0.0
} else {
self.successful_operations as f64 / self.total_operations as f64 * 100.0
}
}
}
async fn test_find_vs_find_with_groups_array() -> Result<(), Box<dyn std::error::Error>> {
println!("\n=== 测试 find vs find_with_groups 对 array_field 的处理差异 ===");
println!("\n--- 使用 find 方法查询 tags 包含 '测试' 的用户 ---");
let find_condition = QueryCondition {
field: "tags".to_string(),
operator: QueryOperator::In,
value: DataValue::Array(vec![DataValue::String("测试".to_string())]),
};
let find_results = ModelManager::<User>::find(vec![find_condition], None).await?;
println!("find 方法返回 {} 个用户:", find_results.len());
for user in &find_results {
println!(" - {}: tags = {:?}", user.username, user.tags);
println!(" tags 的类型: {}", std::any::type_name::<Vec<String>>());
}
println!("\n--- 使用 find_with_groups 方法查询 tags 包含 '测试' 的用户 ---");
let group_condition = QueryConditionGroup::Group {
operator: LogicalOperator::And,
conditions: vec![QueryConditionGroup::Single(QueryCondition {
field: "tags".to_string(),
operator: QueryOperator::In,
value: DataValue::Array(vec![DataValue::String("测试".to_string())]),
})],
};
let group_results = ModelManager::<User>::find_with_groups(
vec![group_condition],
None,
).await?;
println!("find_with_groups 方法返回 {} 个结果:", group_results.len());
for user in &group_results {
println!(" - {}: tags = {:?}", user.username, user.tags);
println!(" tags 的类型: {}", std::any::type_name::<Vec<String>>());
}
println!("\n=== 对比分析 ===");
println!("find 返回数量: {}", find_results.len());
println!("find_with_groups 返回数量: {}", group_results.len());
if find_results.len() != group_results.len() {
println!("❌ 错误:两种方法返回的记录数不一致!");
}
if find_results.len() == group_results.len() {
println!("\n比较两种方法返回的 tags 内容:");
for (i, (find_user, group_user)) in find_results.iter().zip(group_results.iter()).enumerate() {
println!(" 用户 {}: {}", i + 1, find_user.username);
println!(" find 返回的 tags: {:?}", find_user.tags);
println!(" find_with_groups 返回的 tags: {:?}", group_user.tags);
if find_user.tags == group_user.tags {
println!(" ✅ tags 内容一致");
} else {
println!(" ❌ tags 内容不一致!");
}
}
}
Ok(())
}
async fn cleanup_test_data() {
println!("清理测试数据...");
println!("正在清理用户表...");
match rat_quickdb::drop_table(DATABASE_ALIAS, "users").await {
Ok(_) => println!("✅ 用户表清理成功"),
Err(e) => println!("⚠️ 清理用户表失败: {}", e),
}
if let Err(e) = rat_quickdb::drop_table(DATABASE_ALIAS, "employees").await {
println!("清理员工表失败: {}", e);
}
}
async fn demonstrate_crud() -> Result<SimpleStats, Box<dyn std::error::Error>> {
println!("\n=== CRUD操作演示 ===");
let mut stats = SimpleStats::new();
println!("\n测试表不存在错误识别功能...");
match ModelManager::<User>::find_by_id("00000000-0000-0000-0000-000000000000").await {
Err(QuickDbError::TableNotExistError { table, message }) => {
println!("✅ 成功识别表不存在错误:");
println!(" 表名: {}", table);
println!(" 错误信息: {}", message);
}
Err(e) => {
println!("⚠️ 识别到其他错误: {}", e);
}
Ok(_) => {
println!("ℹ️ 表已存在(可能是之前创建的)");
}
}
let user = User {
id: String::new(),
username: format!("test_user_{}", uuid::Uuid::new_v4().simple()),
email: format!("test_{}@example.com", uuid::Uuid::new_v4().simple()),
password_hash: "hashed_password".to_string(),
full_name: "测试用户".to_string(),
age: Some(25),
is_active: true,
created_at: Utc::now(),
tags: Some(vec!["测试".to_string(), "用户".to_string()]),
};
let start = Instant::now();
let user_id = match user.save().await {
Ok(id) => {
println!("✅ 用户创建成功: {}", id);
id
}
Err(e) => {
println!("❌ 用户创建失败: {}", e);
return Ok(stats);
}
};
stats.add_operation(start.elapsed().as_millis() as u64, true);
let start = Instant::now();
match ModelManager::<User>::find_by_id(&user_id).await {
Ok(Some(found_user)) => {
println!("✅ 用户查询成功: {}", found_user.username);
stats.add_operation(start.elapsed().as_millis() as u64, true);
let start = Instant::now();
let mut update_data = HashMap::new();
update_data.insert("age".to_string(), DataValue::Int(26));
match found_user.update(update_data).await {
Ok(_) => {
println!("✅ 用户更新成功");
stats.add_operation(start.elapsed().as_millis() as u64, true);
}
Err(e) => {
println!("❌ 用户更新失败: {}", e);
stats.add_operation(start.elapsed().as_millis() as u64, false);
}
}
let start = Instant::now();
match found_user.delete().await {
Ok(_) => {
println!("✅ 用户删除成功");
stats.add_operation(start.elapsed().as_millis() as u64, true);
}
Err(e) => {
println!("❌ 用户删除失败: {}", e);
stats.add_operation(start.elapsed().as_millis() as u64, false);
}
}
}
Ok(None) => {
println!("❌ 用户未找到");
stats.add_operation(start.elapsed().as_millis() as u64, false);
}
Err(e) => {
println!("❌ 查询失败: {}", e);
stats.add_operation(start.elapsed().as_millis() as u64, false);
}
}
Ok(stats)
}
async fn demonstrate_concurrency() -> Result<SimpleStats, Box<dyn std::error::Error>> {
println!("\n=== 并发操作演示 ===");
let mut stats = SimpleStats::new();
let concurrent_count = 10;
let mut tasks = Vec::new();
for i in 0..concurrent_count {
let task = tokio::spawn(async move {
let user = User {
id: String::new(),
username: format!("concurrent_user_{}_{}", i, uuid::Uuid::new_v4().simple()),
email: format!(
"concurrent_{}_{}@example.com",
i,
uuid::Uuid::new_v4().simple()
),
password_hash: "hashed_password".to_string(),
full_name: format!("并发用户 {}", i),
age: Some(20 + i),
is_active: true,
created_at: Utc::now(),
tags: Some(vec!["并发".to_string(), "测试".to_string()]),
};
let start = Instant::now();
match user.save().await {
Ok(id) => {
println!(" 并发创建用户 {}: 成功 ({})", i, id);
(start.elapsed().as_millis() as u64, true)
}
Err(e) => {
println!(" 并发创建用户 {}: 失败 - {}", i, e);
(start.elapsed().as_millis() as u64, false)
}
}
});
tasks.push(task);
}
for (i, task) in tasks.into_iter().enumerate() {
match task.await {
Ok((duration, success)) => {
stats.add_operation(duration, success);
}
Err(e) => {
println!("任务 {} 执行失败: {}", i, e);
stats.add_operation(0, false);
}
}
}
println!(
"并发操作完成 - 成功率: {:.1}%, 平均耗时: {:.1}ms",
stats.success_rate(),
stats.average_time()
);
Ok(stats)
}
async fn demonstrate_pagination() -> Result<SimpleStats, Box<dyn std::error::Error>> {
println!("\n=== 分页查询演示 ===");
let mut stats = SimpleStats::new();
println!("创建测试员工数据...");
let mut created_ids = Vec::new();
for i in 0..50 {
let employee = Employee {
id: String::new(),
employee_id: format!("EMP{:04}", i),
name: format!("员工 {}", i),
department: match i % 3 {
0 => "技术部".to_string(),
1 => "销售部".to_string(),
_ => "人事部".to_string(),
},
salary: 5000.0 + (i as f64 * 100.0),
is_active: i % 5 != 0, created_at: Utc::now(),
};
match employee.save().await {
Ok(id) => created_ids.push(id),
Err(e) => println!("创建员工 {} 失败: {}", i, e),
}
}
let page_size = 10;
for page in 0..5 {
let start = Instant::now();
let pagination = PaginationConfig {
limit: page_size,
skip: page * page_size,
};
let options = QueryOptions {
pagination: Some(pagination),
..Default::default()
};
match ModelManager::<Employee>::find(vec![], Some(options)).await {
Ok(employees) => {
stats.add_operation(start.elapsed().as_millis() as u64, true);
println!("第 {} 页: {} 条记录", page + 1, employees.len());
for (i, emp) in employees.iter().take(3).enumerate() {
println!(
" {}. {} - {} - ${:.0}",
(page as u64 * page_size as u64 + i as u64 + 1),
emp.name,
emp.department,
emp.salary
);
}
if employees.len() > 3 {
println!(" ... 还有 {} 条", employees.len() - 3);
}
}
Err(e) => {
stats.add_operation(start.elapsed().as_millis() as u64, false);
println!("第 {} 页查询失败: {}", page + 1, e);
}
}
}
println!("清理测试数据...");
for id in created_ids {
if let Ok(Some(employee)) = ModelManager::<Employee>::find_by_id(&id).await {
let _ = employee.delete().await;
}
}
println!(
"分页查询完成 - 成功率: {:.1}%, 平均耗时: {:.1}ms",
stats.success_rate(),
stats.average_time()
);
Ok(stats)
}
async fn performance_benchmark() -> Result<(), Box<dyn std::error::Error>> {
println!("\n=== 性能基准测试 ===");
let test_count = 100;
let start = Instant::now();
let mut successful = 0;
for i in 0..test_count {
let user = User {
id: String::new(),
username: format!("perf_user_{}", i),
email: format!("perf_{}@test.com", i),
password_hash: "hash".to_string(),
full_name: format!("性能用户 {}", i),
age: Some(25 + i),
is_active: true,
created_at: Utc::now(),
tags: Some(vec!["性能测试".to_string()]),
};
if user.save().await.is_ok() {
successful += 1;
}
}
let create_time = start.elapsed();
println!(
"创建 {} 条记录: 成功 {} 条, 耗时 {:?}, 平均 {:.1}ms/条",
test_count,
successful,
create_time,
create_time.as_millis() as f64 / test_count as f64
);
let start = Instant::now();
let mut found = 0;
match ModelManager::<User>::find(vec![], None).await {
Ok(users) => {
found = users.len();
}
Err(e) => println!("批量查询失败: {}", e),
}
let query_time = start.elapsed();
println!(
"查询 {} 条记录: 找到 {} 条, 耗时 {:?}",
test_count, found, query_time
);
Ok(())
}
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
println!("=== RatQuickDB MongoDB 模型操作演示 ===");
LoggerBuilder::new()
.with_level(LevelFilter::Warn) .add_terminal_with_config(TermConfig::default())
.init()?;
let tls_config = rat_quickdb::types::TlsConfig {
enabled: true,
ca_cert_path: None,
client_cert_path: None,
client_key_path: None,
verify_server_cert: false,
verify_hostname: false,
min_tls_version: Some("1.2".to_string()),
cipher_suites: None,
};
let zstd_config = rat_quickdb::types::ZstdConfig {
enabled: true,
compression_level: Some(3),
compression_threshold: Some(1024),
};
let db_config = DatabaseConfig::builder()
.db_type(DatabaseType::MongoDB)
.connection(ConnectionConfig::MongoDB {
host: "db0.0ldm0s.net".to_string(),
port: 27017,
database: "testdb".to_string(),
username: Some("testdb".to_string()),
password: Some("testdb123456".to_string()),
auth_source: Some("testdb".to_string()),
direct_connection: true,
options: None,
tls_config: Some(tls_config),
zstd_config: Some(zstd_config),
})
.pool(
PoolConfig::builder()
.max_connections(25)
.min_connections(5)
.connection_timeout(10)
.idle_timeout(30)
.max_lifetime(1200)
.max_retries(6)
.retry_interval_ms(250)
.keepalive_interval_sec(20)
.health_check_timeout_sec(3)
.build()?,
)
.alias(DATABASE_ALIAS)
.id_strategy(IdStrategy::Uuid)
.build()?;
add_database(db_config).await?;
println!("数据库连接成功");
cleanup_test_data().await;
println!("清理完成");
let crud_stats = demonstrate_crud().await?;
let concurrent_stats = demonstrate_concurrency().await?;
let pagination_stats = demonstrate_pagination().await?;
test_find_vs_find_with_groups_array().await?;
performance_benchmark().await?;
println!("\n=== 操作统计 ===");
println!(
"CRUD操作: {} 次, 成功率 {:.1}%, 平均 {:.1}ms",
crud_stats.total_operations,
crud_stats.success_rate(),
crud_stats.average_time()
);
println!(
"并发操作: {} 次, 成功率 {:.1}%, 平均 {:.1}ms",
concurrent_stats.total_operations,
concurrent_stats.success_rate(),
concurrent_stats.average_time()
);
println!(
"分页操作: {} 次, 成功率 {:.1}%, 平均 {:.1}ms",
pagination_stats.total_operations,
pagination_stats.success_rate(),
pagination_stats.average_time()
);
println!("\n=== 健康检查 ===");
let health = health_check().await;
for (alias, is_healthy) in health {
let status = if is_healthy { "✅" } else { "❌" };
println!("{}: {}", alias, status);
}
cleanup_test_data().await;
println!("\n演示完成");
Ok(())
}