Aller au contenu

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.

Fenêtre de terminal
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 :

Fenêtre de terminal
cat > requete.json <<'JSON'
{
"selector": { "type": "client", "nom": { "$regex": "^A" } },
"fields": ["_id", "nom"],
"limit": 20
}
JSON
curl -sn -X POST "$COUCH/boutique/_find" -H 'Content-Type: application/json' -d @requete.json | jq .
{ "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"}}).

{
"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.

{ "selector": { "adresse.ville": "Lyon" } }
{ "selector": { "lignes": { "$elemMatch": { "article": "A12", "quantite": { "$gte": 2 } } } } }
{ "selector": { "tags": { "$all": ["promo", "nouveau"] } } }

Le tri exige un index sur les champs triés :

Fenêtre de terminal
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) :

Fenêtre de terminal
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}).

Fenêtre de terminal
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.

Fenêtre de terminal
curl -sn -X POST "$COUCH/boutique/_find" -H 'Content-Type: application/json' \
-d '{"selector":{"type":"client"},"execution_stats":true,"limit":1}' | jq .execution_stats

total_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) »
Fenêtre de terminal
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'

Une vue produit un index à partir d’une fonction JavaScript (voir Index et performances pour la création) :

Fenêtre de terminal
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").

CouchDB n’a pas de UPDATE … WHERE. Procédure : sélectionner avec _find, modifier avec jq, renvoyer avec _bulk_docs.

Fenêtre de terminal
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.

Fenêtre de terminal
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.

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.