[][src]Crate ejdb

Safe and idiomatic Rust bindings for EJDB, a MongoDB-like embedded database library.

ejdb.rs provides an interface for EJDB, an embeddable JSON-based document database library. Think of it as SQLite-like MongoDB. EJDB attempts to be compatible (to some extent) with MongoDB basic concepts and query language, so if you have any experience with MongoDB, learning EJDB will be easy.

EJDB uses BSON internally, just like MongoDB, so ejdb.rs uses bson-rs crate, which is reexported as ejdb::bson module. Please note that it is important to use types and functions from ejdb::bson module, not loading bson with extern crate, because of possible version incompatibilities. bson! macro provided by this crate also uses types from ejdb::bson.

The central type in this library is Database structure. It represents an opened EJDB database. An EJDB database usually consists of several files: the database itself, a file for each collection and a file for each index. Therefore, it makes sense to dedicate a whole directory for EJDB database files. However, Database::open() method and its comrades accepts a path to the database file itself, not the directory.

use ejdb::Database;

let db = Database::open("/path/to/db").unwrap();

Database's Drop implementation closes the database automatically according to RAII pattern.

The database can be opened in various modes; see DatabaseOpenMode structure for more information.

After the database is opened, you can obtain collections out of it. It is done primarily with Database::collection() method:

let coll = db.collection("some_collection").unwrap();

Database::collection() method returns an existing collection or creates a new one with the default options. See CollectionOptions structure for more information about which options collections have.

A collection may be used to perform queries, initiate transactions or save/load BSON documents by their identifiers directly, without using queries. Collection objects can also be used to manage indices.

Saving/loading BSON documents

You can use Collection::save() or Collection::save_all() methods to store BSON documents directly into the collection, and Collection::load() to load a document by its id:

let mut d = bson! {
    "name" => "Foo Bar",
    "count" => 10
};
let inserted_id = coll.save(&d).unwrap();

d.insert("_id", inserted_id.clone());
let d2 = coll.load(&inserted_id).unwrap().unwrap();
assert_eq!(d, d2);

If the _id field is not present in the BSON document, it will be generated and added automatically.

Collection::save_all() method is implemented over Collection::save() and returns a special kind of error which contains information about errors for each save operation, if any.

Performing queries

EJDB supports a pretty large subset of operations provided by MongoDB, and even has its own unique queries, like joins.

Queries are performed with Collection::query() method which accepts, two arguments: anything which can be borrowed into a Query and anything which can be borrowed into a QueryHints. Query is the actual query, i.e. constraints on the data in a collection, and QueryHints alter the way the query is processed and returned.

Both query and query hints are just BSON documents of special format, therefore ejdb.rs provides the respective From<bson::Document>/Into<bson::Document> for both Query and QueryHints; however, it is recommended to use the builder API instead of constructing queries manually because this way it is much harder to create invalid queries. Naturally, invalid queries are by no means unsafe in Rust sense - if such a query is passed for execution, an error will be returned.

Query builder API provides two entry points, ejdb::query::Q and ejdb::query::QH, which are kind of aliases for Query::new() and QueryHints::new() but look arguably nicer. To run a query, pass an instance of Query and QueryHints to Collection::query() method. The latter returns a ejdb::PreparedQuery instance which can be used to execute the query in various ways.

use ejdb::query::{Q, QH};
use ejdb::bson;
use ejdb::Result;

let n = coll.query(Q.field("name").eq("Foo").set("count", 10), QH.empty()).update().unwrap();
// `n` is the number of affected rows

let names = ["foo", "bar", "baz"];
let items = coll.query(Q.field("name").contained_in(names.iter().cloned()), QH.max(12))
    .find().unwrap();
// `items` is an iterator which contains at maximum 12 records whose `name`
// field is either "foo", "bar" or "baz"
let items: Result<Vec<bson::Document>> = items.collect();  // collect them into a vector

let item = coll.query(Q.field("count").between(-10, 10.2), QH.field("name").include())
    .find_one().unwrap();
// `item` is an `Option<bson::Document>` which contains a record whose `count` field
// is between -10 and 10.2, inclusive, if there is one, and this document will only contain
// `name` field.

let n = coll.query(Q.field("name").exists(true), QH.empty()).count().unwrap();
// `n` is the number of records which contain `name` field

Transactions

You can use Collection::begin_transaction() method which will start a transaction over this collection. Citing the official documentation:

EJDB provides atomic and durable non parallel and read-uncommitted collection level transactions, i.e., There is only one transaction for collection is active for a single point in a time. The data written in a transaction is visible for other non transactional readers. EJDB transaction system utilizes write ahead logging to provide consistent transaction rollbacks.

Transactions in ejdb.rs are implemented with RAII pattern: a transaction is represented by a guard object. When this object is dropped, the transaction is committed or aborted. By default it is aborted; but you can change the default behavior with corresponding methods. Alternatively, you can explicitly commit or abort the transaction with Transaction::commit() or Transaction::abort(), respectively. Additionally, these methods return a Result<()> which can be used to track errors; when the transaction is closed on its drop, the result is ignored.

loop {
    let tx = coll.begin_transaction().unwrap();
    // execute queries and other operations
    // if some error happens and the loop exits prematurely, e.g. through unwinding,
    // the transaction will be aborted automatically

    // try to commit the transaction and try again if there is an error
    if let Ok(_) = tx.commit() {
        break;
    }
}

Indices

It is also possible to use Collection::index() method to configure indices in the collection. index() accepts the name of the field on which the user needs to configure indices; it returns a builder-like object which can be used to tweak indices on this field.

In EJDB a field can have several associated indices of different types, which is important for heterogeneous fields. It is also possible to rebuild and optimize indices. This can be done with the respective methods on Index structure returned by Collection::index().

// create a case-sensitive string index on field `name`
coll.index("name").string(true).set().unwrap();

// create case-insensitive string and numeric indices on field `title`
coll.index("title").number().string(false).set().unwrap();

// remove number and array indices from field `items`
coll.index("items").number().array().drop().unwrap();

// optimize string index on field `name`
coll.index("name").string(true).optimize().unwrap();

// drop all indices on field `properties`
coll.index("properties").drop_all();

All consuming methods except for Index::drop_all() will panic if index type is not specified before their invocation:

coll.index("name").set();  // will panic

Re-exports

pub extern crate bson as bson_crate;
pub extern crate ejdb_sys;
pub use bson_crate as bson;
pub use types::Error;
pub use types::Result;

Modules

ejdb_bson

Contains low-level utilities for conversion between Rust and EJDB BSON representations.

meta

Types returned by metadata query method on Database structure.

open_mode

Database open mode constants.

query

Query API, a simple builder-like constructor for EJDB queries.

types

Various common types.

Macros

bson

A convenience macro to construct BSON documents.

Structs

Collection

A handle to an EJDB collection.

CollectionOptions

Represents a set of options of an EJDB collection.

Database

An EJDB database handle.

DatabaseOpenMode

Several bit flags defining how an EJDB database should be opened.

Index

A builder for an operation on an index of a certain field of an EJDB collection.

PreparedQuery

Represents a query which is ready to be executed.

QueryResult

An iterator over EJDB query results.

Transaction

Represents an active transaction.