Macro mongodb_ext::mongo_db [−][src]
macro_rules! mongo_db {
($(#[$additional_db_attr : meta]) * $db_name : ident
{
$($(#[$additional_coll_attr : meta]) * $coll_name : ident
$(< _id : $id_spec : ident >) ?
{
$($(#[$additional_field_attr : meta]) * $field : ident :
$field_type : ty), * $(,) ?
}), + $(,) ?
}) => { ... };
}
Expand description
Model a mongodb database.
This macro creates structs / functions / constants / modules that represent a mongoDB database. Being a macro (which is expanded at compile time) there is no run time performance penalty when using this macro.
Structure
This macro wraps everything in a module called mongo
.
The main database handler has the following attributes:
- Its name represents the database’s name (eg. a database named
MyDatabase
has a structmongo::MyDatabase
). - It implements the
MongoClient
trait. - It contains handles to all given collections inside the database.
These handles have the format
{collection_name}_coll
where{collection_name}
represents the collection’s name insnake_case
. - It also contains a
client
and adatabase
field for you to use.
All collections are wrapped in an additional public module named schema
.
Each collection has its own struct which stores all specified fields.
All collection structs implement Serialize
, Deserialize
and MongoCollection
.
By default a field _id
gets added to each collection automatically:
pub _id: Option<DefaultId>
(see DefaultId
for more info).
This field needs to exist for you to be able to obtain an _id
field from the database.
When serializing, _id
gets skipped if it is None
.
All fields except _id
get renamed to camelCase
when serializing (converting _id
to camelCase
results in id
).
Note: All structs’ names in camelCase
can be accessed via the MongoClient
/ MongoCollection
trait.
Examples
Manipulating / Removing _id
You can specify any type (that implements Serialize
and Deserialize
) to be used inside the _id
Option
by specifying it in <
/ >
after the collection name:
use mongodb_ext::mongo_db;
mongo_db! {
SomeDatabase {
SomeCollection<_id: u128> {
first_name: String,
}
}
}
// _id is now u128 instead of `DefaultId`
let some_document = mongo::schema::SomeCollection {
_id: Some(255),
first_name: String::from("Bob")
};
It is also possible to disable the generation of an _id
field all together by using <_id: none>
.
use mongodb_ext::mongo_db;
mongo_db! {
SomeDatabase {
SomeCollection<_id: none> {
#[serde(skip_serializing_if = "Option::is_none")]
email_address: Option<String>,
first_name: String,
}
}
}
// no `_id` exists, this example assumes that users are addressed via their email address
let some_document = mongo::schema::SomeCollection {
email_address: Some(String::from("bob@example.com")),
first_name: String::from("Bob")
};
These features are unique for each collection:
use mongodb_ext::mongo_db;
mongo_db! {
SomeDatabase {
SomeCollection<_id: u128> {
first_name: String,
},
Another {
some_field: u32,
},
AndYetAnother<_id: none> {
email: String,
name: String,
}
}
}
// `_id` type changed to `u128`
let some_document = mongo::schema::SomeCollection {
_id: Some(255),
first_name: String::from("Bob")
};
// `_id` type default, eg. `DefaultId`
let another_document = mongo::schema::Another {
_id: Some(String::from("my_id")),
some_field: 1,
};
// `_id` field disabled
let and_yet_another_document = mongo::schema::AndYetAnother {
name: String::from("Bob"),
email: String::from("bob@example.com")
};
Serializing from json!
and doc!
use mongodb_ext::mongo_db;
use serde_json::{json, Value};
use mongodb::{bson::{doc, Document}, bson};
mongo_db! {
#[derive(Debug, Clone)]
DatabaseOfItems {
#[derive(Debug, Clone, PartialEq)]
Items {
counter: u16,
name: String
},
}
}
// Note that `_id` is not specified here
let my_item: Value = json! ({
"counter": 0,
"name": "my_special_item"
});
let my_collection_entry: mongo::schema::Items =
serde_json::from_value(my_item)
.expect("Could not convert json Value to collection document");
assert_eq!(
my_collection_entry,
mongo::schema::Items {
_id: None,
counter: 0,
name: String::from("my_special_item")
}
);
// Note that `_id` is not specified here
let my_item: Document = doc! {
"counter": 0,
"name": "my_special_item"
};
let my_collection_entry: mongo::schema::Items = bson::de::from_document(my_item)
.expect("Could not convert mongodb bson Document to collection document");
assert_eq!(
my_collection_entry,
mongo::schema::Items {
_id: None,
counter: 0,
name: String::from("my_special_item")
}
);
General Examples
use mongodb_ext::{mongo_db, MongoClient, MongoCollection};
use serde_json::ser;
mongo_db! {
SomeDatabase {
#[derive(Debug, Clone)]
SomeCollection {
first_name: String,
}
}
}
let mut some_document = mongo::schema::SomeCollection {
_id: None,
first_name: String::from("alice")
};
// When serializing, `_id` is skipped only if `None`.
// Note the key conversion to `camelCase`.
assert_eq!(
ser::to_string(&some_document).unwrap(),
String::from("{\"firstName\":\"alice\"}")
);
// update `_id` field to include in serialization.
some_document._id = Some(String::from("my-custom-ID"));
assert_eq!(
ser::to_string(&some_document).unwrap(),
String::from("{\"_id\":\"my-custom-ID\",\"firstName\":\"alice\"}")
);
assert_eq!("someCollection", mongo::schema::SomeCollection::NAME);
assert_eq!("someDatabase", mongo::SomeDatabase::NAME);
use mongodb_ext::{mongo_db, MongoCollection, MongoClient};
mongo_db! {
#[derive(Debug, Clone)]
MyDatabase {
#[derive(Debug, Clone)]
MyFirstCollection {
first_name: String,
last_name: String,
age: u8,
},
#[derive(Debug)]
AnotherCollection {
some_field: String
}
}
}
// all constants that were defined
assert_eq!("myDatabase", mongo::MyDatabase::NAME);
assert_eq!("myFirstCollection", mongo::schema::MyFirstCollection::NAME);
assert_eq!("anotherCollection", mongo::schema::AnotherCollection::NAME);
// initializer function and general usage
// note that `tokio_test::block_on` is just a test function to run `async` code
let mongo = tokio_test::block_on(mongo::MyDatabase::new("mongodb://example.com"))
.expect("Could not create mongoDB client");
let bob = mongo::schema::MyFirstCollection {
_id: None,
first_name: String::from("Bob"),
last_name: String::from("Bob's last name"),
age: 255,
};
// This should fail beause there is no actual mongoDB service running at the specified connection.
assert!(tokio_test::block_on(
mongo.my_first_collection_coll.insert_one(bob, None)
).is_err());