Document Store: Use Cases
Document Store: Use Cases
Part of: HeliosDB Document Store User Guide
Use Case 1: Content Management System
Scenario: Blog platform with articles, authors, comments, and tags.
Schema:
// Articles collection{ "_id": "article_123", "title": "Introduction to HeliosDB", "slug": "introduction-to-heliosdb", "content": "HeliosDB is a next-generation database...", "author": { "id": "author_456", "name": "Alice Smith", "email": "alice@example.com" }, "tags": ["database", "nosql", "tutorial"], "status": "published", "publishedAt": "2025-01-15T10:00:00Z", "views": 1234, "comments": [ { "id": "comment_789", "author": "Bob", "text": "Great article!", "createdAt": "2025-01-15T11:00:00Z" } ], "metadata": { "readTime": 5, "featured": true }}Implementation:
use heliosdb_document::{DocumentStore, Collection, DocumentId, IndexDefinition, IndexType};use serde_json::json;
// Initialize storelet store = DocumentStore::new("./cms_data")?;let articles = Collection::new("articles");
// Create indexesstore.create_index(IndexDefinition { name: "slug_idx".to_string(), collection: articles.clone(), index_type: IndexType::SingleField { field: "slug".to_string(), unique: true, }, created_at: chrono::Utc::now(),})?;
store.create_index(IndexDefinition { name: "tags_idx".to_string(), collection: articles.clone(), index_type: IndexType::Multikey { field: "tags".to_string(), }, created_at: chrono::Utc::now(),})?;
store.create_index(IndexDefinition { name: "content_text_idx".to_string(), collection: articles.clone(), index_type: IndexType::Text { fields: vec!["title".to_string(), "content".to_string()], weights: { let mut w = std::collections::HashMap::new(); w.insert("title".to_string(), 3.0); w.insert("content".to_string(), 1.0); w }, }, created_at: chrono::Utc::now(),})?;
// Insert articlelet article = json!({ "title": "Introduction to HeliosDB", "slug": "introduction-to-heliosdb", "content": "HeliosDB is a next-generation multi-model database...", "author": { "id": "author_456", "name": "Alice Smith", "email": "alice@example.com" }, "tags": ["database", "nosql", "tutorial"], "status": "published", "publishedAt": chrono::Utc::now().to_rfc3339(), "views": 0, "comments": []});
store.insert(&articles, &DocumentId::new("article_123"), article)?;
// Query: Find articles by taglet filter = Filter { op: FilterOp::In { field: "tags".to_string(), values: vec![json!("database")], },};let tagged_articles = store.find(&articles, filter)?;
// Query: Full-text search// (Using MongoDB driver for text search)let cursor = collection.find(doc! { "$text": { "$search": "database tutorial" }}, None).await?;
// Query: Get article by sluglet filter = Filter::eq("slug", json!("introduction-to-heliosdb"));let article = store.find(&articles, filter)?.into_iter().next();
// Update: Increment view countstore.update( &articles, &DocumentId::new("article_123"), json!({"views": article.unwrap().data["views"].as_i64().unwrap() + 1}))?;
// Update: Add commentlet new_comment = json!({ "id": format!("comment_{}", uuid::Uuid::new_v4()), "author": "Bob Johnson", "text": "Great article!", "createdAt": chrono::Utc::now().to_rfc3339()});
// Get current comments and appendlet article = store.get(&articles, &DocumentId::new("article_123"))?.unwrap();let mut comments = article.data["comments"].as_array().unwrap().clone();comments.push(new_comment);
store.update( &articles, &DocumentId::new("article_123"), json!({"comments": comments}))?;Aggregation: Popular Tags:
use heliosdb_document::{AggregationStage, AggregationOp};
let pipeline = vec![ AggregationStage::Match { filter: Filter::eq("status", json!("published")), }, AggregationStage::Unwind { field: "tags".to_string(), preserve_null: false, }, AggregationStage::Group { id: Some(json!("$tags")), fields: { let mut fields = std::collections::HashMap::new(); fields.insert("count".to_string(), AggregationOp::Count); fields }, }, AggregationStage::Sort { fields: vec![("count".to_string(), SortOrder::Desc)], }, AggregationStage::Limit { count: 10 },];
let popular_tags = store.aggregate(&articles, pipeline)?;println!("Popular tags: {:?}", popular_tags);Use Case 2: E-Commerce Product Catalog
Scenario: Product catalog with categories, variants, pricing, and inventory.
Schema:
{ "_id": "product_laptop_001", "name": "UltraBook Pro 15", "sku": "UBP15-256-16", "description": "High-performance laptop for professionals", "category": "Electronics > Computers > Laptops", "brand": "TechCorp", "price": { "amount": 1299.99, "currency": "USD", "discounted": 1099.99, "taxRate": 0.08 }, "variants": [ { "sku": "UBP15-256-16-SLV", "color": "Silver", "storage": "256GB", "memory": "16GB", "price": 1299.99, "stock": 15 }, { "sku": "UBP15-512-32-BLK", "color": "Black", "storage": "512GB", "memory": "32GB", "price": 1699.99, "stock": 8 } ], "specifications": { "processor": "Intel Core i7", "display": "15.6\" 4K", "weight": "1.8kg", "battery": "10 hours" }, "images": [ "https://cdn.example.com/laptop-001-front.jpg", "https://cdn.example.com/laptop-001-side.jpg" ], "ratings": { "average": 4.5, "count": 128 }, "tags": ["laptop", "ultrabook", "professional", "4k"], "inStock": true, "createdAt": "2025-01-01T00:00:00Z", "updatedAt": "2025-01-15T10:00:00Z"}Implementation:
let store = DocumentStore::new("./ecommerce_data")?;let products = Collection::new("products");
// Indexes for e-commerce// 1. SKU (unique)store.create_index(IndexDefinition { name: "sku_idx".to_string(), collection: products.clone(), index_type: IndexType::SingleField { field: "sku".to_string(), unique: true, }, created_at: chrono::Utc::now(),})?;
// 2. Price range queriesstore.create_index(IndexDefinition { name: "price_idx".to_string(), collection: products.clone(), index_type: IndexType::SingleField { field: "price.amount".to_string(), unique: false, }, created_at: chrono::Utc::now(),})?;
// 3. Category navigationstore.create_index(IndexDefinition { name: "category_idx".to_string(), collection: products.clone(), index_type: IndexType::SingleField { field: "category".to_string(), unique: false, }, created_at: chrono::Utc::now(),})?;
// 4. Full-text searchstore.create_index(IndexDefinition { name: "search_idx".to_string(), collection: products.clone(), index_type: IndexType::Text { fields: vec![ "name".to_string(), "description".to_string(), "brand".to_string(), ], weights: { let mut w = std::collections::HashMap::new(); w.insert("name".to_string(), 3.0); w.insert("brand".to_string(), 2.0); w.insert("description".to_string(), 1.0); w }, }, created_at: chrono::Utc::now(),})?;
// Query: Products in price rangelet filter = Filter::and(vec![ Filter { op: FilterOp::Gte { field: "price.amount".to_string(), value: json!(500.0), }, }, Filter { op: FilterOp::Lte { field: "price.amount".to_string(), value: json!(1500.0), }, }, Filter::eq("inStock", json!(true)),]);
let products_in_range = store.find(&products, filter)?;
// Query: Products by categorylet filter = Filter { op: FilterOp::Regex { field: "category".to_string(), pattern: "^Electronics > Computers".to_string(), options: "i".to_string(), },};
let computers = store.find(&products, filter)?;
// Aggregation: Average price by categorylet pipeline = vec![ AggregationStage::Group { id: Some(json!("$category")), fields: { let mut fields = std::collections::HashMap::new(); fields.insert("avgPrice".to_string(), AggregationOp::Avg { field: "price.amount".to_string(), }); fields.insert("count".to_string(), AggregationOp::Count); fields.insert("minPrice".to_string(), AggregationOp::Min { field: "price.amount".to_string(), }); fields.insert("maxPrice".to_string(), AggregationOp::Max { field: "price.amount".to_string(), }); fields }, }, AggregationStage::Sort { fields: vec![("avgPrice".to_string(), SortOrder::Desc)], },];
let category_stats = store.aggregate(&products, pipeline)?;Inventory Management:
// Update stock when order placedfn reduce_stock( store: &DocumentStore, product_id: &str, variant_sku: &str, quantity: i32,) -> Result<()> { let products = Collection::new("products"); let doc_id = DocumentId::new(product_id);
// Get current product let product = store.get(&products, &doc_id)?.unwrap();
// Find variant and update stock let mut variants = product.data["variants"].as_array().unwrap().clone(); for variant in &mut variants { if variant["sku"].as_str() == Some(variant_sku) { let current_stock = variant["stock"].as_i64().unwrap(); if current_stock < quantity as i64 { return Err("Insufficient stock".into()); } variant["stock"] = json!(current_stock - quantity as i64); break; } }
// Update product store.update(&products, &doc_id, json!({ "variants": variants, "updatedAt": chrono::Utc::now().to_rfc3339() }))?;
Ok(())}Use Case 3: User Profiles and Preferences
Scenario: User management system with profiles, preferences, activity tracking.
Schema:
{ "_id": "user_alice_123", "username": "alice_smith", "email": "alice@example.com", "profile": { "firstName": "Alice", "lastName": "Smith", "avatar": "https://cdn.example.com/avatars/alice.jpg", "bio": "Software engineer and database enthusiast", "location": { "city": "San Francisco", "state": "CA", "country": "USA", "coordinates": { "type": "Point", "coordinates": [-122.4194, 37.7749] } } }, "preferences": { "theme": "dark", "language": "en", "timezone": "America/Los_Angeles", "notifications": { "email": true, "push": true, "sms": false }, "privacy": { "profileVisibility": "public", "showEmail": false, "showLocation": true } }, "activity": { "lastLogin": "2025-01-15T10:30:00Z", "loginCount": 456, "lastActive": "2025-01-15T14:20:00Z" }, "roles": ["user", "premium"], "tags": ["developer", "early-adopter"], "metadata": { "accountCreated": "2024-06-01T00:00:00Z", "emailVerified": true, "phoneVerified": false }}Implementation:
let store = DocumentStore::new("./user_data")?;let users = Collection::new("users");
// Schema validationlet schema = SchemaBuilder::new() .title("User Schema") .property("username", PropertyBuilder::string() .min_length(3) .max_length(20) .pattern("^[a-zA-Z0-9_]+$") .build()) .property("email", PropertyBuilder::string() .format("email") .build()) .property("profile", PropertyBuilder::object() .property("firstName", PropertyBuilder::string().min_length(1).build()) .property("lastName", PropertyBuilder::string().min_length(1).build()) .build()) .required(vec!["username", "email", "profile"]) .build();
store.register_schema("users", schema)?;
// Indexes// 1. Username (unique)store.create_index(IndexDefinition { name: "username_idx".to_string(), collection: users.clone(), index_type: IndexType::SingleField { field: "username".to_string(), unique: true, }, created_at: chrono::Utc::now(),})?;
// 2. Email (unique)store.create_index(IndexDefinition { name: "email_idx".to_string(), collection: users.clone(), index_type: IndexType::SingleField { field: "email".to_string(), unique: true, }, created_at: chrono::Utc::now(),})?;
// 3. Location (geospatial)store.create_index(IndexDefinition { name: "location_idx".to_string(), collection: users.clone(), index_type: IndexType::Geospatial { field: "profile.location.coordinates".to_string(), index_type: GeoIndexType::TwoDSphere, }, created_at: chrono::Utc::now(),})?;
// Create userlet user = json!({ "username": "alice_smith", "email": "alice@example.com", "profile": { "firstName": "Alice", "lastName": "Smith", "bio": "Software engineer", "location": { "city": "San Francisco", "state": "CA", "country": "USA", "coordinates": { "type": "Point", "coordinates": [-122.4194, 37.7749] } } }, "preferences": { "theme": "dark", "language": "en", "notifications": { "email": true, "push": true } }, "roles": ["user"], "metadata": { "accountCreated": chrono::Utc::now().to_rfc3339(), "emailVerified": false }});
store.insert(&users, &DocumentId::new("user_alice_123"), user)?;
// Update preferencesfn update_preferences( store: &DocumentStore, user_id: &str, prefs: serde_json::Value,) -> Result<()> { store.update( &Collection::new("users"), &DocumentId::new(user_id), json!({"preferences": prefs}) )?; Ok(())}
// Track loginfn track_login(store: &DocumentStore, user_id: &str) -> Result<()> { let users = Collection::new("users"); let doc_id = DocumentId::new(user_id);
let user = store.get(&users, &doc_id)?.unwrap(); let login_count = user.data["activity"]["loginCount"].as_i64().unwrap_or(0);
store.update(&users, &doc_id, json!({ "activity": { "lastLogin": chrono::Utc::now().to_rfc3339(), "loginCount": login_count + 1, "lastActive": chrono::Utc::now().to_rfc3339() } }))?;
Ok(())}
// Find nearby users (geospatial)// (Using MongoDB driver)let nearby = collection.find(doc! { "profile.location.coordinates": { "$near": { "$geometry": { "type": "Point", "coordinates": [-122.4194, 37.7749] // SF }, "$maxDistance": 10000 // 10km } }}, None).await?;Use Case 4: Event Logging and Analytics
Scenario: Application event logging with real-time analytics.
Schema:
{ "_id": "event_12345", "eventType": "page_view", "timestamp": "2025-01-15T14:30:00Z", "user": { "id": "user_456", "sessionId": "session_789" }, "page": { "url": "/products/laptop-001", "title": "UltraBook Pro 15", "referrer": "https://google.com" }, "device": { "type": "desktop", "os": "macOS", "browser": "Chrome", "screenResolution": "1920x1080" }, "location": { "country": "USA", "region": "California", "city": "San Francisco", "ip": "192.168.1.100" }, "metadata": { "duration": 45, // seconds "scrollDepth": 75 // percent }}Implementation:
let store = DocumentStore::new("./events_data")?;let events = Collection::new("events");
// Indexes optimized for time-series queries// 1. Timestamp (for time-range queries)store.create_index(IndexDefinition { name: "timestamp_idx".to_string(), collection: events.clone(), index_type: IndexType::SingleField { field: "timestamp".to_string(), unique: false, }, created_at: chrono::Utc::now(),})?;
// 2. Event type + timestamp (compound)store.create_index(IndexDefinition { name: "type_time_idx".to_string(), collection: events.clone(), index_type: IndexType::Compound { fields: vec!["eventType".to_string(), "timestamp".to_string()], unique: false, }, created_at: chrono::Utc::now(),})?;
// 3. User ID (for user analytics)store.create_index(IndexDefinition { name: "user_idx".to_string(), collection: events.clone(), index_type: IndexType::SingleField { field: "user.id".to_string(), unique: false, }, created_at: chrono::Utc::now(),})?;
// Log eventfn log_event( store: &DocumentStore, event_type: &str, user_id: &str, data: serde_json::Value,) -> Result<()> { let event = json!({ "eventType": event_type, "timestamp": chrono::Utc::now().to_rfc3339(), "user": { "id": user_id }, "data": data });
store.insert( &Collection::new("events"), &DocumentId::new(&format!("event_{}", uuid::Uuid::new_v4())), event )?;
Ok(())}
// Real-time analytics aggregationuse heliosdb_document::{AggregationStage, AggregationOp};
// Events per hour (last 24 hours)let pipeline = vec![ AggregationStage::Match { filter: Filter { op: FilterOp::Gte { field: "timestamp".to_string(), value: json!( (chrono::Utc::now() - chrono::Duration::hours(24)) .to_rfc3339() ), }, }, }, AggregationStage::Group { id: Some(json!({ "hour": { "$hour": "$timestamp" }, "type": "$eventType" })), fields: { let mut fields = std::collections::HashMap::new(); fields.insert("count".to_string(), AggregationOp::Count); fields }, }, AggregationStage::Sort { fields: vec![ ("_id.hour".to_string(), SortOrder::Asc), ("count".to_string(), SortOrder::Desc), ], },];
let hourly_stats = store.aggregate(&events, pipeline)?;
// Top pages by viewslet pipeline = vec![ AggregationStage::Match { filter: Filter::eq("eventType", json!("page_view")), }, AggregationStage::Group { id: Some(json!("$page.url")), fields: { let mut fields = std::collections::HashMap::new(); fields.insert("views".to_string(), AggregationOp::Count); fields.insert("avgDuration".to_string(), AggregationOp::Avg { field: "metadata.duration".to_string(), }); fields }, }, AggregationStage::Sort { fields: vec![("views".to_string(), SortOrder::Desc)], }, AggregationStage::Limit { count: 10 },];
let top_pages = store.aggregate(&events, pipeline)?;
// User journey (session replay)let filter = Filter::eq("user.sessionId", json!("session_789"));let session_events = store.find(&events, filter)?;// Sort by timestamplet mut events_sorted = session_events;events_sorted.sort_by(|a, b| { a.data["timestamp"] .as_str() .cmp(&b.data["timestamp"].as_str())});Use Case 5: Mobile App Backend
Scenario: Offline-first mobile app with sync.
Schema:
{ "_id": "note_uuid_123", "userId": "user_456", "title": "Meeting Notes", "content": "Discussed Q1 roadmap...", "tags": ["work", "meeting"], "attachments": [ { "id": "attach_789", "filename": "roadmap.pdf", "url": "https://cdn.example.com/files/roadmap.pdf", "size": 1024000 } ], "metadata": { "createdAt": "2025-01-15T10:00:00Z", "updatedAt": "2025-01-15T14:30:00Z", "deviceId": "device_mobile_001", "version": 3, // For conflict resolution "synced": true }}Implementation:
// Mobile app: Store locally and syncuse heliosdb_document::{DocumentStore, Collection, DocumentId};
let store = DocumentStore::new("./mobile_data")?;let notes = Collection::new("notes");
// Create note offlinefn create_note_offline( store: &DocumentStore, user_id: &str, title: &str, content: &str,) -> Result<String> { let note_id = format!("note_{}", uuid::Uuid::new_v4());
let note = json!({ "userId": user_id, "title": title, "content": content, "tags": [], "metadata": { "createdAt": chrono::Utc::now().to_rfc3339(), "updatedAt": chrono::Utc::now().to_rfc3339(), "version": 1, "synced": false // Not yet synced } });
store.insert( &Collection::new("notes"), &DocumentId::new(¬e_id), note )?;
Ok(note_id)}
// Sync to serverasync fn sync_to_server(store: &DocumentStore) -> Result<()> { let notes = Collection::new("notes");
// Find unsynced notes let filter = Filter::eq("metadata.synced", json!(false)); let unsynced = store.find(¬es, filter)?;
for note in unsynced { // Upload to server let response = upload_to_server(¬e.data).await?;
// Mark as synced store.update( ¬es, ¬e.metadata.id, json!({"metadata": {"synced": true}}) )?; }
Ok(())}
// Conflict resolution (last-write-wins with version)fn merge_note( store: &DocumentStore, note_id: &str, local_note: &Document, server_note: &serde_json::Value,) -> Result<()> { let local_version = local_note.data["metadata"]["version"].as_i64().unwrap(); let server_version = server_note["metadata"]["version"].as_i64().unwrap();
if server_version > local_version { // Server is newer, update local store.update( &Collection::new("notes"), &DocumentId::new(note_id), server_note.clone() )?; } else if local_version > server_version { // Local is newer, push to server upload_to_server(&local_note.data).await?; } // If equal, no action needed
Ok(())}Use Case 6: Real-Time Collaboration
Scenario: Collaborative document editing (like Google Docs).
Schema:
{ "_id": "doc_collaborative_123", "title": "Project Proposal", "owner": "user_alice", "collaborators": [ { "userId": "user_bob", "role": "editor", "addedAt": "2025-01-10T00:00:00Z" }, { "userId": "user_carol", "role": "viewer", "addedAt": "2025-01-12T00:00:00Z" } ], "content": { "blocks": [ { "id": "block_1", "type": "heading", "text": "Introduction", "version": 5 }, { "id": "block_2", "type": "paragraph", "text": "This proposal outlines...", "version": 12 } ] }, "activeEditors": [ { "userId": "user_bob", "cursor": { "blockId": "block_2", "position": 45 }, "lastActive": "2025-01-15T14:30:00Z" } ], "version": 17, "metadata": { "createdAt": "2025-01-10T00:00:00Z", "updatedAt": "2025-01-15T14:30:00Z" }}Implementation:
// Change streams for real-time updateslet mut stream = store.watch(Collection::new("documents"))?;
// Broadcast changes to all connected clientstokio::spawn(async move { while let Some(event) = stream.next().await.unwrap() { match event.event_type { ChangeEventType::Update => { if let Some(desc) = event.update_description { // Broadcast to WebSocket clients let update_msg = json!({ "type": "document_update", "documentId": event.document_id.as_str(), "changes": desc.updated_fields, "version": desc.updated_fields.get("version") });
broadcast_to_collaborators( event.document_id.as_str(), update_msg ).await; } } _ => {} } }});
// Update document blockfn update_block( store: &DocumentStore, doc_id: &str, block_id: &str, new_text: &str,) -> Result<()> { let docs = Collection::new("documents"); let doc_id_obj = DocumentId::new(doc_id);
// Get document let doc = store.get(&docs, &doc_id_obj)?.unwrap();
// Update specific block let mut blocks = doc.data["content"]["blocks"].as_array().unwrap().clone(); for block in &mut blocks { if block["id"].as_str() == Some(block_id) { block["text"] = json!(new_text); let version = block["version"].as_i64().unwrap(); block["version"] = json!(version + 1); break; } }
// Update document let doc_version = doc.data["version"].as_i64().unwrap(); store.update(&docs, &doc_id_obj, json!({ "content": { "blocks": blocks }, "version": doc_version + 1, "metadata": { "updatedAt": chrono::Utc::now().to_rfc3339() } }))?;
Ok(())}
// Track active editorsfn update_cursor( store: &DocumentStore, doc_id: &str, user_id: &str, block_id: &str, position: i32,) -> Result<()> { let docs = Collection::new("documents"); let doc_id_obj = DocumentId::new(doc_id);
let doc = store.get(&docs, &doc_id_obj)?.unwrap();
// Update active editors list let mut editors = doc.data["activeEditors"].as_array().unwrap().clone(); let mut found = false;
for editor in &mut editors { if editor["userId"].as_str() == Some(user_id) { editor["cursor"] = json!({ "blockId": block_id, "position": position }); editor["lastActive"] = json!(chrono::Utc::now().to_rfc3339()); found = true; break; } }
if !found { editors.push(json!({ "userId": user_id, "cursor": { "blockId": block_id, "position": position }, "lastActive": chrono::Utc::now().to_rfc3339() })); }
store.update(&docs, &doc_id_obj, json!({ "activeEditors": editors }))?;
Ok(())}Navigation: ← Previous: Change Streams | Back to Index | Next: Performance Optimization →