Files
kakeibo/backend/src/db/indexes.rs
T

143 lines
4.3 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
//! İ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::<Document>(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<IndexModel>)> {
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 }),
// Ekstre içe aktarmada tekrar tespiti
idx(doc! { "household_id": 1, "import_key": 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::IMPORT_RULES,
vec![idx(doc! { "household_id": 1, "pattern": 1 })],
),
(
col::DEBT_SNAPSHOTS,
vec![idx(doc! { "household_id": 1, "date": -1 })],
),
(
col::INSTALLMENT_PLANS,
vec![idx(doc! { "household_id": 1, "first_month": 1 })],
),
(
col::DEBT_TARGETS,
vec![idx(doc! { "household_id": 1, "month": 1 })],
),
(
col::RESTORE_POINTS,
vec![idx(doc! { "household_id": 1, "created_at": -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"
);
}
}
}