use motedb::{Database, Result};
use motedb::types::{BoundingBox, Timestamp, Value};
use rand::Rng;
use std::collections::HashMap;
use std::fs;
use std::path::PathBuf;
use std::time::{SystemTime, UNIX_EPOCH};
fn main() -> Result<()> {
println!("Running documentation examples...");
docs_quick_start_example_runs()?;
docs_batch_insert_example_runs()?;
docs_vector_index_example_runs()?;
docs_text_index_example_runs()?;
docs_spatial_index_example_runs()?;
docs_timestamp_example_runs()?;
docs_transaction_example_runs()?;
println!("✅ All documentation examples executed successfully");
Ok(())
}
fn setup_db(name: &str) -> Result<(Database, PathBuf)> {
let (base_path, storage_dir) = unique_db_path(name);
let created = Database::create(&base_path)?;
drop(created);
let db = Database::open(&base_path)?;
Ok((db, storage_dir))
}
fn unique_db_path(name: &str) -> (String, PathBuf) {
let mut base = std::env::temp_dir();
let nanos = SystemTime::now()
.duration_since(UNIX_EPOCH)
.expect("time went backwards")
.as_nanos();
let random: u64 = rand::thread_rng().gen();
base.push(format!("motedb_doc_test_{}_{}_{}", name, nanos, random));
let storage_dir = base.with_extension("mote");
if storage_dir.exists() {
let _ = fs::remove_dir_all(&storage_dir);
}
(base.to_string_lossy().into_owned(), storage_dir)
}
fn cleanup_dir(path: PathBuf) {
let _ = fs::remove_dir_all(path);
}
fn docs_quick_start_example_runs() -> Result<()> {
let (db, dir) = setup_db("quick_start")?;
db.execute(
"CREATE TABLE users (
id INT,
name TEXT,
email TEXT,
age INT
)",
)?;
db.execute("INSERT INTO users VALUES (1, 'Alice', 'alice@example.com', 25)")?;
let results = db.execute("SELECT * FROM users WHERE id = 1")?.materialize()?; assert_eq!(results.row_count(), 1);
db.flush()?;
drop(db);
cleanup_dir(dir);
Ok(())
}
fn docs_batch_insert_example_runs() -> Result<()> {
let (db, dir) = setup_db("batch_insert")?;
db.execute(
"CREATE TABLE users (
id INT,
name TEXT,
email TEXT,
age INT
)",
)?;
let mut rows = Vec::new();
for i in 0..100 {
let mut row = HashMap::new();
row.insert("id".into(), Value::Integer(i));
row.insert("name".into(), Value::Text(format!("User{}", i)));
row.insert("email".into(), Value::Text(format!("user{}@example.com", i)));
row.insert("age".into(), Value::Integer(20 + (i % 30)));
rows.push(row);
}
let row_ids = db.batch_insert_map("users", rows)?;
assert_eq!(row_ids.len(), 100);
db.flush()?;
drop(db);
cleanup_dir(dir);
Ok(())
}
fn docs_vector_index_example_runs() -> Result<()> {
let (db, dir) = setup_db("vector_index")?;
db.execute(
"CREATE TABLE documents (
id INT,
title TEXT,
embedding VECTOR(4)
)",
)?;
db.execute("CREATE VECTOR INDEX docs_embedding ON documents(embedding)")?;
let mut rows = Vec::new();
for i in 0..10 { let mut row = HashMap::new();
row.insert("id".into(), Value::Integer(i));
row.insert("title".into(), Value::Text(format!("Doc {}", i)));
row.insert("embedding".into(), Value::Vector(motedb::types::ArcVec::new(vec![i as f32 * 0.1; 4])));
rows.push(row);
}
db.batch_insert_with_vectors_map("documents", rows, &["embedding"])?;
db.flush()?;
let hits = db.vector_search("docs_embedding", &[0.0, 0.0, 0.0, 0.0], 5)?;
assert!(!hits.is_empty(), "向量索引应该返回结果(增量更新已生效)");
drop(db);
cleanup_dir(dir);
Ok(())
}
fn docs_text_index_example_runs() -> Result<()> {
let (db, dir) = setup_db("text_index")?;
db.execute(
"CREATE TABLE articles (
id INT,
title TEXT,
content TEXT
)",
)?;
db.execute("CREATE TEXT INDEX articles_content ON articles(content)")?;
let mut rows = Vec::new();
for (i, content) in [
"Rust database guide",
"Vector search with DiskANN",
"Spatial indexing tutorial",
]
.iter()
.enumerate()
{
let mut row = HashMap::new();
row.insert("id".into(), Value::Integer(i as i64));
row.insert("title".into(), Value::Text(format!("Article {}", i)));
row.insert("content".into(), Value::Text((*content).into()));
rows.push(row);
}
db.batch_insert_map("articles", rows)?;
db.flush()?;
let hits = db.text_search_ranked("articles_content", "Rust", 5)?;
assert!(!hits.is_empty(), "文本索引应该返回结果(增量更新已生效)");
drop(db);
cleanup_dir(dir);
Ok(())
}
fn docs_spatial_index_example_runs() -> Result<()> {
let (db, dir) = setup_db("spatial_index")?;
db.execute(
"CREATE TABLE locations (
id INT,
name TEXT,
coords VECTOR(2),
category TEXT
)",
)?;
let bounds = BoundingBox::new(-180.0, -90.0, 180.0, 90.0);
db.create_spatial_index("locations_coords", bounds)?;
let mut rows = Vec::new();
for i in 0..10 {
let mut row = HashMap::new();
row.insert("id".into(), Value::Integer(i));
row.insert("name".into(), Value::Text(format!("POI {}", i)));
row.insert("coords".into(), Value::Vector(motedb::types::ArcVec::new(vec![116.0 + i as f32 * 0.01, 39.0])));
row.insert("category".into(), Value::Text("restaurant".into()));
rows.push(row);
}
db.batch_insert_map("locations", rows)?;
db.flush()?;
let query_box = BoundingBox::new(116.0, 38.5, 117.0, 40.0);
let _hits = db.spatial_search("locations_coords", &query_box)?;
drop(db);
cleanup_dir(dir);
Ok(())
}
fn docs_timestamp_example_runs() -> Result<()> {
let (db, dir) = setup_db("timestamp_index")?;
db.execute(
"CREATE TABLE sensor_data (
id INT,
sensor_id INT,
value FLOAT,
ts TIMESTAMP
)",
)?;
let mut rows = Vec::new();
for i in 0..50 {
let mut row = HashMap::new();
row.insert("id".into(), Value::Integer(i));
row.insert("sensor_id".into(), Value::Integer(i % 4));
row.insert("value".into(), Value::Float((i as f64) * 0.1));
row.insert(
"ts".into(),
Value::Timestamp(Timestamp::from_secs(1_700_000_000 + i)),
);
rows.push(row);
}
db.batch_insert_map("sensor_data", rows)?;
db.flush()?;
let _ids = db.query_timestamp_range(1_700_000_005, 1_700_000_025)?;
drop(db);
cleanup_dir(dir);
Ok(())
}
fn docs_transaction_example_runs() -> Result<()> {
let (db, dir) = setup_db("transactions")?;
db.execute(
"CREATE TABLE accounts (
id INT,
balance INT
)",
)?;
db.execute("INSERT INTO accounts VALUES (1, 1000)")?;
db.execute("INSERT INTO accounts VALUES (2, 0)")?;
let tx = db.begin_transaction()?;
db.execute("UPDATE accounts SET balance = balance - 100 WHERE id = 1")?;
db.execute("UPDATE accounts SET balance = balance + 100 WHERE id = 2")?;
db.savepoint(tx, "after_transfer")?;
db.execute("UPDATE accounts SET balance = balance + 50 WHERE id = 2")?;
db.rollback_to_savepoint(tx, "after_transfer")?;
db.commit_transaction(tx)?;
let results = db.execute("SELECT balance FROM accounts ORDER BY id")?.materialize()?; assert_eq!(results.row_count(), 2);
drop(db);
cleanup_dir(dir);
Ok(())
}