How to store actix_web Json to mongodb? - mongodb

Trying to store incomming data into mongo using r2d2-mongodb and actix_web.
#[derive(Serialize, Deserialize, Debug)]
struct Weight {
desc: String,
grams: u32,
}
fn store_weights(weights: web::Json<Vec<Weight>>, db: web::Data<Pool<MongodbConnectionManager>>) -> Result<String> {
let conn = db.get().unwrap();
let coll = conn.collection("weights");
for weight in weights.iter() {
coll.insert_one(weight.into(), None).unwrap();
}
Ok(String::from("ok"))
}
I can't seem to understand what/how I need to convert weight into to use with insert_one.
The above code errors into error[E0277]: the trait bound 'bson::ordered::OrderedDocument: std::convert::From<&api::weight::Weight>' is not satisfied

The signature for insert_one is:
pub fn insert_one(
&self,
doc: Document,
options: impl Into<Option<InsertOneOptions>>
) -> Result<InsertOneResult>
Document is bson::Document, an alias for bson::ordered::OrderedDocument.
Your type Weight does not implement the trait Into<Document>, which is required for weight::into(). You could implement it, but a more idiomatic way would be using the Serialize trait with bson::to_bson:
fn store_weights(weights: Vec<Weight>) -> Result<&'static str, Box<dyn std::error::Error>> {
let conn = db.get()?;
let coll = conn.collection("weights");
for weight in weights.iter() {
let document = match bson::to_bson(weight)? {
Document(doc) => doc,
_ => unreachable!(), // Weight should always serialize to a document
};
coll.insert_one(document, None)?;
}
Ok("ok")
}
Notes:
to_bson returns an enum, Bson, which can be Array, Boolean, Document, etc. We use match to make sure it is Document.
I've used ? instead of unwrap, to make use of the Result return type. Make sure the errors are Into<Error> for your Result type.
Returning &'static str instead of allocating a new String for each request.

Related

How to access database connection from a Rocket's fairing?

I'm trying to use database connection from a Rocket's on_ignite fairing:
use sqlx::{ self, FromRow };
use rocket::fairing::{self, Fairing, Info, Kind};
use rocket::{Build, Rocket};
use crate::database::PostgresDb;
#[derive(FromRow)]
struct TableRow {
column_a: String,
column_b: String
}
#[rocket::async_trait]
impl Fairing for TableRow {
fn info(&self) -> Info {
Info {
name: "Cache table row",
kind: Kind::Ignite,
}
}
async fn on_ignite(&self, rocket: Rocket<Build>) -> fairing::Result {
let mut db = rocket
.state::<Connection<PostgresDb>>()
.expect("Unable to find db connection.");
let row = sqlx::query_as::<_, TableRow>("SELECT * FROM table WHERE id = 1;")
.fetch_one(&mut db)
.await
.unwrap();
fairing::Result::Ok(rocket.manage(row))
}
}
The problem is I get following rust error during .fetch_one(&mut db):
the trait bound `&mut rocket_db_pools::Connection<PostgresDb>: Executor<'_>` is not satisfied
the following other types implement trait `Executor<'c>`:
<&'c mut PgConnection as Executor<'c>>
<&'c mut PgListener as Executor<'c>>
<&'c mut PoolConnection<Postgres> as Executor<'c>>
<&'t mut Transaction<'c, Postgres> as Executor<'t>>
<&sqlx::Pool<DB> as Executor<'p>>rustcClick for full compiler diagnostic
cache_rbac_on_ignite.rs(56, 14): required by a bound introduced by this call
query_as.rs(132, 17): required by a bound in `QueryAs::<'q, DB, O, A>::fetch_all`
I tried solution suggested here: How to get the database Connection in rocket.rs Fairing. but it did not work out.
Here is the code:
use sqlx::{ self, FromRow, Database };
use rocket::fairing::{self, Fairing, Info, Kind};
use rocket::{Build, Rocket};
use crate::database::PostgresDb;
#[derive(FromRow)]
struct TableRow {
column_a: String,
column_b: String
}
#[rocket::async_trait]
impl Fairing for TableRow {
fn info(&self) -> Info {
Info {
name: "Cache table row",
kind: Kind::Ignite,
}
}
async fn on_ignite(&self, rocket: Rocket<Build>) -> fairing::Result {
let mut db = PostgresDb::get_one(rocket).await.unwrap();
let row = sqlx::query_as::<_, TableRow>("SELECT * FROM table WHERE id = 1;")
.fetch_one(&mut db)
.await
.unwrap();
fairing::Result::Ok(rocket.manage(row))
}
}
I get following rust error on line let mut db = PostgresDb::get_one(rocket).await.unwrap();:
no function or associated item named `get_one` found for struct `PostgresDb` in the current scope
function or associated item not found in `PostgresDb`rustcClick for full compiler diagnostic
mod.rs(8, 1): function or associated item `get_one` not found for this struct
What is the right way to use database connection inside of the fairing? Thank you!
Finally found an answer. Here is what worked for me:
use rocket::fairing::{self, Fairing, Info, Kind};
use rocket::{Build, Rocket};
use rocket_db_pools::{ sqlx::{ self, FromRow }, Database };
use crate::database::PostgresDb;
#[derive(FromRow)]
struct TableRow {
column_a: String,
column_b: String
}
#[rocket::async_trait]
impl Fairing for TableRow {
fn info(&self) -> Info {
Info {
name: "Cache table row",
kind: Kind::Ignite,
}
}
async fn on_ignite(&self, rocket: Rocket<Build>) -> fairing::Result {
let db = PostgresDb::fetch(&rocket).unwrap();
let mut conn = db.aquire().await.unwrap();
let row = sqlx::query_as::<_, TableRow>("SELECT * FROM table WHERE id = 1;")
.fetch_one(&mut conn)
.await
.unwrap();
fairing::Result::Ok(rocket.manage(row))
}
}

