W3docs

Requête MongoDB en Python : Filtres, Opérateurs et Projection

Apprenez à interroger MongoDB avec Python et pymongo — filtrez des documents, utilisez des opérateurs de comparaison et logiques, projetez des champs et paginez les résultats.

Ce chapitre explique comment construire des requêtes dans MongoDB à l'aide du pilote Python pymongo. Vous apprendrez à filtrer des documents avec des correspondances exactes et des opérateurs de comparaison, à combiner des conditions avec des opérateurs logiques, à sélectionner uniquement les champs dont vous avez besoin grâce à la projection, et à paginer les résultats avec skip() et limit().

Si vous n'avez pas encore configuré de connexion, consultez d'abord MongoDB Get Started et MongoDB Create Collection.

Configuration des données d'exemple

Tous les exemples de ce chapitre utilisent la même collection customers. Exécutez ce fragment une seule fois pour la remplir :

import pymongo

client = pymongo.MongoClient("mongodb://localhost:27017/")
db = client["mystore"]
col = db["customers"]

# Drop and re-create so examples are repeatable
col.drop()
col.insert_many([
    {"name": "Alice", "age": 30, "city": "London",   "score": 88},
    {"name": "Bob",   "age": 25, "city": "New York",  "score": 74},
    {"name": "Carol", "age": 35, "city": "London",   "score": 91},
    {"name": "Dave",  "age": 28, "city": "Berlin",    "score": 65},
    {"name": "Eve",   "age": 22, "city": "New York",  "score": 77},
])
print("Sample data inserted.")

Interroger tous les documents

Appeler find() avec un filtre vide ({}) renvoie tous les documents de la collection :

for doc in col.find({}):
    print(doc)
# {'_id': ..., 'name': 'Alice', 'age': 30, 'city': 'London',   'score': 88}
# {'_id': ..., 'name': 'Bob',   'age': 25, 'city': 'New York',  'score': 74}
# ...

Utilisez find_one() lorsque vous n'avez besoin que du premier document correspondant :

doc = col.find_one({"city": "London"})
print(doc)
# {'_id': ..., 'name': 'Alice', 'age': 30, 'city': 'London', 'score': 88}

find_one() renvoie None si aucun document ne correspond — vérifiez toujours ce cas avant d'accéder aux champs.

Filtres de correspondance exacte

Passez un dictionnaire à find() pour trouver les documents où un champ est égal à une valeur spécifique :

# All customers in London
results = col.find({"city": "London"})
for doc in results:
    print(doc["name"], doc["city"])
# Alice London
# Carol London

Plusieurs clés dans le même dictionnaire de filtre agissent comme un ET implicite — les deux conditions doivent être vraies :

# Customers in London AND older than 30
results = col.find({"city": "London", "age": {"$gt": 30}})
for doc in results:
    print(doc["name"], doc["age"])
# Carol 35

Opérateurs de comparaison

Les opérateurs de comparaison MongoDB vous permettent de trouver des documents où un champ se situe dans une plage ou un ensemble donné. Tous les opérateurs sont préfixés par $.

OpérateurSignificationExemple de filtre
$eqÉgal à{"age": {"$eq": 30}}
$neDifférent de{"city": {"$ne": "London"}}
$gtSupérieur à{"score": {"$gt": 80}}
$gteSupérieur ou égal{"score": {"$gte": 80}}
$ltInférieur à{"age": {"$lt": 28}}
$lteInférieur ou égal{"age": {"$lte": 28}}
$inDans une liste{"city": {"$in": ["London", "Berlin"]}}
$ninPas dans une liste{"city": {"$nin": ["London"]}}

Exemple — clients avec un score supérieur à 80 :

results = col.find({"score": {"$gt": 80}})
for doc in results:
    print(doc["name"], doc["score"])
# Alice 88
# Carol 91

Exemple — clients âgés de 25 à 30 ans (inclus) :

results = col.find({"age": {"$gte": 25, "$lte": 30}})
for doc in results:
    print(doc["name"], doc["age"])
# Alice 30
# Bob   25
# Dave  28

Vous pouvez combiner plusieurs opérateurs sur le même champ dans un seul dictionnaire, comme illustré ci-dessus.

Exemple — clients à Londres ou à Berlin :

results = col.find({"city": {"$in": ["London", "Berlin"]}})
for doc in results:
    print(doc["name"], doc["city"])
# Alice London
# Carol London
# Dave  Berlin

Opérateurs logiques

Utilisez $and, $or et $nor lorsque vous avez besoin de combiner des conditions d'une manière que les clés de dictionnaire ordinaires ne peuvent pas exprimer.

$or — au moins une condition doit correspondre

# Customers younger than 25 OR with a score above 90
results = col.find({"$or": [{"age": {"$lt": 25}}, {"score": {"$gt": 90}}]})
for doc in results:
    print(doc["name"], doc["age"], doc["score"])
# Carol 35 91
# Eve   22 77

$and — à utiliser lorsque deux opérateurs différents s'appliquent au même champ

Le ET implicite (clés multiples) ne fonctionne pas lorsque vous avez besoin de deux opérateurs $ sur le même champ. Utilisez plutôt $and :

# Customers whose score is > 65 AND < 90
# (cannot use {"score": {"$gt": 65}, "score": {"$lt": 90}} — duplicate key)
results = col.find({"$and": [{"score": {"$gt": 65}}, {"score": {"$lt": 90}}]})
for doc in results:
    print(doc["name"], doc["score"])
# Alice 88
# Bob   74
# Eve   77

$nor — aucune des conditions ne doit correspondre

# Customers who are NOT in London AND do NOT have score > 80
results = col.find({"$nor": [{"city": "London"}, {"score": {"$gt": 80}}]})
for doc in results:
    print(doc["name"], doc["city"], doc["score"])
