//! İndeksler (DATA_MODEL.md §4). Açılışta her seferinde çağrılır; //! MongoDB zaten var olan aynı indeksi yeniden oluşturmaz. use mongodb::{ Database, IndexModel, bson::{Document, doc}, error::Result, options::IndexOptions, }; use super::col; pub async fn ensure(db: &Database) -> Result<()> { for (collection, indexes) in definitions() { db.collection::(collection) .create_indexes(indexes) .await?; } tracing::info!("İndeksler hazır"); Ok(()) } fn idx(keys: Document) -> IndexModel { IndexModel::builder().keys(keys).build() } fn idx_opts(keys: Document, options: IndexOptions) -> IndexModel { IndexModel::builder().keys(keys).options(options).build() } fn unique() -> IndexOptions { IndexOptions::builder().unique(true).build() } fn definitions() -> Vec<(&'static str, Vec)> { let hp = || idx(doc! { "household_id": 1, "period_id": 1 }); let mut defs = vec![ (col::USERS, vec![idx_opts(doc! { "email": 1 }, unique())]), (col::INVITES, vec![idx_opts(doc! { "code": 1 }, unique())]), ( col::PERIODS, vec![ idx(doc! { "household_id": 1, "start_date": 1 }), // Hanede en fazla bir açık dönem idx_opts( doc! { "household_id": 1 }, IndexOptions::builder() .name("one_open_period_per_household".to_string()) .unique(true) .partial_filter_expression(doc! { "status": "open" }) .build(), ), ], ), ( col::CATEGORIES, vec![idx(doc! { "household_id": 1, "sort_order": 1 })], ), ( col::PAYMENT_METHODS, vec![idx(doc! { "household_id": 1, "sort_order": 1 })], ), (col::PLAN_ITEMS, vec![hp()]), (col::BUDGETS, vec![hp()]), (col::NOTES, vec![hp()]), ( col::ENTRIES, vec![ idx(doc! { "household_id": 1, "period_id": 1, "date": 1 }), idx(doc! { "household_id": 1, "date": 1 }), ], ), ( col::CARRYOVERS, vec![idx(doc! { "household_id": 1, "to_period_id": 1 })], ), ( col::ASSETS, vec![idx(doc! { "household_id": 1, "sort_order": 1 })], ), ( col::ASSET_TRANSACTIONS, vec![idx(doc! { "household_id": 1, "asset_id": 1, "date": 1 })], ), ( col::EXCHANGE_RATES, vec![idx(doc! { "code": 1, "household_id": 1, "rate_date": -1 })], ), ]; // Senkronizasyon (Faz 11): "şu tarihten sonra değişenler" sorgusu. for &name in col::HOUSEHOLD_SCOPED { let sync = idx(doc! { "household_id": 1, "updated_at": 1 }); match defs.iter_mut().find(|(c, _)| *c == name) { Some((_, list)) => list.push(sync), None => defs.push((name, vec![sync])), } } defs } #[cfg(test)] mod tests { use super::*; #[test] fn every_household_collection_has_sync_index() { let defs = definitions(); for &name in col::HOUSEHOLD_SCOPED { let (_, list) = defs.iter().find(|(c, _)| *c == name).unwrap(); assert!( list.iter() .any(|i| i.keys == doc! { "household_id": 1, "updated_at": 1 }), "{name} koleksiyonunda senkronizasyon indeksi yok" ); } } }