Trouble creating a more complex closure

I am trying to get my head around creating closures. I get the simpler ones like:
let squaredNumber = { (num: Int) -> (Int) in
return num * num
}
print( squaredNumber(9) ) // 81
and I understand how the sorting one works:
let team = ["Bob", "Rick", "Peter"]
print( team.sorted() ) // ["Bob", "Peter", "Rick"]
print( team.sorted(by: { $0 > $1 }) ) //["Rick", "Peter", "Bob"]
So now I am trying to create my own version of sorting one, in terms of setup.
In the sorting one, there is a plain function sorted() and a sorted(by:...) option.
The code I am playing with so far is (commented code is just to play with):
struct Person {
let name_given = { (fn: String?, ln: String?) -> String in
// return a different value if a nil is supplied
guard let fn1 = fn else { return "No First Name" }
guard let ln1 = ln else { return "No Last Name" }
// Only returns value if not nil
// if let fn1 = fn { return fn1 }
// if let ln1 = ln { return ln1 }
// returns what has been decided above
return "\(fn!) \(ln!)"
}
let no_name = {() -> String in
return "Hello"
}
}
let dad = Person()
print( dad.name_given("Jeff", "Smith") )
print( dad.no_name() )
but I can only get this to work dad.something() but I would like the option of a parameter closure in a function that is I am guessing an optional.
Am I even on the right track in terms of thinking this through.
Basically I want to create a function that execute different code based on wether they have a parameter or not.
So something like dad() or dad(name: {"Jeff", "Smith"}) or dad(age: {35})
The closures would combine strings or do something with the code and then return the result or something.
There is no story to the code above, I just want to understand how to create it.
/////////////////
Edit for clarity:
So I realise my aim was explained with a lot of confusion, because I am trying to figure this out.
Here is hopefully a better attempt:
So this code still doesn't work, but bare with me.
Take for example this:
static func closure(both: (_ a: String, _ b: String) -> String { a, b in
return "\(a) \(b)"
})
and then this:
static func closure(single: (_ a: String) -> String { a in
return "\(a)"
})
So then I would effectively be able to do something like this:
Person.closure(both: {"First", "Last"}) -> This would output "First Last"
and
Person.closure(single: {"First"}) -> This would output "First"
My outcome would be that I could have a static class that has a bunch of closures, but that are grouped.
So if I want to a bunch of string type closures, it would be easy to find them because you could do something like:
StaticStruct.string(<thing1>: {<closure params>})
StaticStruct.string(<thing2>: {<closure params>})
StaticStruct.string(<thing3>: {<closure params>})
or if I want to do something with numbers, it would be:
StaticStruct.numbers(<thing1>: {<closure params>})
StaticStruct.numbers(<thing2>: {<closure params>})
StaticStruct.numbers(<thing3>: {<closure params>})
I hope this makes more sense.
I like the way it looks when you do an array sort, that is why I started thinking like this.
What you are asking is flawed for Swift:
If you don't provide anything at all, not even nil, how can Swift know what's missing?
Think about this example:
print( dad.name_given("Connor") )
How can Swift know if you provided only the first name, or only the last name? In your example, you will need to be specific on what is not provided:
print( dad.name_given(nil, "Connor") ) // Surely only last name is provided
A function would solve this problem by providing a default value to the parameters, so you don't have to pass nil, but then you would need to specifically tell what you're passing, or Swift will assume:
func person(fn: String? = "", ln: String? = "") -> String {...}
print( dad.person(ln: "Connor")) // Surely passing the second parameter
func person(_ fn: String? = "", _ ln: String? = "") -> String {...}
print( dad.person("Connor")) // It will assume that you are passing the first parameter
But with closures, you can't provide a default value to parameters:
let name_given = { (fn: String? = "", ln: String? = "") -> String in...}
// Error: Default arguments are not allowed in closures
It might not solve your problem, but you can create a function that provides default values to the parameters (no need to pass nil), and accepts a closure to treat those parameters.
func describe(fn: String = "", ln: String = "", _ action: #escaping (String?, String?)->String) -> String {
let first: String? = fn == "" ? nil : fn
let second: String? = ln == "" ? nil : ln
return action(first, second)
}
print( dad.describe(ln: "Connor", dad.name_given)) // No first Name

