Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Design Documents & Views

Design documents are special documents with IDs starting with _design/. They define views, filters, and validation functions. In RouchDB, design documents are stored as DesignDocument structs and can be used with the ViewEngine for Rust-native map/reduce queries.

Design Document CRUD

Creating a Design Document

#![allow(unused)]
fn main() {
use rouchdb::{Database, DesignDocument, ViewDef};
use std::collections::HashMap;

let db = Database::memory("mydb");

let ddoc = DesignDocument {
    id: "_design/myapp".into(),
    rev: None,
    views: {
        let mut v = HashMap::new();
        v.insert("by_type".into(), ViewDef {
            map: "function(doc) { emit(doc.type, 1); }".into(),
            reduce: Some("_count".into()),
        });
        v
    },
    filters: HashMap::new(),
    validate_doc_update: None,
    shows: HashMap::new(),
    lists: HashMap::new(),
    updates: HashMap::new(),
    language: Some("javascript".into()),
};

let result = db.put_design(ddoc).await?;
assert!(result.ok);
}

Reading a Design Document

Pass the short name (without _design/ prefix):

#![allow(unused)]
fn main() {
let ddoc = db.get_design("myapp").await?;
println!("ID: {}", ddoc.id); // "_design/myapp"
println!("Views: {:?}", ddoc.views.keys().collect::<Vec<_>>());
}

Updating a Design Document

Read the document, modify it, and put it back with the current revision:

#![allow(unused)]
fn main() {
let mut ddoc = db.get_design("myapp").await?;
ddoc.views.insert("by_name".into(), ViewDef {
    map: "function(doc) { emit(doc.name); }".into(),
    reduce: None,
});
let result = db.put_design(ddoc).await?;
}

Deleting a Design Document

#![allow(unused)]
fn main() {
let ddoc = db.get_design("myapp").await?;
let rev = ddoc.rev.unwrap();
db.delete_design("myapp", &rev).await?;
}

DesignDocument Fields

FieldTypeDescription
idStringMust start with _design/.
revOption<String>Current revision (set after reading).
viewsHashMap<String, ViewDef>Named view definitions with map and optional reduce.
filtersHashMap<String, String>Named filter functions.
validate_doc_updateOption<String>Validation function source.
showsHashMap<String, String>Show functions.
listsHashMap<String, String>List functions.
updatesHashMap<String, String>Update handler functions.
languageOption<String>Language for the functions (e.g., "javascript").

ViewEngine (Rust-Native Views)

For local databases, RouchDB provides a ViewEngine that runs map/reduce using Rust closures instead of JavaScript. This is faster and type-safe.

Registering a View

#![allow(unused)]
fn main() {
use rouchdb::{Database, ViewEngine, ViewQueryOptions, query_view, ReduceFn};

let db = Database::memory("mydb");
db.put("alice", serde_json::json!({"type": "user", "name": "Alice", "age": 30})).await?;
db.put("bob", serde_json::json!({"type": "user", "name": "Bob", "age": 25})).await?;
db.put("inv1", serde_json::json!({"type": "invoice", "amount": 100})).await?;

let mut engine = ViewEngine::new();

// Register a Rust map function for "myapp/by_type"
engine.register_map("myapp", "by_type", |doc| {
    let doc_type = doc.get("type").and_then(|v| v.as_str()).unwrap_or("unknown");
    vec![(serde_json::json!(doc_type), serde_json::json!(1))]
});
}

Updating and Querying a View

The ViewEngine builds an index by scanning the changes feed. Call update_index() to build or refresh, then get_index() to access the results:

#![allow(unused)]
fn main() {
// Build/refresh the index
engine.update_index(db.adapter(), "myapp", "by_type").await?;

// Access the index entries
if let Some(index) = engine.get_index("myapp", "by_type") {
    for (doc_id, entries) in &index.entries {
        for (key, value) in entries {
            println!("{}: key={} value={}", doc_id, key, value);
        }
    }
}
}

For full map/reduce queries (with reduce, grouping, sorting), use the standalone query_view() function:

#![allow(unused)]
fn main() {
let map_fn = |doc: &serde_json::Value| -> Vec<(serde_json::Value, serde_json::Value)> {
    let doc_type = doc.get("type").and_then(|v| v.as_str()).unwrap_or("unknown");
    vec![(serde_json::json!(doc_type), serde_json::json!(1))]
};

let results = query_view(
    db.adapter(),
    &map_fn,
    Some(&ReduceFn::Count),
    ViewQueryOptions {
        reduce: true,
        group: true,
        ..ViewQueryOptions::new()
    },
).await?;

for row in &results.rows {
    println!("{}: {} documents", row.key, row.value);
}
}

Incremental Updates

The ViewEngine tracks the last sequence number and only processes new/changed documents on subsequent update_index() calls, making it efficient for large databases.

Cleanup

Remove unused view indexes:

#![allow(unused)]
fn main() {
db.view_cleanup().await?;
}