# Bob  New York 74
# Dave Berlin   65
# Eve  New York 77

$not — nier une expression de champ unique

$not enveloppe une expression d'opérateur unique et renvoie les documents pour lesquels la condition est fausse ou le champ n'existe pas :

# Customers who do NOT have a score greater than 80
results = col.find({"score": {"$not": {"$gt": 80}}})
for doc in results:
    print(doc["name"], doc["score"])
# Bob  74
# Dave 65
# Eve  77

Filtres par expression régulière

Utilisez l'opérateur $regex (ou passez un modèle re compilé) pour effectuer une correspondance de sous-chaîne ou de motif sur des champs de type string :

import re

# Customers whose name starts with a vowel
results = col.find({"name": {"$regex": "^[AEIOU]", "$options": "i"}})
for doc in results:
    print(doc["name"])
# Alice
# Eve

$options: "i" rend la correspondance insensible à la casse. Évitez les motifs avec un joker en début de chaîne comme .*text sur de grandes collections — ils ne peuvent pas utiliser d'index et effectueront un balayage complet de la collection.

Projection — renvoyer uniquement des champs spécifiques

Par défaut, find() renvoie tous les champs du document, y compris _id. Utilisez un second argument (la projection) pour inclure ou exclure des champs.

Inclure des champs spécifiques

Passez 1 pour chaque champ souhaité. Seuls ces champs (plus _id) sont renvoyés :

# Return only name and score
results = col.find({}, {"name": 1, "score": 1})
for doc in results:
    print(doc)
# {'_id': ..., 'name': 'Alice', 'score': 88}
# {'_id': ..., 'name': 'Bob',   'score': 74}
# ...

Exclure des champs spécifiques

Passez 0 pour chaque champ à masquer. Tous les autres champs sont renvoyés :

# Hide _id and city
results = col.find({}, {"_id": 0, "city": 0})
for doc in results:
    print(doc)
# {'name': 'Alice', 'age': 30, 'score': 88}
# {'name': 'Bob',   'age': 25, 'score': 74}
# ...

Vous ne pouvez pas mélanger 1 et 0 dans la même projection (sauf pour _id, qui peut toujours être mis à 0 en même temps que des inclusions).

Trier les résultats

Enchaînez .sort() sur le curseur pour ordonner les résultats. Utilisez pymongo.ASCENDING (1) ou pymongo.DESCENDING (-1) :

import pymongo

# Sort by score descending
results = col.find({}, {"_id": 0, "name": 1, "score": 1}).sort("score", pymongo.DESCENDING)
for doc in results:
    print(doc["name"], doc["score"])
# Carol 91
# Alice 88
# Eve   77
# Bob   74
# Dave  65

Passez une liste de tuples (champ, direction) pour trier selon plusieurs champs :

# Sort by city ascending, then by age descending within each city
results = col.find({}, {"_id": 0, "name": 1, "city": 1, "age": 1}).sort(
    [("city", pymongo.ASCENDING), ("age", pymongo.DESCENDING)]
)
for doc in results:
    print(doc["city"], doc["name"], doc["age"])
# Berlin  Dave  28
# London  Carol 35
# London  Alice 30
# New York Bob   25   <- Bob (25) vs Eve (22): descending so 25 first
# New York Eve   22

Consultez MongoDB Sort pour un examen approfondi des options de tri.

Limiter et paginer les résultats

limit()

limit(n) arrête le curseur après avoir renvoyé n documents :

# Top 3 by score
results = col.find({}, {"_id": 0, "name": 1, "score": 1}).sort("score", -1).limit(3)
for doc in results:
    print(doc["name"], doc["score"])
# Carol 91
# Alice 88
# Eve   77

skip() pour la pagination

Combinez skip() et limit() pour implémenter une navigation simple page par page :

page_size = 2
page = 1  # zero-indexed

results = (
    col.find({}, {"_id": 0, "name": 1, "score": 1})
       .sort("score", -1)
       .skip(page * page_size)
       .limit(page_size)
)
for doc in results:
    print(doc["name"], doc["score"])
# Eve  77   (page 1, items 3–4 of the sorted list)
# Bob  74

Pour les grandes collections, préférez la pagination basée sur des plages — filtrez sur le dernier _id vu ou un horodatage — car skip() parcourt quand même les documents ignorés en interne.

Consultez MongoDB Limit pour plus de détails.

Compter les documents

Utilisez count_documents() avec le même dictionnaire de filtre pour compter sans récupérer les données :

total = col.count_documents({})
london = col.count_documents({"city": "London"})
print(f"Total: {total}, In London: {london}")
# Total: 5, In London: 2

Évitez le cursor.count() déprécié — il a été supprimé dans PyMongo 4.

Pièges courants

Les curseurs PyMongo sont épuisés après l'itération. Une fois que vous avez parcouru un curseur, il est vide. Stockez les résultats dans une liste si vous avez besoin d'itérer plusieurs fois :

docs = list(col.find({"city": "London"}))
print(len(docs))   # 2
print(docs[0]["name"])  # Alice

_id est un ObjectId, pas une string. Si vous stockez un _id en tant que string et essayez ensuite de l'interroger, la requête ne renverra rien. Importez ObjectId depuis bson pour effectuer la conversion :

from bson import ObjectId

doc = col.find_one({"_id": ObjectId("665000000000000000000001")})

Les requêtes sont sensibles à la casse par défaut. {"city": "london"} ne correspondra pas aux documents stockés avec "London". Utilisez $regex avec $options: "i" pour une correspondance insensible à la casse, ou normalisez les valeurs lors de l'insertion.

Prochaines étapes

Was this page helpful?