Using the cursor in mongodb-rust sync

I took the code from the documentation, but it doesn't work.
pub fn get_countries(&self) {
let cursor = self.countries.find(None, None);
for doc in cursor {
println!("{}", doc?)
}
}
mongodb::sync::Cursor<bson::Document> doesn't implement std::fmt::Display
mongodb::sync::Cursor<bson::Document> cannot be formatted with the default formatter
the ? operator can only be applied to values that implement std::ops::Try
the ? operator cannot be applied to type mongodb::sync::Cursor<bson::Document>
Also the cursor.collect() does not work correctly.
the method collect exists for enum std::result::Result<mongodb::sync::Cursor<bson::Document>, mongodb::error::Error>, but its trait bounds were not satisfied
method cannot be called on std::result::Result<mongodb::sync::Cursor<bson::Document>, mongodb::error::Error> due to unsatisfied trait bounds
I tried using cursor.iter() or cursor.into_iter(), the result was the same
Full code of module
use bson::Document;
use mongodb::{
error::Error,
sync::{ Collection, Database},
};
pub struct Core {
db: Database,
countries: Collection<Document>,
}
impl Core {
pub fn new(db: &Database) -> Core {
Core {
db: db.clone(),
countries: db.collection("countries"),
}
}
pub fn get_country(&self, name: &String) -> Result<Option<Document>, Error> {
self.countries.find_one(bson::doc! { "idc": name }, None)
}
pub fn get_countries(&self) {
let cursor = self.countries.find(None, None);
for doc in cursor {
println!("{}", doc?)
}
}
}
It seems that the doc value is returning a Cursor, so I'm guessing that cursor must be rather the Result<Cursor<T>> type returned by the Collection::find method. https://docs.rs/mongodb/latest/mongodb/sync/struct.Collection.html#method.find
Shouldn't you unwrap (or handle the result with a proper match) your self.countries.find(None, None) result ?
pub fn get_countries(&self) {
let cursor = self.countries.find(None, None).unwrap();
for doc in cursor {
println!("{}", doc?)
}
}
My solution
pub fn get_countries(&self) -> Vec<Document> {
let cursor = self.countries.find(None, None).unwrap();
let mut total: Vec<Document> = Vec::new();
for doc in cursor {
total.push(doc.unwrap());
}
total
}

Typing of return type after mongodb projection

