221 lines
8.8 KiB
Rust
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)
|
|
}
|
|
} |