use chrono::{Utc, Months}; use mongodb::Database; use mongodb::bson::oid::ObjectId; use mongodb::bson::{doc, Document, Bson}; use futures::stream::{TryStreamExt, StreamExt}; use mongodb::options::{FindOptions, AggregateOptions}; use rust_decimal::Decimal; use rust_decimal::prelude::{ToPrimitive, FromPrimitive}; use std::default; use std::sync::Arc; use crate::models::car::*; use crate::storages::traits::*; use async_trait::async_trait; use super::rent::MongoRentRepository; pub struct MongoCarRepository { pub database: Arc } #[async_trait] impl CarRepository for MongoCarRepository { async fn create(&self, car: BindingCar) -> Result { let collection = self.database.collection::("clients"); let document = doc! { "_id": ObjectId::parse_str(car.owner_id.clone()).unwrap() }; let update = doc! { "$push": { "cars": doc! { "_id": ObjectId::new(), "brand": car.brand.clone(), "model": car.model.clone(), "price": car.price.to_f64().unwrap(), "car_station_id": ObjectId::parse_str(car.car_station_id.to_string()).unwrap() } } }; let result = collection.update_one(document, update, None).await; match result { Ok(result) => { let brand = car.brand.clone(); let model = car.model.clone(); let price = car.price; let car_station_id = car.car_station_id.clone(); let owner_id = car.owner_id.clone(); let car = Car { id: "".to_string(), brand, model, car_station_id, price, owner_id}; Ok(car) }, Err(e) => Err(e.to_string()) } } async fn read(&self, id: String) -> Result { match self.read_all().await.unwrap().into_iter().filter(|c| c.id.starts_with(&id)).next() { Some(car) => Ok(car), None => Err("Car not found".to_string()) } } async fn read_all(&self) -> Result, String> { let collection = self.database.collection::("clients"); let mut cursor = collection.find(doc! {"cars": { "$exists": true }}, None).await.map_err(|e| e.to_string())?; let mut cars = Vec::new(); while let Some(result) = cursor.next().await { match result { Ok(document) => { for car_document in document.get_array("cars").unwrap() { let car = car_document.as_document().unwrap(); let id = car.get("_id").unwrap().as_object_id().unwrap().to_hex(); let brand = car.get_str("brand").unwrap().to_string(); let model = car.get_str("model").unwrap().to_string(); let price = Decimal::from_f64(car.get_f64("price").unwrap()).unwrap(); let car_station_id = car.get_object_id("car_station_id").unwrap().to_hex(); let owner_id = document.get_object_id("_id").unwrap().to_hex(); let car = Car { id, brand, model, car_station_id, price, owner_id}; cars.push(car); } }, Err(e) => return Err(e.to_string()) } } Ok(cars) } async fn update(&self, id: String, car: BindingCar) -> Result { let collection = self.database.collection::("clients"); let mut cursor = collection.find(None, None).await.map_err(|e| e.to_string())?; while let Some(result) = cursor.next().await { match result { Ok(document) => { for (i, car_document) in document.get_array("cars").unwrap().into_iter().enumerate() { if car_document.as_document().unwrap().get_object_id("_id").unwrap().to_hex().starts_with(&id) { collection.update_one(doc! { "_id": ObjectId::parse_str(document.get_str("_id").unwrap()).unwrap() }, doc! { "$set": doc! { format!("cars.${}.brand", i): car.brand.clone(), format!("cars.${}.model", i): car.model.clone(), format!("cars.${}.price", i): car.price.to_f64().unwrap(), format!("cars.${}.car_station_id", i): ObjectId::parse_str(car.car_station_id.clone()).unwrap(), } }, None).await.unwrap(); return Ok(Car {..Default::default()}) } } }, Err(e) => return Err(e.to_string()) } } Err("Car not found".to_string()) } async fn delete(&self, id: String) -> Result<(), String> { let collection = self.database.collection::("clients"); let document = doc! { "cars._id": ObjectId::parse_str(&id).unwrap() }; let update = doc! { "$pull": { "cars": { "_id": ObjectId::parse_str(&id).unwrap() } } }; let result = collection.update_one(document, update, None).await; match result { Ok(result) => { if result.modified_count == 0 { Err("Car not found".to_string()) } else { Ok(()) } }, Err(e) => Err(e.to_string()) } } async fn get_report(&self) -> Result, String> { let mut reports = Vec::new(); let rents = (MongoRentRepository {database: Arc::clone(&self.database)}).read_all().await.unwrap(); let cars = self.read_all().await.unwrap(); for car in cars { let mut times = 0; let mut sum = Decimal::from_f64(0.0).unwrap(); for rent in rents.iter() { if rent.car_id.starts_with(&car.id) && rent.time_amount.is_some() { times += 1; sum += car.price * Decimal::from(rent.time_amount.unwrap()); } } reports.push(Report {car_id: car.id.clone(), brand: car.brand.clone(), model: car.model.clone(), times, income: sum}) } // let pipeline = vec![ // doc! { // "$match": { // "rents.start_time": { // "$gte": Utc::now().checked_sub_months(Months::new(1)).unwrap(), // "$lt": Utc::now().checked_add_months(Months::new(1)).unwrap() // } // } // }, // doc! { // "$lookup": { // "from": "cars", // "localField": "rents.car_id", // "foreignField": "_id", // "as": "car" // } // }, // doc! { // "$unwind": "$car" // }, // doc! { // "$group": { // "_id": "$rents.car_id", // "brand": { "$first": "$car.brand" }, // "model": { "$first": "$car.model" }, // "times": { "$sum": 1 }, // "income": { "$sum": { "$multiply": [ "$car.price", "$rents.time_amount" ] } } // } // }, // doc! { // "$project": { // "_id": 0, // "car_id": { "$toString": "$_id" }, // "brand": 1, // "model": 1, // "times": 1, // "income": { "$round": [ "$income", 2 ] } // } // } // ]; // let collection = self.database.collection::("clients"); // let options = AggregateOptions::builder().build(); // let cursor = collection.aggregate(pipeline, options).await.unwrap(); // let reports = cursor // .map(|doc| { // let doc = doc.unwrap(); // let car_id = doc.get_str("car_id").unwrap().to_owned(); // let brand = doc.get_str("brand").unwrap().to_owned(); // let model = doc.get_str("model").unwrap().to_owned(); // let times = doc.get_i64("times").unwrap(); // let income = Decimal::from_f64(doc.get_f64("income").unwrap()).unwrap(); // Report { car_id, brand, model, times, income } // }) // .collect::>() // .await; Ok(reports) } }