CouchDB : requêtes
Les requêtes Mango (_find) décrivent des critères en JSON. Elles s’appuient sur des index (voir Index et performances). Exemples sur la base boutique avec des documents client et commande.
Principe
Section intitulée « Principe »curl -sn -X POST "$COUCH/boutique/_find" \ -H 'Content-Type: application/json' \ -d '{"selector":{"type":"client","actif":true},"fields":["_id","nom","email"],"limit":10}' | jq .| Clé | Rôle |
|---|---|
selector |
Critères de sélection (obligatoire) |
fields |
Champs retournés (sinon le document entier) |
sort |
Tri (nécessite un index correspondant) |
limit |
Nombre maximal de résultats (25 par défaut) |
skip |
Résultats à ignorer (déconseillé pour paginer) |
bookmark |
Signet de pagination |
use_index |
Force l’index utilisé |
execution_stats |
Statistiques d’exécution |
Pour écrire les requêtes dans un fichier :
cat > requete.json <<'JSON'{ "selector": { "type": "client", "nom": { "$regex": "^A" } }, "fields": ["_id", "nom"], "limit": 20}JSONcurl -sn -X POST "$COUCH/boutique/_find" -H 'Content-Type: application/json' -d @requete.json | jq .Opérateurs de comparaison
Section intitulée « Opérateurs de comparaison »{ "selector": { "age": { "$gte": 18, "$lt": 65 } } }{ "selector": { "statut": { "$in": ["nouvelle", "payee"] } } }{ "selector": { "statut": { "$ne": "annulee" } } }{ "selector": { "email": { "$exists": true } } }{ "selector": { "age": { "$type": "number" } } }{ "selector": { "nom": { "$regex": "(?i)^ali" } } }| Opérateur | Sens |
|---|---|
$eq, $ne |
Égal, différent |
$gt, $gte, $lt, $lte |
Comparaisons |
$in, $nin |
Dans / hors d’une liste |
$exists |
Le champ existe |
$type |
Type du champ |
$regex |
Expression régulière |
$mod, $size |
Reste de division, taille d’un tableau |
$all, $elemMatch |
Tableaux |
L’égalité simple s’écrit {"nom":"Alice"} (équivalent à {"nom":{"$eq":"Alice"}}).
Opérateurs logiques
Section intitulée « Opérateurs logiques »{ "selector": { "$and": [ { "type": "commande" }, { "$or": [{ "statut": "nouvelle" }, { "statut": "payee" }] }, { "total": { "$gt": 100 } } ] }}Autres : $not, $nor. Des critères séparés dans un même objet sont combinés par un « et » implicite.
Champs imbriqués et tableaux
Section intitulée « Champs imbriqués et tableaux »{ "selector": { "adresse.ville": "Lyon" } }{ "selector": { "lignes": { "$elemMatch": { "article": "A12", "quantite": { "$gte": 2 } } } } }{ "selector": { "tags": { "$all": ["promo", "nouveau"] } } }Tri et pagination
Section intitulée « Tri et pagination »Le tri exige un index sur les champs triés :
curl -sn -X POST "$COUCH/boutique/_index" -H 'Content-Type: application/json' \ -d '{"index":{"fields":["type","nom"]},"name":"idx-type-nom","type":"json"}'
curl -sn -X POST "$COUCH/boutique/_find" -H 'Content-Type: application/json' \ -d '{"selector":{"type":"client","nom":{"$gt":null}},"sort":[{"type":"asc"},{"nom":"asc"}],"limit":10}' | jq .Pagination par signet (plus fiable et plus rapide que skip) :
r=$(curl -sn -X POST "$COUCH/boutique/_find" -H 'Content-Type: application/json' \ -d '{"selector":{"type":"client","nom":{"$gt":null}},"sort":[{"type":"asc"},{"nom":"asc"}],"limit":10}')echo "$r" | jq '.docs | length'signet=$(echo "$r" | jq -r .bookmark)curl -sn -X POST "$COUCH/boutique/_find" -H 'Content-Type: application/json' \ -d "{\"selector\":{\"type\":\"client\",\"nom\":{\"\$gt\":null}},\"sort\":[{\"type\":\"asc\"},{\"nom\":\"asc\"}],\"limit\":10,\"bookmark\":\"$signet\"}" | jq '.docs | length'Un champ de tri doit aussi apparaître dans le sélecteur (d’où "nom":{"$gt":null}).
Comprendre une requête : _explain
Section intitulée « Comprendre une requête : _explain »curl -sn -X POST "$COUCH/boutique/_explain" -H 'Content-Type: application/json' \ -d '{"selector":{"type":"client","nom":"Alice"}}' | jq '{index: .index.name, limit, fields}'Un index _all_docs dans la réponse signifie qu’aucun index adapté n’existe : la requête parcourt toute la base. Créer un index correspondant.
curl -sn -X POST "$COUCH/boutique/_find" -H 'Content-Type: application/json' \ -d '{"selector":{"type":"client"},"execution_stats":true,"limit":1}' | jq .execution_statstotal_docs_examined bien supérieur à results_returned signale un manque d’index.
Lire par identifiant ou par plage (le plus rapide)
Section intitulée « Lire par identifiant ou par plage (le plus rapide) »curl -sn "$COUCH/boutique/client-2" | jq .curl -sn "$COUCH/boutique/_all_docs?startkey=\"commande:2026\"&endkey=\"commande:2026\"&include_docs=true" | jq '.rows | length'Vues (MapReduce)
Section intitulée « Vues (MapReduce) »Une vue produit un index à partir d’une fonction JavaScript (voir Index et performances pour la création) :
curl -sn "$COUCH/boutique/_design/stats/_view/par-client?key=\"client-2\"&include_docs=true" | jq .curl -sn "$COUCH/boutique/_design/stats/_view/total-par-client?group=true" | jq .rows| Paramètre | Rôle |
|---|---|
key, keys |
Clé(s) exacte(s) |
startkey, endkey |
Intervalle de clés |
limit, skip |
Pagination |
descending=true |
Ordre inverse |
include_docs=true |
Joint le document |
reduce=false |
Ignorer la fonction de réduction |
group=true |
Regrouper par clé |
Les valeurs de key sont du JSON : les chaînes sont entre guillemets ("client-2").
Modifier plusieurs documents
Section intitulée « Modifier plusieurs documents »CouchDB n’a pas de UPDATE … WHERE. Procédure : sélectionner avec _find, modifier avec jq, renvoyer avec _bulk_docs.
curl -sn -X POST "$COUCH/boutique/_find" -H 'Content-Type: application/json' \ -d '{"selector":{"type":"client","actif":true},"limit":1000}' \ | jq '{docs: [.docs[] | .actif = false]}' \ | curl -sn -X POST "$COUCH/boutique/_bulk_docs" -H 'Content-Type: application/json' -d @- | jq 'map(select(.error))'La dernière commande affiche les documents en erreur (conflits) : s’il y en a, relancer.
Suivre les modifications : _changes
Section intitulée « Suivre les modifications : _changes »curl -sn "$COUCH/boutique/_changes?since=now&feed=longpoll&timeout=30000" | jq .curl -sn "$COUCH/boutique/_changes?limit=5&include_docs=true" | jq '.results[].id'Le flux _changes liste les modifications dans l’ordre : c’est la base de la réplication et des traitements asynchrones.
Dépannage
Section intitulée « Dépannage »| Symptôme | Piste |
|---|---|
{"error":"no_usable_index","reason":"No index exists for this sort…"} |
Créer un index sur les champs de sort |
Requête lente, _all_docs dans _explain |
Index manquant ou inutilisable (opérateur non indexable comme $regex ou $ne) |
warning: No matching index found |
Même cause : création d’un index |
| Résultats incomplets | limit par défaut à 25 : pagination avec signet |
bookmark renvoie les mêmes résultats |
Signet non repris, ou requête différente entre deux appels |
{"error":"invalid_json"} |
Échappement dans le shell : utiliser un fichier -d @requete.json |
$regex très lent |
Une expression régulière ne tire pas parti des index : préférer des préfixes avec $gte/$lt |
Ubuntu 24.04 et 26.04 : différences à connaître
Section intitulée « Ubuntu 24.04 et 26.04 : différences à connaître »Aucune : Mango dépend de la version de CouchDB.
Pour aller plus loin
Section intitulée « Pour aller plus loin »- Requêtes Mango
- Suite : Utilisateurs et droits