Rust - MongoDB and BSON

Serde, BSON, and MongoDB

This purpose of this guide is to give clear examples of inserting and fetching Rust data structures using a MongoDB database.

Vocab

Serde - Rust Library for Serializing and Deserializing data structures.
Serializing - Translating data structures into a format suitable for network transfer or conversion.
Deserializing - Constructing a data structure from a serialized format.
BSON - Binary JSON, serialization format used by MongoDB to represent data structures.
Document - Represents a single data structure.

Cargo.toml

Include the following crates in your cargo.toml file:

1
2
3
4
5
[dependencies]
serde = "1.0" # serialization library
serde_derive = "1.0" # automatically derives Serialize and Deserialize impls for your data
bson = "0.9.0" # BSON library - makes use of Serde to Serialize and Deserialize with BSON
mongodb = "0.3.2" # Official prototype MongoDB rust driver

Import the crates into your main.rs or lib.rs

1
2
3
4
5
#[macro use(bson, doc)]
extern crate bson;
extern crate mongodb;
extern crate serde;
#[macro use] extern crate serde_derive; // #[macro use] extends the macros in the crate to our modules

Preparing your data

Serde_derive is capable of implementing Serialize and Deserialize for most simple data structures.
Advanced Serde topics can be found here: Serde Docs

Use #[derive(Serialize, Deserialize)] to automatically implement Serialize and Deserialize for your data.

1
2
3
4
5
6
#[derive(Serialize, Debug, Debug)]
pub struct Person {
first: String,
last: String,
age: u8
}

Preparing a MongoDB connection

In this example we assume MongoDB is installed on the local machine and contains a database called “Test”. We will be using a collection “People” to store our Person data structures
MongoDB rust driver docs are here: MongoDB driver Docs

1
2
3
4
const MONGO_URL: &'static str = "localhost";
const MONGO_PORT: u16 = 27017;
const MONGO_DB: &'static str = "Test";
const MONGO_COLL_PEOPLE: &'static str = "People";
1
2
3
4
5
6
use mongodb::{Client, ThreadedClient};
use mongodb::db::ThreadedDatabase;
let client = Client::connect(MONGO_URL, MONGO_PORT)
.expect("failed to initialize client");
let people_coll = client.db(MONGO_DB).collection(MONGO_COLL_PEOPLE);

note: MongoDB’s Client is an Arc type backed by a connection pool. You can clone it as many times as you’d like.

Inserting your data into MongoDB (Serializing)

The code below contains lots of matches that can be simplified, but you will be able to see where errors could occur.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
use bson;
// only Serialize is needed here
#[derive(Serialize, Deserialize,)]
pub struct Person {
pub first: String,
pub last: String,
pub age: u16
}
impl Person {
// takes a borrowed model and returns a simple true/false result
pub fn insert(&self, model: &T) -> Result<bool,bool> {
match bson::to_bson(model) {
Ok(model_bson) => {
match model_bson{
bson::Bson::Document(model_doc) => {
match self.coll.insert_one( model_doc, None) {
Ok(db_result) => {
return Ok(true)
},
Err(err) => {
println!("Failed to insert new model doc into database:\n{}",err);
return Err(false)
}
}
},
_ => {
println!("Failed to create document from new model bson");
return Err(false)
}
}
},
Err(err) => {
println!("Failed to create bson from new model:\n{}",err);
return Err(false)
}
}
}
}

Fetching your data from MongoDB (Deserializing)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
use bson;
// only Deserialize is needed here
#[derive(Serialize, Deserialize,)]
pub struct Person {
pub first: String,
pub last: String,
pub age: u16
}
impl Person {
// takes a borrowed partial model and returns the model if it matches or None if it doesn't
// returns a bool instead of error types
pub fn find_one(&self, model: &T) -> Result<Option<T>, bool> {
match bson::to_bson(model) {
Ok(model_bson) => {
match model_bson{
bson::Bson::Document(model_doc) => {
match self.coll.find_one( Some(model_doc), None) {
Ok(db_result) => {
match db_result {
Some(result_doc) => {
match bson::from_bson(bson::Bson::Document(result_doc)) {
Ok(result_model) => {
return Ok(Some(result_model))
},
Err(err) => {
println!("failed to get model from bson");
return Err(false)
}
}
},
None => {
println!("No model found");
return Ok(None)
}
}
},
Err(err) => {
println!("Failed to delete doc from database:\n{}",err);
return Err(false)
}
}
},
_ => {
println!("Failed to create document from new model bson");
return Err(false)
}
}
},
Err(err) => {
println!("Failed to create bson from model:\n{}",err);
return Err(false)
}
}
}
}
Share Comments