Files
kakeibo/backend/src/db/indexes.rs
T
suphicakir 267d292a90 feat(birikim): varlıklar ve alım/satım kayıtları (ortalama maliyet)
- Varsayılan varlıklar (TL, altınlar, USD, EUR) + migration v2 ile eski hanelere ekleme
- Döviz ekleme, varlık adı/aktifliği
- Alım/satım ekle, düzenle, sil; eldekinden fazla satış (geriye tarihli dahil) engellenir
- Ağırlıklı ortalama maliyet ve gerçekleşen kâr/zarar (hesaplanır, saklanmaz)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-13 00:35:23 +03:00

121 lines
3.6 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 }),
],
),
(
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"
);
}
}
}