Introducing the MongoDB Driver for the Rust Programming Language

Jul 25 • Posted 9 months ago

Discuss on Hacker News

This is a guest post by Jao-ke Chin-Lee and Jed Estep, who are currently interns at 10gen. This summer they were tasked with building a Rust driver for MongoDB.

Today we are open sourcing the alpha release of a MongoDB driver for the Rust programming language. This is the culmination of two months of work with help from the Rust community, and we’re excited to be sharing our initial version. We are looking forward to feedback from both the Rust and MongoDB communities.

About Rust

Rust is a multi-paradigm, systems-oriented language currently in development at Mozilla. It features elements of imperative, functional, and object-oriented languages, as well as including ambitious new features such as affine types that allow for smart automatic memory management and concurrency. Its powerful type- and memory-safety features run at compile time, and still maintain performance typical of a low-level language. As the language continues to grow, the availability of a MongoDB driver written in native Rust exposes both Rust and MongoDB to new audiences.

About the MongoDB Driver

The driver is Apache licensed, and is implemented purely in Rust without relying on any C extensions or libraries (with the exception of an embedded copy of md5, which can be removed when Rust offers a native implementation).

Using the driver will feel familiar to users of current MongoDB drivers. Basic BSON types, such as strings, numbers, and objects, have a built-in internal representation based around Rust’s algebraic data types. Users can also implement a trait (analogous to a Haskell typeclass, similar to a Java interface) which allows them to treat types native to their codebase as though they were native to BSON as well.

Once a user’s data is formatted, interacting with the database is done through familiar objects like Collection, DB, Cursor, and Client. These objects have similar APIs to their counterparts in other languages, and presently offer CRUD, indexing, as well as administrative functionality in a framework that is Rustically abstracted and balances the philosophy of Rust’s static guarantees with the flexibility of MongoDB. A small example is offered below.

Example Usage

First we need to import the mongo library and the classes we’ll be using.

extern mod mongo;

use mongo::client::*;
use mongo::util::*;
use mongo::coll::*;
use mongo::db::*;

In order to connect with a Mongo server, we first create a client.

let client = @Client::new();

To connect to an unreplicated, unsharded server running on localhost, port 27017 (MONGO_DEFAULT_PORT), we use the connect method:

match client.connect(~"127.0.0.1", MONGO_DEFAULT_PORT) {
    Ok(_) => (),
    Err(e) => fail!(e.to_str()),
}

We create a capped collection named “capped” in the database “rust_ex” by first creating a handle to the “rust_ex” database (which may or may not be empty) and calling create_collection with arguments specifying the size of the capped collection (which must not already exist).

let db = DB::new(~"rust_ex", client);
match db.create_collection(~"capped", None, Some(~[CAPPED(100000), MAX_DOCS(20)])) {
    Ok(_) => (),
    Err(e) => fail!(e.to_str()),
};

Now we create a tailable cursor to extract out documents where the value of the “a” field is divisible by 5, and project on the “msg” field.

let coll = Collection::new(~"rust_ex", ~"capped", client);
let mut cursor = match coll.find(   Some(SpecNotation(~"{ 'a':{'$mod':[5,0]} }")),
                                    Some(SpecNotation(~"{ 'msg':1 }")),
                                    None) {
    Ok(c) => c,     // JSON-formatted strings are automatically converted to BSON
    Err(e) => fail!(e.to_str()),
};
cursor.add_flags(~[CUR_TAILABLE, AWAIT_DATA]);

Then we spawn a thread to populate the capped collection. Note that for the first insert, we specify None as the writeconcern, which indicates the default of 1, whereas for the subsequent inserts, we specify the writeconcern as journaled.

coll.insert(~"{ 'a':0, 'msg':'first insert' }", None);
let n = 50;
do spawn {
    let tmp_client = @Client::new();
    tmp_client.connect(~"127.0.0.1", MONGO_DEFAULT_PORT);

    let coll = Collection::new(~"rust_ex", ~"capped", tmp_client);
    let mut i = 1;
    for n.times {
        match coll.insert(  fmt!("{ 'a':%?, 'msg':'insert no. %?' }", i, i),
                            Some(~[JOURNAL(true)])) {
            Ok(_) => (),
            Err(e) => println(fmt!("%s", e.to_str())),
        };
        i += 1;
    }
    tmp_client.disconnect();
}

Meanwhile, in the main thread, we iterate on the results returned from the tailable cursor.

for 25.times {
    let mut p = cursor.next();
    while p.is_none() && !cursor.is_dead() { p = cursor.next(); }
    if cursor.is_dead() { break; }
    println(fmt!("read %?", p.unwrap().to_str()));
}

Finally, we disconnect the client. This client can be reused to connect to other servers afterwards.

match client.disconnect() {
    Ok(_) => (),
    Err(e) => fail!(e.to_str()),
}

For similar examples including a worked one in which user-implemented structs are inserted into and read from database please refer to the examples.

Resources

Please find more examples, as well as the source code which we encourage you to check out and play around with, at the GitHub repository. We also have documentation available. Keep in mind that the driver currently will only build on Rust 0.7 release, and will not work with other versions of Rust. We welcome any feedback and contributions; create an issue or submit a pull request!

About Us

The MongoDB Rust driver was developed by Jao-ke Chin-Lee and Jed Estep, who are currently interns at 10gen. We were drawn to the project by the innovation of Rust as well as the idea of bridging the Rust and MongoDB communities.

Acknowledgements

Many thanks to the Rust community on IRC and rust-dev for their guidance in Rust, and for developing such an exciting language. Special thanks to 10gen for hosting us as interns, Stacy Ferranti and Ian Whalen for managing the internship program, and our mentors Tyler Brock and Andrew Morrow for their help and support throughout the project.