I am making a graphql resolver in rust, and am only fetching the fields from the graphql query in my mongodb database. However Rust complains that the fetched data, of course, is now not of the same type as the specified return type. What is the right way to do something like this.
I guess I could do #[serde(default)], but that doesn't work exactly as expected (I will explain later)
use async_graphql::*;
use serde::{Deserialize, Serialize};
use mongodb::{bson::doc, bson::oid::ObjectId, options::FindOptions, Collection};
#[derive(SimpleObject, Serialize, Deserialize, Debug)]
#[graphql(complex)]
struct Post {
#[serde(rename = "_id")]
pub id: ObjectId,
pub title: String,
// I could do something like
// #[serde(default)]
pub body: String,
}
#[ComplexObject]
impl Post {
async fn text_snippet(&self) -> &str {
let length = self.body.len();
let end = min(length, 5);
&self.body[0..end]
}
}
struct Query;
#[Object]
impl Query {
// fetching posts
async fn posts<'ctx>(&self, ctx: &Context<'ctx>) -> Vec<Post> {
let posts = ctx.data_unchecked::<Collection<Post>>();
let projection = // getting the projection doc here based on graphql fields, lets say doc! {"title": 1}
let options = FindOptions::builder().limit(10).projection(projection).build();
let cursor = posts.find(None, options).await.unwrap();
cursor.try_collect().await.unwrap_or_else(|_| vec![])
}
}
But when I run the query
{
posts {
id
title
textSnippet
}
}
i get
thread 'actix-rt:worker:0' panicked at 'called `Result::unwrap()` on an `Err` value: Error { kind: BsonDecode(DeserializationError { message: "missing field `body`" }), labels: [] }', server/src/schema/post.rs:20:46
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
and when i do the #[serde(default)] stuff on body, and I then query textSnippet and not body, the textSnippet is an empty string.
How do i fix this?
Could you wrap every field in Post with an Option and let the try_collect fill the returned fields for you?
You can create a struct with those fileds you need and use a collection of the new struct.
use async_graphql::*;
use serde::{Deserialize, Serialize};
use mongodb::{bson::doc, bson::oid::ObjectId, options::FindOptions, Collection};
#[derive(SimpleObject, Serialize, Deserialize, Debug)]
#[graphql(complex)]
struct Post {
#[serde(rename = "_id")]
pub id: ObjectId,
pub title: String,
// I could do something like
// #[serde(default)]
pub body: String,
}
#[derive(SimpleObject, Serialize, Deserialize, Debug)]
#[graphql(complex)]
struct PostTitle {
#[serde(rename = "_id")]
pub id: ObjectId,
pub title: String,
}
struct Query;
#[Object]
impl Query {
// fetching posts
async fn posts<'ctx>(&self, ctx: &Context<'ctx>) -> Vec<PostTitle> {
let posts = ctx.data_unchecked::<Collection<PostTitle>>();
let projection = doc! {"title": 1}
let options = FindOptions::builder().limit(10).projection(projection).build();
let cursor = posts.find(None, options).await.unwrap();
cursor.try_collect().await.unwrap_or_else(|_| vec![])
}
}

Array transform having failable initialiser

I am using Swift 1.2 in Xcode 6.3.1
Following is my Person struct
struct Person {
let age: Int
init?(age: Int) { //Failable init
if age > 100 { return nil }
self.age = age
}
}
I am having a list of ages against which I have to make Person Objects.
I have made playground file.
let arr = Array(1...150) //Sample set of ages against which Person is created
var personList: [Person]!
and
personList = arr.map({ (val: Int) -> Person? in
return Person(age: val) //Makes object of type Person?
}).filter {
$0 != nil
}.map {
return $0!
}
Here I have uses map - filter - map because the first map invokes failable intializer, (hence it returns Person?) and personList is of type [Person].
Hence second function filters all the non nil objects and third map forcefully opens to optional therby making Person? to Person.
Is there a more easy/readable way out ? Chaining map-filter-map definitely seems to be an overkill for this
You can use flatMap to get rid of any nils in the array, this tutorial discusses the method in length, but the following will work best:
let personList = arr.flatMap { Person(age: $0) }
Note: This answer was given for Swift 1.2, the current
version at the time the question was posted. Since Swift 2 there is a better solution, see #Jeremie's answer.
I don't know of a built-in function that combines filter()
and map(). You can write the code slightly more compact using
the shorthand argument $0 in all closures:
let personList = arr.map { Person(age: $0) }
.filter { $0 != nil }
.map { $0! }
Of course you can define your own extension method which maps the
array elements and keeps only the non-nil results:
extension Array {
func optmap<U>(transform: T -> U?) -> [U] {
var result : [U] = []
for elem in self {
if let mapped = transform(elem) {
result.append(mapped)
}
}
return result
}
}
and then use it as
let personList = arr.optmap { Person(age: $0) }
You can use compactMap which is better that flatMap in this case to remove any nils in the array:
let personList = arr.compactMap { Person(age: $0) }
The Swift document declared:
Returns an array containing the non-nil results of calling the given
transformation with each element of this sequence.