From 08167143aa2331ab6816c8c232a22fc1b1eda272 Mon Sep 17 00:00:00 2001 From: Akulij Date: Fri, 25 Apr 2025 22:44:37 +0300 Subject: [PATCH] migrate db to mongodb from diesel(postgres) --- Cargo.lock | 188 +--------------------------- Cargo.toml | 7 +- src/db/mod.rs | 325 ++++++++++++++++++++++++++---------------------- src/db/tests.rs | 3 +- src/main.rs | 4 +- 5 files changed, 183 insertions(+), 344 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 0b74fbb..fc88205 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -133,18 +133,6 @@ version = "0.22.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" -[[package]] -name = "bb8" -version = "0.8.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d89aabfae550a5c44b43ab941844ffcd2e993cb6900b342debf59e9ea74acdb8" -dependencies = [ - "async-trait", - "futures-util", - "parking_lot", - "tokio", -] - [[package]] name = "bitflags" version = "1.3.2" @@ -499,69 +487,6 @@ dependencies = [ "unicode-xid", ] -[[package]] -name = "diesel" -version = "2.2.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "470eb10efc8646313634c99bb1593f402a6434cbd86e266770c6e39219adb86a" -dependencies = [ - "bitflags 2.9.0", - "byteorder", - "chrono", - "diesel_derives", - "itoa", - "pq-sys", -] - -[[package]] -name = "diesel-async" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "51a307ac00f7c23f526a04a77761a0519b9f0eb2838ebf5b905a58580095bdcb" -dependencies = [ - "async-trait", - "bb8", - "diesel", - "futures-util", - "scoped-futures", - "tokio", - "tokio-postgres", -] - -[[package]] -name = "diesel-derive-enum" -version = "2.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81c5131a2895ef64741dad1d483f358c2a229a3a2d1b256778cdc5e146db64d4" -dependencies = [ - "heck 0.4.1", - "proc-macro2", - "quote", - "syn 2.0.100", -] - -[[package]] -name = "diesel_derives" -version = "2.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a93958254b70bea63b4187ff73d10180599d9d8d177071b7f91e6da4e0c0ad55" -dependencies = [ - "diesel_table_macro_syntax", - "dsl_auto_type", - "proc-macro2", - "quote", - "syn 2.0.100", -] - -[[package]] -name = "diesel_table_macro_syntax" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "209c735641a413bc68c4923a9d6ad4bcb3ca306b794edaa7eb0b3228a99ffb25" -dependencies = [ - "syn 2.0.100", -] - [[package]] name = "digest" version = "0.10.7" @@ -599,20 +524,6 @@ dependencies = [ "futures", ] -[[package]] -name = "dsl_auto_type" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "139ae9aca7527f85f26dd76483eb38533fd84bd571065da1739656ef71c5ff5b" -dependencies = [ - "darling", - "either", - "heck 0.5.0", - "proc-macro2", - "quote", - "syn 2.0.100", -] - [[package]] name = "either" version = "1.15.0" @@ -628,7 +539,7 @@ version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a1e6a265c649f3f5979b601d26f1d05ada116434c87741c9493cb56218f76cbc" dependencies = [ - "heck 0.5.0", + "heck", "proc-macro2", "quote", "syn 2.0.100", @@ -714,12 +625,6 @@ dependencies = [ "pin-project-lite", ] -[[package]] -name = "fallible-iterator" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4443176a9f2c162692bd3d352d745ef9413eec5782a80d8fd6f8a1ac692a07f7" - [[package]] name = "fastrand" version = "2.3.0" @@ -927,16 +832,13 @@ name = "gongbotrs" version = "0.1.0" dependencies = [ "async-trait", - "bb8", "bson", "chrono", "chrono-tz", - "diesel", - "diesel-async", - "diesel-derive-enum", "dotenvy", "enum_stringify", "envconfig", + "futures", "mongodb", "serde", "teloxide", @@ -969,12 +871,6 @@ dependencies = [ "hashbrown 0.15.2", ] -[[package]] -name = "heck" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" - [[package]] name = "heck" version = "0.5.0" @@ -1858,35 +1754,6 @@ version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" -[[package]] -name = "postgres-protocol" -version = "0.6.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76ff0abab4a9b844b93ef7b81f1efc0a366062aaef2cd702c76256b5dc075c54" -dependencies = [ - "base64 0.22.1", - "byteorder", - "bytes", - "fallible-iterator", - "hmac", - "md-5", - "memchr", - "rand 0.9.0", - "sha2", - "stringprep", -] - -[[package]] -name = "postgres-types" -version = "0.2.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "613283563cd90e1dfc3518d548caee47e0e725455ed619881f5cf21f36de4b48" -dependencies = [ - "bytes", - "fallible-iterator", - "postgres-protocol", -] - [[package]] name = "powerfmt" version = "0.2.0" @@ -1902,16 +1769,6 @@ dependencies = [ "zerocopy 0.8.24", ] -[[package]] -name = "pq-sys" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41c852911b98f5981956037b2ca976660612e548986c30af075e753107bc3400" -dependencies = [ - "libc", - "vcpkg", -] - [[package]] name = "proc-macro-error-attr2" version = "2.0.0" @@ -2250,15 +2107,6 @@ dependencies = [ "windows-sys 0.59.0", ] -[[package]] -name = "scoped-futures" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b24aae2d0636530f359e9d5ef0c04669d11c5e756699b27a6a6d845d8329091" -dependencies = [ - "pin-project-lite", -] - [[package]] name = "scopeguard" version = "1.2.0" @@ -2535,7 +2383,7 @@ checksum = "4e9f90acc5ab146a99bf5061a7eb4976b573f560bc898ef3bf8435448dd5e7ad" dependencies = [ "dotenvy", "either", - "heck 0.5.0", + "heck", "hex", "once_cell", "proc-macro2", @@ -2765,7 +2613,7 @@ version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3118a980ed2ec11f73d9495a6606905bd74726e3ffe95a42fbeb187ded8fdbf4" dependencies = [ - "heck 0.5.0", + "heck", "proc-macro2", "quote", "syn 2.0.100", @@ -2899,7 +2747,6 @@ dependencies = [ "bytes", "libc", "mio", - "parking_lot", "pin-project-lite", "signal-hook-registry", "socket2", @@ -2928,32 +2775,6 @@ dependencies = [ "tokio", ] -[[package]] -name = "tokio-postgres" -version = "0.7.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c95d533c83082bb6490e0189acaa0bbeef9084e60471b696ca6988cd0541fb0" -dependencies = [ - "async-trait", - "byteorder", - "bytes", - "fallible-iterator", - "futures-channel", - "futures-util", - "log", - "parking_lot", - "percent-encoding", - "phf", - "pin-project-lite", - "postgres-protocol", - "postgres-types", - "rand 0.9.0", - "socket2", - "tokio", - "tokio-util", - "whoami", -] - [[package]] name = "tokio-rustls" version = "0.24.1" @@ -3308,7 +3129,6 @@ checksum = "6994d13118ab492c3c80c1f81928718159254c53c472bf9ce36f8dae4add02a7" dependencies = [ "redox_syscall", "wasite", - "web-sys", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index b3f8270..137e829 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,16 +7,13 @@ edition = "2021" [dependencies] async-trait = "0.1.88" -bb8 = "0.8.6" bson = { version = "2.14.0", features = ["chrono-0_4"] } -chrono = "0.4.40" +chrono = { version = "0.4.40", features = ["serde"] } chrono-tz = "0.10.3" -diesel = { version = "2.2.8", features = ["postgres", "chrono"] } -diesel-async = { version = "0.5.2", features = ["bb8", "postgres"] } -diesel-derive-enum = "2.1.0" dotenvy = "0.15.7" enum_stringify = "0.6.3" envconfig = "0.11.0" +futures = "0.3.31" mongodb = "3.2.3" serde = { version = "1.0.219", features = ["derive", "serde_derive"] } teloxide = { version = "0.14.0", features = ["macros", "postgres-storage-nativetls"] } diff --git a/src/db/mod.rs b/src/db/mod.rs index 6730f8b..c0aa15d 100644 --- a/src/db/mod.rs +++ b/src/db/mod.rs @@ -1,22 +1,15 @@ -pub mod models; -pub mod schema; - -use std::os::unix::process::CommandExt; - -use self::models::*; - use async_trait::async_trait; -use bb8::PooledConnection; -use chrono::Utc; -use diesel::prelude::*; -use diesel::query_builder::NoFromClause; -use diesel::query_builder::SelectStatement; -use diesel_async::pooled_connection::bb8::Pool; -use diesel_async::pooled_connection::AsyncDieselConnectionManager; -use diesel_async::AsyncConnection; -use diesel_async::AsyncPgConnection; -use diesel_async::RunQueryDsl; +use chrono::{DateTime, Utc}; use enum_stringify::EnumStringify; +use futures::stream::{StreamExt, TryStreamExt}; + +use mongodb::Database; +use mongodb::{ + bson::doc, + options::{ClientOptions, ResolverConfig}, + Client, +}; +use serde::{Deserialize, Serialize}; #[derive(EnumStringify)] #[enum_stringify(case = "flat")] @@ -29,85 +22,124 @@ pub trait GetReservationStatus { fn get_status(&self) -> Option; } -impl GetReservationStatus for models::Reservation { - fn get_status(&self) -> Option { - ReservationStatus::try_from(self.status.clone()).ok() - } +//impl GetReservationStatus for models::Reservation { +// fn get_status(&self) -> Option { +// ReservationStatus::try_from(self.status.clone()).ok() +// } +//} +#[derive(Serialize, Deserialize, Default)] +pub struct User { + pub _id: bson::oid::ObjectId, + pub id: i64, + pub is_admin: bool, + pub first_name: String, + pub last_name: Option, + pub username: Option, + pub language_code: Option, +} + +#[derive(Serialize, Deserialize)] +pub struct Message { + pub _id: bson::oid::ObjectId, + pub chat_id: i64, + pub message_id: i64, + pub token: String, +} + +#[derive(Serialize, Deserialize)] +pub struct Literal { + pub _id: bson::oid::ObjectId, + pub token: String, + pub value: String, +} + +#[derive(Serialize, Deserialize)] +pub struct Event { + pub _id: bson::oid::ObjectId, + pub time: DateTime, +} + +#[derive(Serialize, Deserialize)] +pub struct Media { + pub _id: bson::oid::ObjectId, + pub token: String, + pub media_type: String, + pub file_id: String, + pub media_group_id: Option, } #[derive(Clone)] pub struct DB { - pool: diesel_async::pooled_connection::bb8::Pool, + client: Client, } impl DB { pub async fn new>(db_url: S) -> Self { - let config = AsyncDieselConnectionManager::::new(db_url); - let pool = Pool::builder().build(config).await.unwrap(); - DB { pool } + let options = ClientOptions::parse(db_url.into()).await.unwrap(); + let client = Client::with_options(options).unwrap(); + + DB { client } } } #[async_trait] impl CallDB for DB { - async fn get_pool( - &mut self, - ) -> PooledConnection<'_, AsyncDieselConnectionManager> { - self.pool.get().await.unwrap() + async fn get_database(&mut self) -> Database { + self.client.database("gongbot") } } #[async_trait] pub trait CallDB { //type C; - async fn get_pool( - &mut self, - ) -> PooledConnection<'_, AsyncDieselConnectionManager>; + async fn get_database(&mut self) -> Database; //async fn get_pool(&mut self) -> PooledConnection<'_, AsyncDieselConnectionManager>; async fn get_users(&mut self) -> Vec { - use self::schema::users::dsl::*; - let mut conn = self.get_pool().await; + let db = self.get_database().await; + let users = db.collection::("users"); users - .filter(id.gt(0)) - .load::(&mut conn) + .find(doc! {}) .await .unwrap() + .map(|u| u.unwrap()) + .collect() + .await } async fn set_admin(&mut self, userid: i64, isadmin: bool) { - use self::schema::users::dsl::*; - let mut conn = self.get_pool().await; - diesel::update(users) - .filter(id.eq(userid)) - .set(is_admin.eq(isadmin)) - .execute(&mut conn) + let db = self.get_database().await; + let users = db.collection::("users"); + users + .update_one( + doc! { + "id": userid + }, + doc! { + "$set": { "is_admin": isadmin } + }, + ) .await .unwrap(); } async fn get_or_init_user(&mut self, userid: i64, firstname: &str) -> User { - use self::schema::users::dsl::*; - let conn = &mut self.get_pool().await; + let db = self.get_database().await; + let users = db.collection::("users"); - let user = users - .filter(id.eq(userid)) - .first::(conn) + users + .update_one( + doc! { "id": userid }, + doc! { "$set": { "first_name": firstname } }, + ) + .upsert(true) .await - .optional() .unwrap(); - match user { - Some(existing_user) => existing_user, - None => diesel::insert_into(users) - .values(( - id.eq(userid as i64), - is_admin.eq(false), - first_name.eq(firstname), - )) - .get_result(conn) - .await - .unwrap(), - } + users + .find_one(doc! { "id": userid }) + .await + .unwrap() + .expect("no such user created") } async fn get_message( @@ -115,15 +147,12 @@ pub trait CallDB { chatid: i64, messageid: i32, ) -> Result, Box> { - use self::schema::messages::dsl::*; - let conn = &mut self.get_pool().await; + let db = self.get_database().await; + let messages = db.collection::("messages"); let msg = messages - .filter(chat_id.eq(chatid)) - .filter(message_id.eq(messageid as i64)) - .first::(conn) - .await - .optional()?; + .find_one(doc! { "chat_id": chatid, "message_id": messageid as i64 }) + .await?; Ok(msg) } @@ -143,29 +172,21 @@ pub trait CallDB { messageid: i32, literal: &str, ) -> Result<(), Box> { - use self::schema::messages::dsl::*; - let msg = self.get_message(chatid, messageid).await?; - let conn = &mut self.get_pool().await; + let db = self.get_database().await; + let messages = db.collection::("messages"); - match msg { - Some(msg) => { - diesel::update(messages) - .filter(id.eq(msg.id)) - .set(token.eq(literal)) - .execute(conn) - .await?; - } - None => { - diesel::insert_into(messages) - .values(( - chat_id.eq(chatid), - message_id.eq(messageid as i64), - token.eq(literal), - )) - .execute(conn) - .await?; - } - }; + messages + .update_one( + doc! { + "chat_id": chatid, + "message_id": messageid as i64 + }, + doc! { + "$set": { "token": literal } + }, + ) + .upsert(true) + .await?; Ok(()) } @@ -174,14 +195,10 @@ pub trait CallDB { &mut self, literal: &str, ) -> Result, Box> { - use self::schema::literals::dsl::*; - let conn = &mut self.get_pool().await; + let db = self.get_database().await; + let messages = db.collection::("messages"); - let literal = literals - .filter(token.eq(literal)) - .first::(conn) - .await - .optional()?; + let literal = messages.find_one(doc! { "token": literal }).await?; Ok(literal) } @@ -200,50 +217,59 @@ pub trait CallDB { literal: &str, valuestr: &str, ) -> Result<(), Box> { - use self::schema::literals::dsl::*; - let conn = &mut self.get_pool().await; + let db = self.get_database().await; + let literals = db.collection::("literals"); - diesel::insert_into(literals) - .values((token.eq(literal), value.eq(valuestr))) - .on_conflict(token) - .do_update() - .set(value.eq(valuestr)) - .execute(conn) + literals + .update_one( + doc! { "token": literal }, + doc! { "$set": { "value": valuestr } }, + ) + .upsert(true) .await?; Ok(()) } async fn get_all_events(&mut self) -> Vec { - use self::schema::events::dsl::*; - let mut conn = self.get_pool().await; + let db = self.get_database().await; + let events = db.collection::("events"); + events - .filter(id.gt(0)) - .load::(&mut conn) + .find(doc! {}) .await .unwrap() + .map(|e| e.unwrap()) + .collect() + .await } async fn create_event( &mut self, event_datetime: chrono::DateTime, ) -> Result> { - use self::schema::events::dsl::*; - let conn = &mut self.get_pool().await; + let db = self.get_database().await; + let events = db.collection::("events"); - let new_event = diesel::insert_into(events) - .values((time.eq(event_datetime),)) - .get_result::(conn) - .await?; + let new_event = Event { + _id: bson::oid::ObjectId::new(), + time: event_datetime, + }; + + events.insert_one(&new_event).await?; Ok(new_event) } async fn get_media(&mut self, literal: &str) -> Result, Box> { - use self::schema::media::dsl::*; - let conn = &mut self.get_pool().await; + let db = self.get_database().await; + let media = db.collection::("media"); - let media_items = media.filter(token.eq(literal)).load::(conn).await?; + let media_items = media + .find(doc! { "token": literal }) + .await? + .try_collect() + .await?; Ok(media_items) } @@ -252,13 +278,11 @@ pub trait CallDB { &mut self, media_group: &str, ) -> Result> { - use self::schema::media::dsl::*; - let conn = &mut self.get_pool().await; + let db = self.get_database().await; + let media = db.collection::("media"); let is_exists = media - .filter(media_group_id.eq(media_group)) - .count() - .get_result::(conn) + .count_documents(doc! { "media_group_id": media_group }) .await? > 0; @@ -266,14 +290,15 @@ pub trait CallDB { } async fn drop_media(&mut self, literal: &str) -> Result> { - use self::schema::media::dsl::*; - let conn = &mut self.get_pool().await; + let db = self.get_database().await; + let media = db.collection::("media"); - let deleted_count = diesel::delete(media.filter(token.eq(literal))) - .execute(conn) - .await?; + let deleted_count = media + .delete_many(doc! { "token": literal }) + .await? + .deleted_count; - Ok(deleted_count) + Ok(deleted_count as usize) } async fn drop_media_except( @@ -281,20 +306,18 @@ pub trait CallDB { literal: &str, except_group: &str, ) -> Result> { - use self::schema::media::dsl::*; - let conn = &mut self.get_pool().await; + let db = self.get_database().await; + let media = db.collection::("media"); - let deleted_count = diesel::delete( - media.filter( - token - .eq(literal) - .and(media_group_id.ne(except_group).or(media_group_id.is_null())), - ), - ) - .execute(conn) - .await?; + let deleted_count = media + .delete_many(doc! { + "token": literal, + "media_group_id": { "$ne": except_group } + }) + .await? + .deleted_count; - Ok(deleted_count) + Ok(deleted_count as usize) } async fn add_media( @@ -304,18 +327,18 @@ pub trait CallDB { fileid: &str, media_group: Option<&str>, ) -> Result> { - use self::schema::media::dsl::*; - let conn = &mut self.get_pool().await; + let db = self.get_database().await; + let media = db.collection::("media"); - let new_media = diesel::insert_into(media) - .values(( - token.eq(literal), - media_type.eq(mediatype), - file_id.eq(fileid), - media_group_id.eq(media_group), - )) - .get_result::(conn) - .await?; + let new_media = Media { + _id: bson::oid::ObjectId::new(), + token: literal.to_string(), + media_type: mediatype.to_string(), + file_id: fileid.to_string(), + media_group_id: media_group.map(|g| g.to_string()), + }; + + media.insert_one(&new_media).await?; Ok(new_media) } diff --git a/src/db/tests.rs b/src/db/tests.rs index 7469a0c..0665d4c 100644 --- a/src/db/tests.rs +++ b/src/db/tests.rs @@ -1,7 +1,6 @@ -use diesel::Connection; -use diesel_async::AsyncPgConnection; use dotenvy; +use super::CallDB; use super::DB; async fn setup_db() -> DB { diff --git a/src/main.rs b/src/main.rs index 9690692..229c3e2 100644 --- a/src/main.rs +++ b/src/main.rs @@ -84,7 +84,7 @@ async fn main() -> Result<(), Box> { for event in events { match db.clone().create_event(event).await { - Ok(e) => println!("Created event {}", e.id), + Ok(e) => println!("Created event {}", e._id), Err(err) => println!("Failed to create event, error: {}", err), } } @@ -551,7 +551,7 @@ async fn make_start_buttons(db: &mut DB) -> InlineKeyboardMarkup { .map(|e| { vec![InlineKeyboardButton::callback( e.time.with_timezone(&Asia::Dubai).to_string(), - format!("event:{}", e.id), + format!("event:{}", e._id), )] }) .collect();