I am new to Rust and was trying to create a webserver with Actix-web to perform CRUD operations via MongoDB. The first API I am creating is to save a simple document in MongoDB by something received from a POST request. The code for the post request handler function is:
extern crate r2d2;
extern crate r2d2_mongodb;
use r2d2::Pool;
use r2d2_mongodb::mongodb::db::ThreadedDatabase;
use r2d2_mongodb::{ConnectionOptions, MongodbConnectionManager};
use actix_web::{web, App, HttpRequest, HttpResponse, HttpServer, Responder};
use bson::{doc, Bson, Document};
async fn post_request(info: web::Json<Info>, pool: web::Data<Pool<MongodbConnectionManager>>) -> HttpResponse {
let name: &str = &info.name;
let connection = pool.get().unwrap();
let doc = doc! {
"name": name
};
let result = connection.collection("user").insert_one(doc, None);
HttpResponse::Ok().body(format!("username: {}", info.name))
}
I am using r2d2 to establish a connection pool for MongoDB instead of opening and closing a connection. The error i am getting is
error[E0308]: mismatched types
--> src/main.rs:17:59
|
17 | let result = connection.collection("user").insert_one(doc, None);
| ^^^ expected struct `OrderedDocument`, found struct `bson::Document`
The insert_one function doc says it accepts a bson::Document but when I give that to it, it says expected struct `r2d2_mongodb::mongodb::ordered::OrderedDocument`
Here are my Cargo.toml dependancies
mongodb = "1.1.1"
actix-web = "3.3.2"
dotenv = "0.15.0"
r2d2-mongodb = "0.2.2"
r2d2 = "0.8.9"
serde = "1.0.118"
bson = "1.1.0"
How can I correct this?
The r2d2-mongodb is outdated and no longer supported, the r2d2 crate marks it as:
deprecated: official driver handles pooling internally
So I recommend you don't use it. You should be able to use a mongodb::Client or mongodb::Database instead of a Pool<MongodbConnectionManager>.
The reason for the error is the r2d2-mongodb uses an older version of mongodb (0.3) and therefore an older version of bson (0.13) that is incompatible with the version of bson you're using (1.1.0). You'll probably have similar compatibility issues with the mongodb crate itself as well. You can fix it by lowering your dependencies:
mongodb = "0.3.0"
bson = "0.13.0"
Though, as I brought up before, I don't recommend it.
Related
I'm on a project where I need to manage connections to both a MongoDB Instance and a PostgreSQL instance.
My current idea is to make a custom type that will contain an Arc<Mutex<pgConnection>> and an Arc<Mutex<MongoConnection>> in a struct that itself is within an Arc which would be passed to the Actix Web app_data initialization function.
e.g.
// this is pseudo-code, kinda
type DbPoolPG = r2d2::Pool<ConnectionManager<PostgreSQL>>;
// wont be an r2d2 pool, MongoDB official drivers handle pooling automatically
type DbPoolMongo = r2d2::Pool<ConnectionManager<MongoDB>>;
struct DatabseConnections {
pg: Arc<Mutex<DbPoolPG>>;
mongo: Arc<Mutex<DbPoolMongo>>;
}
#[actix_web::main]
async fn main() -> io::Result<()> {
// Create connection pools
let PostGresPool = r2d2::Pool::builder()
.build(manager)
.expect("Failed to create pool.");
let MongoPool = mongo.create_connection()
let connections = DatabaseConnections {
pg: Arc::new(Mutex::new(PostGresPool))
mongo: Arc::new(Mutex::new(MongoPool))
}
// Start HTTP server
HttpServer::new(move || {
App::new().app_data(web::Data::new(Arc::new(connections)))
.resource("/{name}", web::get().to(index))
})
.bind(("127.0.0.1", 8080))?
.run()
.await
}
The idea seems a bit too simple to actually work though, Does anyone else have any ideas?
nvm, you can just call app_data twice as long as the types are different. then access them by calling the types
For performance optimisation we are trying to read data from Mongo secondary server for selected scenarios. I am using the inline query using "withReadPreference(ReadPreference.secondaryPreferred())" to read the data, PFB the code snippet.
What I want to confirm the data we are getting is coming from secondary server after executing the inline query highlighted, is there any method available to check the same from Java or Springboot
public User read(final String userId) {
final ObjectId objectId = new ObjectId(userId);
final User user = collection.withReadPreference(ReadPreference.secondaryPreferred()).findOne(objectId).as(User.class);
return user;
}
Pretty much the same way in Java. Note we use secondary() not secondaryPrefered(); this guarantees reads from secondary ONLY:
import com.mongodb.ReadPreference;
{
// This is your "regular" primaryPrefered collection:
MongoCollection<BsonDocument> tcoll = db.getCollection("myCollection", BsonDocument.class);
// ... various operations on tcoll, then create a new
// handle that FORCES reads from secondary and will timeout and
// fail if no secondary can be found:
MongoCollection<BsonDocument> xcoll = tcoll.withReadPreference(ReadPreference.secondary());
BsonDocument f7 = xcoll.find(queryExpr).first();
}
This question already has answers here:
Why do I get the error "there is no reactor running, must be called from the context of Tokio runtime" even though I have #[tokio::main]?
(3 answers)
Closed 1 year ago.
I tried to implement mongodb with actix web. This is my main class:
#[actix_web::main]
async fn main() -> std::io::Result<()> {
let db_helper = DbHelper::open_connection().await?;
println!("Connected!");
Ok(())
}
// my db helper
pub async fn open_connection() -> std::io::Result<DbHelper> {
let client = Client::with_uri_str("mongodb://localhost:27017/").await.expect("connect to mongodb issue!");
// List the names of the databases in that deployment.
let databases = client.list_database_names(None, None).await.expect("Test error");
for mongodb in databases {
println!("{}", mongodb);
}
let database = client.database(DB_NAME);
let db_helper = DbHelper {
client,
database,
};
Ok(db_helper)
}
When I tried to run this code, it gave me an error:
thread 'main' panicked at 'there is no reactor running, must be called from the context of a Tokio 1.x runtime',
The problem is from this line: let databases = client.list_database_names(None, None).await.expect("Test error");. I don't know what happened here. If I remove that line or replace actix_web::main with #[tokio::main], it can work. However, the actix web cannot work.
actix-web = "3"
mongodb = "2.0.0-beta.2"
The problem is that you are using Actix web 3.x with Tokio 1.x and mongoDB client 2.x. You need to use Actix web 4.x for it to work with the other two.
I am trying to make a basic web application with the rust language, using the actix framework and r2d2 with mongodb as the database. I could not find any complete and working documentation on how to archive this. Maybe someone can help me out here.
The problem is, that i can't seem to get a mongodb connection from the r2d2 connection pool. Sadly this part isnt covered in any documentation i found.
Some links i found:
Using r2d2 with actix: https://github.com/actix/examples/blob/master/r2d2/src/main.rs
Using mongodb with r2d2: https://docs.rs/r2d2-mongodb/0.2.2/r2d2_mongodb/
This part creates the connection pool and hands it to actix.
fn main() {
std::env::set_var("RUST_LOG", "actix_web=info");
env_logger::init();
let manager = MongodbConnectionManager::new(
ConnectionOptions::builder()
.with_host("localhost", 27017)
.with_db("mydatabase")
.build()
);
let pool = Pool::builder()
.max_size(16)
.build(manager)
.unwrap();
HttpServer::new( move || {
App::new()
// enable logger
.wrap(middleware::Logger::default())
// store db pool in app state
.data(pool.clone())
// register simple handler, handle all methods
.route("/view/{id}", web::get().to(view))
})
.bind("127.0.0.1:8080")
.expect("Can not bind to port 8080")
.run()
.unwrap();
}
This is the handler function trying to access the connection pool
fn view(req: HttpRequest,
pool: web::Data<Pool<MongodbConnectionManager>>) -> impl Responder {
let id = req.match_info().get("id").unwrap_or("unknown");
let conn = pool.get().unwrap();
let result = conn.collections("content").findOne(None, None).unwrap();
// HERE BE CODE ...
format!("Requested id: {}", &id)
}
This is the error showing my problem. The conn variable doesnt seem to be a propper mongodb connection.
error[E0599]: no method named `collections` found for type `std::result::Result<r2d2::PooledConnection<r2d2_mongodb::MongodbConnectionManager>, r2d2::Error>` in the current scope --> src\main.rs:29:23
|
29 | let result = conn.collections("content").findOne(None, None).unwrap();
|
10 | let coll = conn.collection("simulations");
| ^^^^^^^^^^
|
= help: items from traits can only be used if the trait is in scope
= note: the following trait is implemented but not in scope, perhaps add a `use` for it:
`use crate::mongodb::db::ThreadedDatabase;`
my compiler told me to add mongodb::db::ThreadedDatabase in scope.
Trying to insert into a mongodb database from scala. the below codes dont create a db or collection. tried using the default test db too. how do i perform CRUD operations?
object Store {
def main(args: Array[String]) = {
def addMongo(): Unit = {
var mongo = new Mongo()
var db = mongo.getDB("mybd")
var coll = db.getCollection("somecollection")
var obj = new BasicDBObject()
obj.put("name", "Mongo")
obj.put("type", "db")
coll.insert(obj)
coll.save(obj)
println("Saved") //to print to console
}
}
On a first glance things look OK in your code although you have that stray def addMongo(): Unit = {
code at the top. I'll defer to a suggestion on looking for errors here.... Two items of note:
1) save() and insert() are complementary operations - you only need one. insert() will always attempt to create a new document ... save() will create one if the _id field isn't set, and update the represented _id if it does.
2) Mongo clients do not wait for an answer to a write operation by default. It is very possible & likely that an error is occurring within MongoDB causing your write to fail. the getLastError() command will return the result of the last write operation on the current connection. Because MongoDB's Java driver uses connection pools you have to tell it to lock you onto a single connection for the duration of an operation you want to run 'safely' (e.g. check result). This is the easiest way from the Java driver (in Scala, sample code wise, though):
mongo.requestStart() // lock the connection in
coll.insert(obj) // attempt the insert
getLastError.throwOnError() // This tells the getLastError command to throw an exception in case of an error
mongo.requestDone() // release the connection lock
Take a look at this excellent writeup on MongoDB's Write Durability, which focuses specifically on the Java Driver.
You may also want to take a look at the Scala driver I maintain (Casbah) which wraps the Java driver and provides more scala functionality.
We provide among other things an execute-around-method version of the safe write concept in safely() which makes things a lot easier for testing for writes' success.
You just missed the addMongo call in main. The fix is trivial:
object Store {
def main(args: Array[String]) = {
def addMongo(): Unit = {
var mongo = new Mongo()
var db = mongo.getDB("mybd")
var coll = db.getCollection("somecollection")
var obj = new BasicDBObject()
obj.put("name", "Mongo")
obj.put("type", "db")
coll.insert(obj)
coll.save(obj)
println("Saved") //to print to console
}
addMongo // call it!
}