MongoDB Find
Impara a recuperare documenti MongoDB con Python usando find_one(), find(), proiezioni, operatori di query, sort, skip e limit con PyMongo.
Questo capitolo spiega come recuperare documenti da una collezione MongoDB usando il driver Python pymongo. Imparerai i metodi find_one() e find(), come filtrare i risultati con gli operatori di query, come controllare quali campi vengono restituiti con le proiezioni, e come ordinare, saltare e limitare i risultati.
Configurazione iniziale
Assicurati che pymongo sia installato prima di eseguire qualsiasi esempio:
pip install pymongoTutti gli esempi seguenti presuppongono un server MongoDB attivo su mongodb://localhost:27017/. Per seguire gli esempi sul tuo computer, avvia MongoDB con mongod oppure usa un cluster cloud gratuito (MongoDB Atlas).
Preparazione dei dati di esempio
Gli esempi in questo capitolo usano una collezione customers contenente questi cinque documenti. Esegui questo codice una sola volta per popolarla:
import pymongo
client = pymongo.MongoClient("mongodb://localhost:27017/")
db = client["mydatabase"]
col = db["customers"]
# Insert sample documents (skip if already inserted)
col.drop() # start fresh
col.insert_many([
{"name": "Alice", "age": 28, "city": "London"},
{"name": "Bob", "age": 34, "city": "Paris"},
{"name": "Carol", "age": 22, "city": "London"},
{"name": "David", "age": 40, "city": "Berlin"},
{"name": "Eve", "age": 34, "city": "Paris"},
])
print("Sample data ready.")Output atteso:
Sample data ready.PyMongo aggiunge automaticamente un campo _id univoco (un bson.ObjectId) a ogni documento che non ne ha già uno.
Recuperare un singolo documento con find_one()
find_one() restituisce il primo documento che corrisponde al filtro, oppure None se nessun documento corrisponde. È la scelta giusta quando ti aspetti esattamente un risultato (ad esempio, cercare un utente per email).
# Retrieve the first document in the collection
doc = col.find_one()
print(doc)
# {'_id': ObjectId('...'), 'name': 'Alice', 'age': 28, 'city': 'London'}Passa un filtro per trovare un documento specifico:
# Find the customer named Bob
bob = col.find_one({"name": "Bob"})
print(bob)
# {'_id': ObjectId('...'), 'name': 'Bob', 'age': 34, 'city': 'Paris'}Se nessun documento corrisponde, find_one() restituisce None, quindi proteggi sempre il codice da questo caso:
result = col.find_one({"name": "Zara"})
if result is None:
print("No document found.")Recuperare più documenti con find()
find() restituisce un cursore — un iteratore lazy su tutti i documenti corrispondenti. Nessun dato viene prelevato dal server finché non si itera.
Recuperare tutti i documenti
# Iterate every document in the collection
for doc in col.find():
print(doc["name"], doc["age"])Output atteso (l'ordine può variare senza un ordinamento esplicito):
Alice 28
Bob 34
Carol 22
David 40
Eve 34Filtrare con una corrispondenza esatta
Passa un dizionario come primo argomento a find():
# All customers in London
for doc in col.find({"city": "London"}):
print(doc["name"])
# Alice
# CarolProiezioni — Scegliere quali campi restituire
Per impostazione predefinita, MongoDB restituisce ogni campo incluso _id. Una proiezione ti consente di includere o escludere campi specifici, riducendo il traffico di rete e l'utilizzo di memoria.
Passa la proiezione come secondo argomento posizionale (o come argomento con nome projection):
# Return only name and city; suppress _id
for doc in col.find({}, {"_id": 0, "name": 1, "city": 1}):
print(doc)
# {'name': 'Alice', 'city': 'London'}
# {'name': 'Bob', 'city': 'Paris'}
# ...Regole per le proiezioni:
- Usa
1per includere un campo,0per escluderlo. - Non puoi mescolare inclusione ed esclusione nella stessa proiezione, tranne per
_id(che può sempre essere impostato esplicitamente a0).
Operatori di query
MongoDB offre un ricco insieme di operatori per filtrare i documenti. Passali all'interno del dizionario del filtro.
Operatori di confronto
| Operatore | Significato | Esempio |
|---|---|---|
$eq | Uguale (predefinito) | {"age": {"$eq": 34}} |
$ne | Diverso | {"city": {"$ne": "Paris"}} |
$gt | Maggiore di | {"age": {"$gt": 30}} |
$gte | Maggiore o uguale | {"age": {"$gte": 34}} |
$lt | Minore di | {"age": {"$lt": 30}} |
$lte | Minore o uguale | {"age": {"$lte": 28}} |
$in | Valore nella lista | {"city": {"$in": ["London", "Berlin"]}} |
$nin | Valore non nella lista | {"city": {"$nin": ["Paris"]}} |
Esempio — clienti con più di 30 anni:
for doc in col.find({"age": {"$gt": 30}}, {"_id": 0, "name": 1, "age": 1}):
print(doc)
# {'name': 'Bob', 'age': 34}
# {'name': 'David', 'age': 40}
# {'name': 'Eve', 'age': 34}Esempio — clienti a London o Berlin:
for doc in col.find(
{"city": {"$in": ["London", "Berlin"]}},
{"_id": 0, "name": 1, "city": 1}
):
print(doc)
# {'name': 'Alice', 'city': 'London'}
# {'name': 'Carol', 'city': 'London'}
# {'name': 'David', 'city': 'Berlin'}Operatori logici
AND implicito — fornire più chiavi in un unico dizionario filtro significa che tutte le condizioni devono essere soddisfatte:
# Age > 30 AND city is Paris
for doc in col.find({"age": {"$gt": 30}, "city": "Paris"}, {"_id": 0}):
print(doc)
# {'name': 'Bob', 'age': 34, 'city': 'Paris'}
# {'name': 'Eve', 'age': 34, 'city': 'Paris'}$and è necessario quando occorre applicare due condizioni diverse allo stesso campo:
# Age between 28 (inclusive) and 40 (exclusive)
query = {"$and": [{"age": {"$gte": 28}}, {"age": {"$lt": 40}}]}
for doc in col.find(query, {"_id": 0, "name": 1, "age": 1}):
print(doc)
# {'name': 'Alice', 'age': 28}
# {'name': 'Bob', 'age': 34}
# {'name': 'Eve', 'age': 34}$or — almeno una condizione deve essere soddisfatta:
# City is Berlin OR age is 22
for doc in col.find(
{"$or": [{"city": "Berlin"}, {"age": 22}]},
{"_id": 0, "name": 1}
):
print(doc)
# {'name': 'Carol'}
# {'name': 'David'}Corrispondenza di pattern con $regex
Usa $regex per confrontare campi stringa con un'espressione regolare:
# Names that start with the letter 'C' or 'E' (case-sensitive)
for doc in col.find({"name": {"$regex": "^[CE]"}}, {"_id": 0, "name": 1}):
print(doc)
# {'name': 'Carol'}
# {'name': 'Eve'}Per la corrispondenza senza distinzione tra maiuscole e minuscole, aggiungi $options: "i":
for doc in col.find(
{"city": {"$regex": "london", "$options": "i"}},
{"_id": 0, "name": 1, "city": 1}
):
print(doc)
# {'name': 'Alice', 'city': 'London'}
# {'name': 'Carol', 'city': 'London'}Ordinamento dei risultati
Concatena .sort() al cursore. Passa il nome del campo e una costante di direzione:
pymongo.ASCENDING(o1) — A → Z, dal più piccolo al più grandepymongo.DESCENDING(o-1) — Z → A, dal più grande al più piccolo
# Sort by age ascending
for doc in col.find({}, {"_id": 0, "name": 1, "age": 1}).sort("age", pymongo.ASCENDING):
print(doc)
# {'name': 'Carol', 'age': 22}
# {'name': 'Alice', 'age': 28}
# {'name': 'Bob', 'age': 34}
# {'name': 'Eve', 'age': 34}
# {'name': 'David', 'age': 40}Ordina per più campi passando una lista di tuple (campo, direzione):
# Sort by age descending, then by name ascending (tiebreak)
order = [("age", pymongo.DESCENDING), ("name", pymongo.ASCENDING)]
for doc in col.find({}, {"_id": 0, "name": 1, "age": 1}).sort(order):
print(doc)
# {'name': 'David', 'age': 40}
# {'name': 'Bob', 'age': 34}
# {'name': 'Eve', 'age': 34}
# {'name': 'Alice', 'age': 28}
# {'name': 'Carol', 'age': 22}Limitare i risultati
.limit(n) limita il numero di documenti restituiti. È utile per mostrare i primi N risultati.
# Top 3 youngest customers
for doc in col.find({}, {"_id": 0, "name": 1, "age": 1}).sort("age", 1).limit(3):
print(doc)
# {'name': 'Carol', 'age': 22}
# {'name': 'Alice', 'age': 28}
# {'name': 'Bob', 'age': 34}Saltare documenti (paginazione)
.skip(n) salta i primi n documenti. Combinato con .limit(), abilita la paginazione basata su pagine:
PAGE_SIZE = 2
def get_page(page_number):
"""Return one page of customers sorted by age (page_number is 0-indexed)."""
return list(
col.find({}, {"_id": 0, "name": 1, "age": 1})
.sort("age", pymongo.ASCENDING)
.skip(page_number * PAGE_SIZE)
.limit(PAGE_SIZE)
)
print(get_page(0)) # [{'name': 'Carol', 'age': 22}, {'name': 'Alice', 'age': 28}]
print(get_page(1)) # [{'name': 'Bob', 'age': 34}, {'name': 'Eve', 'age': 34}]
print(get_page(2)) # [{'name': 'David', 'age': 40}]Per collezioni di grandi dimensioni, preferisci la paginazione basata su cursore (filtrando per l'ultimo _id visto) rispetto a skip(), perché skip() deve scansionare e scartare documenti, il che diventa lento man mano che l'offset cresce.
Contare i documenti corrispondenti
Usa count_documents() con un filtro per contare le corrispondenze senza recuperare i documenti:
london_count = col.count_documents({"city": "London"})
print(london_count) # 2
total = col.count_documents({})
print(total) # 5Evita il vecchio metodo .count() sui cursori — è stato deprecato in PyMongo 3.7 e rimosso in PyMongo 4.
Verificare se un documento esiste
Quando hai solo bisogno di sapere se almeno un documento corrisponde, usa find_one() (meno costoso del conteggio):
exists = col.find_one({"city": "Berlin"}) is not None
print(exists) # TrueErrori comuni
Il cursore è esaurito dopo una sola iterazione. Se iteri lo stesso cursore due volte, il secondo ciclo non produce nulla. Chiama di nuovo find() oppure converti il risultato in una lista:
cursor = col.find({"city": "Paris"})
results = list(cursor) # materialise once
print(len(results)) # 2
# Now you can iterate `results` as many times as you likefind_one() vs find() — scegli quello giusto. Se sai che c'è al massimo una corrispondenza (ad esempio, una query su un campo univoco come l'email), usa find_one(). Usare find() ti obbliga a iterare anche quando hai bisogno di un solo risultato.
Filtro None vs dizionario vuoto. Sia find() che find({}) restituiscono tutti i documenti. Evita di passare None esplicitamente — usa {} per chiarezza.
Capitoli correlati
- MongoDB Insert — inserire documenti in una collezione prima di interrogarla
- MongoDB Query — approfondimento sulle espressioni di query e i pattern di filtraggio
- MongoDB Sort — trattazione dedicata all'ordinamento su più campi
- MongoDB Limit — limit e la sua interazione con gli indici
- MongoDB Update — modificare i documenti trovati
- MongoDB Delete — rimuovere i documenti corrispondenti