This guide introduces expr::ExprBuilder, then shows how to reuse typed expressions in filters ($expr), projections, and updates.

Building Typed Expressions

ExprBuilder creates BSON expression trees with compile-time constraints.

use tnuctipun::{expr, FieldWitnesses, MongoComparable};
use serde::{Deserialize, Serialize};

#[derive(Debug, Serialize, Deserialize, FieldWitnesses, MongoComparable)]
struct Product {
    pub price: f64,
    pub stock: i32,
    pub reserved: i32,
    pub min_stock: i32,
}

fn expression_basics() {
    let b = expr::empty::<Product>();

    let stock = b.select::<product_fields::Stock>();
    let reserved = b.select::<product_fields::Reserved>();

    // available = stock - reserved
    let available = b.subtract(stock, reserved);

    // low_stock = available < min_stock
    let low_stock = b.lt(available, b.select::<product_fields::MinStock>());

    assert_eq!(
        low_stock.into_bson(),
        bson::doc! {
            "$lt": [
                { "$subtract": ["$stock", "$reserved"] },
                "$min_stock"
            ]
        }
        .into()
    );
}

See also: Finding Documents - Expression Builder ($expr)

Using Expressions in Filters ($expr)

FilterBuilder::expr inserts a typed boolean expression under MongoDB’s $expr operator.

use tnuctipun::{expr, filters, FieldWitnesses, MongoComparable};
use serde::{Deserialize, Serialize};

#[derive(Debug, Serialize, Deserialize, FieldWitnesses, MongoComparable)]
struct Product {
    pub name: String,
    pub stock: i32,
    pub reserved: i32,
    pub min_stock: i32,
}

fn filter_with_expr() {
    let b = expr::empty::<Product>();

    let low_stock = b.lt(
        b.subtract(
            b.select::<product_fields::Stock>(),
            b.select::<product_fields::Reserved>(),
        ),
        b.select::<product_fields::MinStock>(),
    );

    let filter_doc = filters::empty::<Product>()
        .eq::<product_fields::Name, _>("Widget".to_string())
        .expr(low_stock)
        .and();

    // Produces a query containing "$expr": { ... }
    println!("{}", filter_doc);
}

See also: Finding Documents

Using Expressions in Projections

projection::ProjectionBuilder::project_expr lets you compute projected field values using typed expressions.

use tnuctipun::{expr, projection, FieldWitnesses, MongoComparable};
use serde::{Deserialize, Serialize};

#[derive(Debug, Serialize, Deserialize, FieldWitnesses, MongoComparable)]
struct InventoryView {
    pub stock: i32,
    pub reserved: i32,
    pub available: i32,
}

fn projection_with_expr() {
    let b = expr::empty::<InventoryView>();

    let mut proj = projection::empty::<InventoryView>();
    proj.project_expr::<inventoryview_fields::Available, _>(
        b.subtract(
            b.select::<inventoryview_fields::Stock>(),
            b.select::<inventoryview_fields::Reserved>(),
        ),
    );

    let projection_doc = proj.build();
    println!("{}", projection_doc);
}

See also: Finding Documents - Projections

Using Expressions in Updates

updates::UpdateBuilder::set_expr applies an expression as the new value of a field.

use tnuctipun::{expr, updates, FieldWitnesses, MongoComparable};
use serde::{Deserialize, Serialize};

#[derive(Debug, Serialize, Deserialize, FieldWitnesses, MongoComparable)]
struct ScoreCard {
    pub score: i32,
    pub bonus: i32,
}

fn update_with_expr() {
    let b = expr::empty::<ScoreCard>();

    let update_doc = updates::empty::<ScoreCard>()
        .set_expr::<scorecard_fields::Score, _>(
            b.add(
                b.select::<scorecard_fields::Score>(),
                vec![b.select::<scorecard_fields::Bonus>()],
            ),
        )
        .build();

    println!("{}", update_doc);
}

See also: Updating Documents - Expression-Based Updates

Notes on Type Constraints

  • Equality comparisons (eq, ne) use MongoComparable bounds.
  • Ordering comparisons (gt, gte, lt, lte) require MongoOrdered evidence.
  • If an expression uses an unsupported ordering type pair, compilation fails early.

See also: Derive Macros - MongoComparable Macro