221 lines
8.8 KiB
Rust

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<Database>
}
#[async_trait]
impl CarRepository for MongoCarRepository {
async fn create(&self, car: BindingCar) -> Result<Car, String> {
let collection = self.database.collection::<Document>("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<Car, String> {
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<Vec<Car>, String> {
let collection = self.database.collection::<Document>("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<Car, String> {
let collection = self.database.collection::<Document>("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::<Document>("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<Vec<Report>, 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::<Document>("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::<Vec<_>>()
// .await;
Ok(reports)
}
}