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

Consultas

RouchDB ofrece dos formas de consultar documentos: consultas Mango (selectores declarativos) y vistas map/reduce (funciones personalizadas).

Consultas Mango

Mango te permite buscar documentos usando selectores JSON, similar a MongoDB:

#![allow(unused)]
fn main() {
use rouchdb::{Database, FindOptions};

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

// Insertar datos de ejemplo
db.put("alice", serde_json::json!({"name": "Alice", "age": 30, "role": "admin"})).await?;
db.put("bob", serde_json::json!({"name": "Bob", "age": 25, "role": "user"})).await?;
db.put("carol", serde_json::json!({"name": "Carol", "age": 35, "role": "admin"})).await?;

// Buscar admins mayores de 28
let result = db.find(FindOptions {
    selector: serde_json::json!({
        "role": "admin",
        "age": {"$gte": 28}
    }),
    ..Default::default()
}).await?;

for doc in &result.docs {
    println!("{}", doc["name"]); // Alice, Carol
}
}

Operadores disponibles

OperadorDescripcionEjemplo
$eqIgual a{"name": {"$eq": "Alice"}} o {"name": "Alice"}
$neNo igual a{"role": {"$ne": "admin"}}
$gtMayor que{"age": {"$gt": 25}}
$gteMayor o igual que{"age": {"$gte": 25}}
$ltMenor que{"age": {"$lt": 30}}
$lteMenor o igual que{"age": {"$lte": 30}}
$inEsta en la lista{"role": {"$in": ["admin", "mod"]}}
$ninNo esta en la lista{"role": {"$nin": ["banned"]}}
$existsEl campo existe{"email": {"$exists": true}}
$regexCoincide con regex{"name": {"$regex": "^A"}}
$orLogico OR{"$or": [{"age": 25}, {"age": 30}]}
$andLogico AND{"$and": [{"age": {"$gt": 20}}, {"role": "admin"}]}
$notNegacion{"age": {"$not": {"$gt": 30}}}
$elemMatchCoincide en array{"tags": {"$elemMatch": {"$eq": "rust"}}}
$allContiene todos{"tags": {"$all": ["rust", "db"]}}
$sizeTamano del array{"tags": {"$size": 3}}
$modModulo{"age": {"$mod": [5, 0]}}
$typeTipo de valor{"name": {"$type": "string"}}

Proyeccion de campos

Selecciona solo los campos que necesitas:

#![allow(unused)]
fn main() {
let result = db.find(FindOptions {
    selector: serde_json::json!({"role": "admin"}),
    fields: Some(vec!["name".into(), "age".into()]),
    ..Default::default()
}).await?;
// Los documentos solo contienen _id, name, age
}

Ordenamiento

#![allow(unused)]
fn main() {
use rouchdb::SortField;

let result = db.find(FindOptions {
    selector: serde_json::json!({"age": {"$gt": 0}}),
    sort: Some(vec![
        SortField::Simple("age".into()),  // ascendente por defecto
    ]),
    ..Default::default()
}).await?;
}

Paginacion

#![allow(unused)]
fn main() {
let result = db.find(FindOptions {
    selector: serde_json::json!({"age": {"$gt": 0}}),
    skip: Some(10),
    limit: Some(5),
    ..Default::default()
}).await?;
}

Vistas Map/Reduce

Para consultas mas complejas, usa query_view con funciones map y reduce de Rust:

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

// Map: emite pares clave-valor por cada documento
let result = query_view(
    db.adapter(),
    &|doc| {
        // Emitir el rol como clave y 1 como valor
        if let Some(role) = doc.get("role").and_then(|r| r.as_str()) {
            vec![(serde_json::json!(role), serde_json::json!(1))]
        } else {
            vec![]
        }
    },
    Some(&ReduceFn::Count),  // Contar por grupo
    ViewQueryOptions {
        reduce: true,
        group: true,
        ..ViewQueryOptions::new()
    },
).await?;

for row in &result.rows {
    println!("{}: {} usuarios", row.key, row.value);
}
// "admin": 2 usuarios
// "user": 1 usuarios
}

Reduces integrados

FuncionDescripcion
ReduceFn::SumSuma todos los valores numericos
ReduceFn::CountCuenta el numero de filas
ReduceFn::StatsCalcula estadisticas: sum, count, min, max, sumsqr
ReduceFn::Custom(fn)Funcion reduce personalizada

Rangos de claves

#![allow(unused)]
fn main() {
let result = query_view(
    db.adapter(),
    &|doc| {
        let age = doc.get("age").cloned().unwrap_or(serde_json::json!(null));
        let name = doc.get("name").cloned().unwrap_or(serde_json::json!(null));
        vec![(age, name)]
    },
    None,
    ViewQueryOptions {
        start_key: Some(serde_json::json!(25)),
        end_key: Some(serde_json::json!(35)),
        ..ViewQueryOptions::new()
    },
).await?;
}

Mango vs Map/Reduce: cuando usar cada uno

CriterioMangoMap/Reduce
SimplicidadSelectores JSON, facil de usarRequiere escribir closures
FlexibilidadLimitado a operadores predefinidosLogica arbitraria de Rust
AgregacionesNo soportadoSum, Count, Stats, Custom
AgrupamientoNo soportadogroup, group_level
Uso tipicoFiltrar documentosReportes y analisis