MongoDB Find
Aprende a recuperar documentos de MongoDB con Python usando find_one(), find(), proyecciones, operadores de consulta, sort, skip y limit con PyMongo.
Este capítulo explica cómo recuperar documentos de una colección MongoDB usando el driver pymongo de Python. Aprenderás los métodos find_one() y find(), cómo filtrar resultados con operadores de consulta, cómo controlar qué campos se devuelven mediante proyecciones y cómo ordenar, omitir y limitar resultados.
Configuración inicial
Asegúrate de que pymongo esté instalado antes de ejecutar cualquier ejemplo:
pip install pymongoTodos los ejemplos a continuación asumen un servidor MongoDB activo en mongodb://localhost:27017/. Para seguirlos en tu propia máquina, inicia MongoDB con mongod o usa un clúster gratuito en la nube (MongoDB Atlas).
Preparación de datos de muestra
Los ejemplos de este capítulo utilizan una colección customers que contiene estos cinco documentos. Ejecuta esto una vez para poblarla:
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.")Salida esperada:
Sample data ready.PyMongo añade automáticamente un campo _id único (un bson.ObjectId) a cada documento que no tenga uno previamente.
Recuperar un único documento con find_one()
find_one() devuelve el primer documento que coincide con el filtro, o None si ninguno coincide. Es la opción correcta cuando esperas exactamente un resultado (por ejemplo, al buscar un usuario por correo electrónico).
# Retrieve the first document in the collection
doc = col.find_one()
print(doc)
# {'_id': ObjectId('...'), 'name': 'Alice', 'age': 28, 'city': 'London'}Pasa un filtro para encontrar un documento específico:
# Find the customer named Bob
bob = col.find_one({"name": "Bob"})
print(bob)
# {'_id': ObjectId('...'), 'name': 'Bob', 'age': 34, 'city': 'Paris'}Si ningún documento coincide, find_one() devuelve None, por lo que siempre debes protegerte contra esto:
result = col.find_one({"name": "Zara"})
if result is None:
print("No document found.")Recuperar múltiples documentos con find()
find() devuelve un cursor — un iterador perezoso sobre todos los documentos que coinciden. Nada se obtiene del servidor hasta que iteras.
Recuperar todos los documentos
# Iterate every document in the collection
for doc in col.find():
print(doc["name"], doc["age"])Salida esperada (el orden puede variar sin una ordenación explícita):
Alice 28
Bob 34
Carol 22
David 40
Eve 34Filtrar con coincidencia exacta
Pasa un diccionario como primer argumento a find():
# All customers in London
for doc in col.find({"city": "London"}):
print(doc["name"])
# Alice
# CarolProyecciones — elegir qué campos devolver
Por defecto, MongoDB devuelve todos los campos, incluido _id. Una proyección te permite incluir o excluir campos específicos, lo que reduce el tráfico de red y el uso de memoria.
Pasa la proyección como segundo argumento posicional (o argumento de palabra clave 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'}
# ...Reglas para las proyecciones:
- Usa
1para incluir un campo,0para excluirlo. - No puedes mezclar inclusión y exclusión en la misma proyección, excepto para
_id(que siempre puede establecerse explícitamente en0).
Operadores de consulta
MongoDB proporciona un amplio conjunto de operadores para filtrar documentos. Pásalos dentro del diccionario de filtro.
Operadores de comparación
| Operador | Significado | Ejemplo |
|---|---|---|
$eq | Igual (por defecto) | {"age": {"$eq": 34}} |
$ne | Distinto | {"city": {"$ne": "Paris"}} |
$gt | Mayor que | {"age": {"$gt": 30}} |
$gte | Mayor o igual que | {"age": {"$gte": 34}} |
$lt | Menor que | {"age": {"$lt": 30}} |
$lte | Menor o igual que | {"age": {"$lte": 28}} |
$in | Valor en lista | {"city": {"$in": ["London", "Berlin"]}} |
$nin | Valor no en lista | {"city": {"$nin": ["Paris"]}} |
Ejemplo — clientes mayores de 30 años:
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}Ejemplo — clientes en Londres o Berlín:
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'}Operadores lógicos
AND implícito — proporcionar múltiples claves en un único diccionario de filtro significa que todas las condiciones deben coincidir:
# 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 es necesario cuando debes aplicar dos condiciones diferentes al mismo 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 — al menos una condición debe coincidir:
# 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'}Coincidencia de patrones con $regex
Usa $regex para hacer coincidir campos de texto con una expresión regular:
# 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'}Para coincidencia sin distinguir mayúsculas, añade $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'}Ordenar resultados
Encadena .sort() en el cursor. Pasa el nombre del campo y una constante de dirección:
pymongo.ASCENDING(o1) — A → Z, de menor a mayorpymongo.DESCENDING(o-1) — Z → A, de mayor a menor
# 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}Ordena por múltiples campos pasando una lista de tuplas (campo, dirección):
# 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}Limitar resultados
.limit(n) limita el número de documentos devueltos. Es útil para mostrar los N mejores resultados.
# 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}Omitir documentos (paginación)
.skip(n) omite los primeros n documentos. Combinado con .limit(), permite la paginación basada en páginas:
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}]Para colecciones grandes, prefiere la paginación basada en cursor (filtrando por el último _id visto) en lugar de skip(), porque skip() debe escanear y descartar documentos, lo que se vuelve lento a medida que crece el desplazamiento.
Contar documentos coincidentes
Usa count_documents() con un filtro para contar coincidencias sin obtener los documentos:
london_count = col.count_documents({"city": "London"})
print(london_count) # 2
total = col.count_documents({})
print(total) # 5Evita el método .count() más antiguo en los cursores — fue deprecado en PyMongo 3.7 y eliminado en PyMongo 4.
Comprobar si existe un documento
Cuando solo necesitas saber si al menos un documento coincide, usa find_one() (más eficiente que contar):
exists = col.find_one({"city": "Berlin"}) is not None
print(exists) # TrueErrores comunes
El cursor se agota después de una iteración. Si iteras el mismo cursor dos veces, el segundo bucle no produce nada. Llama a find() de nuevo o convierte a 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() — elige el correcto. Si sabes que hay como máximo una coincidencia (por ejemplo, al consultar por un campo único como el correo electrónico), usa find_one(). Usar find() te obliga a iterar incluso cuando solo necesitas un resultado.
Filtro None vs diccionario vacío. Tanto find() como find({}) devuelven todos los documentos. Evita pasar None explícitamente — usa {} para mayor claridad.
Capítulos relacionados
- MongoDB Insert — insertar documentos en una colección antes de consultarlos
- MongoDB Query — cobertura más profunda de expresiones de consulta y patrones de filtrado
- MongoDB Sort — cobertura dedicada a la ordenación por múltiples campos
- MongoDB Limit — limit y su interacción con los índices
- MongoDB Update — modificar documentos que hayas encontrado
- MongoDB Delete — eliminar documentos que coincidan