mongor 0.1.10

Ergonomic MongoDB ODM
Documentation
// Authors: Robert Lopez
// License: MIT (See `LICENSE.md`)

use crate::error::Error;
use mongodb::{
    bson::{doc, Document},
    options::*,
    ClientSession, Collection, IndexModel,
};
use serde::{Deserialize, Serialize};
use std::time::Duration;

/// Drop an index by `name` on a `Collection`
///
/// https://www.mongodb.com/docs/manual/reference/method/db.collection.dropIndex/
///
/// ---
/// Example Usage:
/// ```
///
/// let collection: Collection<D> = ...;
///
/// let index_model: IndexModel = build_blank_index(doc! { "field": 1 }, Some("index_1"));
///
/// apply_index(&collection, index_model, None, None).await?;
///
/// drop_index(&collection, "index_1", None, None).await?;
/// ```
pub async fn drop_index<D>(
    collection: &Collection<D>,
    name: &str,
    options: Option<DropIndexOptions>,
    session: Option<&mut ClientSession>,
) -> Result<(), Error>
where
    D: Serialize + for<'a> Deserialize<'a> + Send + Sync,
{
    if let Some(session) = session {
        collection
            .drop_index(name)
            .with_options(options)
            .session(session)
            .await
            .map_err(Error::Mongo)?;
    } else {
        collection
            .drop_index(name)
            .with_options(options)
            .await
            .map_err(Error::Mongo)?;
    }

    Ok(())
}

/// Apply a `IndexModel` to a `Collection`
///
/// https://www.mongodb.com/docs/manual/indexes/
///
/// ---
/// Example Usage:
/// ```
///
/// let collection: Collection<D> = ...;
///
/// let index_model: IndexModel = build_blank_index(
///     doc! { "field": 1 },
///     None::<&str>,
/// );
///
/// apply_index(
///     &collection,
///     index_model,
///     None::<CreateIndexOptions>,
///     None::<&mut ClientSession>,
/// ).await?;
/// ```
pub async fn apply_index<D>(
    collection: &Collection<D>,
    index_model: IndexModel,
    options: Option<CreateIndexOptions>,
    session: Option<&mut ClientSession>,
) -> Result<(), Error>
where
    D: Serialize + for<'a> Deserialize<'a> + Send + Sync,
{
    if let Some(session) = session {
        collection
            .create_index(index_model)
            .with_options(options)
            .session(session)
            .await
            .map_err(Error::Mongo)?;
    } else {
        collection
            .create_index(index_model)
            .with_options(options)
            .await
            .map_err(Error::Mongo)?;
    }

    Ok(())
}

/// Builds a Time-To-Live (TTL) `IndexModel` based on seconds
/// on document field named `field_name`
///
/// https://www.mongodb.com/docs/manual/core/index-ttl/
///
/// ---
/// Example Usage:
/// ```
///
/// let index_model: IndexModel = build_ttl_index(
///     86_400,
///     "created_at",
///     None::<&str>,
/// );
/// ```
pub fn build_ttl_index(secs: u64, field_name: &str, name: Option<&str>) -> IndexModel {
    let name = name.map(|name| name.to_string());
    let index_options = IndexOptions::builder()
        .name(name)
        .expire_after(Duration::new(secs, 0))
        .build();

    IndexModel::builder()
        .keys(doc! { field_name: 1 })
        .options(index_options)
        .build()
}

/// Builds a "blank" (No properties) `IndexModel` based on the provided fields
///
/// https://www.mongodb.com/docs/manual/indexes/
///
/// ---
/// Example Usage:
/// ```
///
/// let index_model: IndexModel = build_blank_index(
///     doc! { "user": 1, "email": 1 },
///     None::<&str>,
/// );
/// ```
pub fn build_blank_index(fields: Document, name: Option<&str>) -> IndexModel {
    let name = name.map(|name| name.to_string());
    let index_options = IndexOptions::builder().name(name).build();

    IndexModel::builder()
        .keys(fields)
        .options(Some(index_options))
        .build()
}

/// Builds a unique `IndexModel` based on the provided fields
///
/// https://www.mongodb.com/docs/manual/core/index-unique/
///
/// ---
/// Example Usage:
/// ```
///
/// let index_model: IndexModel = build_unique_index(
///     doc! { "user": 1, "email": 1 },
///     None::<&str>,
/// );
/// ```
pub fn build_unique_index(fields: Document, name: Option<&str>) -> IndexModel {
    let name = name.map(|name| name.to_string());
    let index_options = IndexOptions::builder().unique(true).name(name).build();

    IndexModel::builder()
        .keys(fields)
        .options(index_options)
        .build()
}

#[cfg(test)]
mod tests {
    use super::*;
    use mongodb::bson::doc;

    #[test]
    fn test_build_ttl_index() {
        build_ttl_index(1337, "created_at", Some("ttl_index"));
    }

    #[test]
    fn test_build_blank_index() {
        build_blank_index(
            doc! {
                "name": 1,
                "year": 1,
            },
            None,
        );
    }

    #[test]
    fn test_build_unique_index() {
        build_unique_index(
            doc! {
                "name": 1,
                "year": 1,
            },
            Some("unique_index"),
        );
    }
}