thedodd / wither

An ODM for MongoDB built on the official MongoDB Rust driver.
https://docs.rs/wither
Other
325 stars 40 forks source link

r2d2 support? #41

Closed moh-abk closed 5 years ago

moh-abk commented 5 years ago

Just started to use the crate and can't get it to work as I'm using r2d2-mongodb to create a pooled connection. This works with the pure rust driver.

this works

#[get("/hello")]
pub fn hello_world(connection: DbConn) -> JsonValue {
    let db = &connection;
    db.collection("hello")
        .insert_one(doc! { "name": "John" }, None)
        .unwrap();
    json!({ "status": "ok"})
}

this doesn't work

pub fn all_customer_country_query(
    connection: DbConn,
) -> Result<Vec<RestaurantCountry>, MongoDBError> {
    let db = &connection;
    RestaurantCountry::find(db.clone(), None, None)
}

error

error[E0308]: mismatched types
  --> src/restaurants/repository.rs:11:29
   |
11 |     RestaurantCountry::find(db.clone(), None, None)
   |                             ^^^^^^^^^^ expected struct `std::sync::Arc`, found reference
   |
   = note: expected type `std::sync::Arc<mongodb::db::DatabaseInner>`
              found type `&connection::DbConn`
thedodd commented 5 years ago

Hey @moh-abk. Thanks for submitting this issue. So, the wither::Model methods all expect the standard mongo driver's DB connection type which comes from the client type.

I looked through the docs in r2d2-mongodb and the associated crates, and I can not seem to find a type called connection::DbConn (referenced in the error message you posted). Where does that come from? Once I can see the docs on that type, I can help with a path forward on better integration.

moh-abk commented 5 years ago

connection code below @thedodd

use r2d2;
use r2d2::ManageConnection;
use r2d2_mongodb::{ConnectionOptions, MongodbConnectionManager};
use rocket::http::Status;
use rocket::request::{self, FromRequest};
use rocket::{Outcome, Request, State};
use std::env;
use std::ops::Deref;

type Pool = r2d2::Pool<MongodbConnectionManager>;

pub fn init_pool() -> Pool {
    let mongo_addr = env::var("MONGO_ADDR").expect("MONGO_ADDR must be set");
    let mongo_port = env::var("MONGO_PORT").expect("MONGO_PORT must be set");
    let db_name = env::var("MONGO_DB_NAME").expect("MONGO_DB_NAME env var must be set");
    let db_username = env::var("MONGO_DB_USER").expect("MONGO_DB_USER env var must be set");
    let db_password = env::var("MONGO_DB_PASSWORD").expect("MONGO_DB_PASSWORD env var must be set");

    let manager = MongodbConnectionManager::new(
        ConnectionOptions::builder()
            .with_host(&mongo_addr, mongo_port.parse::<u16>().unwrap())
            .with_db(&db_name)
            .with_auth(&db_username, &db_password)
            .build(),
    );
    match Pool::builder().max_size(64).build(manager) {
        Ok(pool) => pool,
        Err(e) => panic!("Error: failed to create mongodb pool {}", e),
    }
}

pub struct DbConn(pub r2d2::PooledConnection<MongodbConnectionManager>);

impl<'a, 'r> FromRequest<'a, 'r> for DbConn {
    type Error = ();

    fn from_request(request: &'a Request<'r>) -> request::Outcome<DbConn, Self::Error> {
        let pool = request.guard::<State<Pool>>()?;
        match pool.get() {
            Ok(conn) => Outcome::Success(DbConn(conn)),
            Err(_) => Outcome::Failure((Status::ServiceUnavailable, ())),
        }
    }
}

impl Deref for DbConn {
    type Target = <r2d2_mongodb::MongodbConnectionManager as ManageConnection>::Connection;

    fn deref(&self) -> &Self::Target {
        &self.0
    }
}
thedodd commented 5 years ago

@moh-abk ah, I see that. So, based on the docs here, it looks like the <r2d2_mongodb::MongodbConnectionManager as ManageConnection>::Connection is the mongodb::Database type, which is what we need. So here is a way to bypass your issue:

Let me know if that works for you, then we can close this issue out.

moh-abk commented 5 years ago

Thanks @thedodd

this worked - RestaurantCity::find((*connection).to_owned(), None, None)