W3docs

MongoDB Find

Узнайте, как извлекать документы MongoDB с Python: find_one(), find(), проекции, операторы запросов, сортировка, skip и limit через PyMongo.

В этой главе объясняется, как извлекать документы из коллекции MongoDB с помощью драйвера pymongo для Python. Вы узнаете про методы find_one() и find(), как фильтровать результаты с помощью операторов запросов, как управлять возвращаемыми полями с помощью проекций, а также как сортировать, пропускать и ограничивать результаты.

Настройка

Убедитесь, что pymongo установлен перед запуском любого примера:

pip install pymongo

Все приведённые ниже примеры предполагают наличие работающего сервера MongoDB по адресу mongodb://localhost:27017/. Чтобы воспроизвести примеры на своём компьютере, запустите MongoDB командой mongod или воспользуйтесь бесплатным облачным кластером (MongoDB Atlas).

Подготовка тестовых данных

В примерах этой главы используется коллекция customers, содержащая пять документов. Выполните следующий код один раз, чтобы заполнить её:

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

Ожидаемый вывод:

Sample data ready.

PyMongo автоматически добавляет уникальное поле _id (типа bson.ObjectId) к каждому документу, у которого его ещё нет.

Получение одного документа с помощью find_one()

find_one() возвращает первый документ, соответствующий фильтру, или None, если совпадений нет. Это правильный выбор, когда вы ожидаете ровно один результат (например, при поиске пользователя по email).

# Retrieve the first document in the collection
doc = col.find_one()
print(doc)
# {'_id': ObjectId('...'), 'name': 'Alice', 'age': 28, 'city': 'London'}

Передайте фильтр для поиска конкретного документа:

# Find the customer named Bob
bob = col.find_one({"name": "Bob"})
print(bob)
# {'_id': ObjectId('...'), 'name': 'Bob', 'age': 34, 'city': 'Paris'}

Если ни один документ не совпадает, find_one() возвращает None, поэтому всегда проверяйте этот случай:

result = col.find_one({"name": "Zara"})
if result is None:
    print("No document found.")

Получение нескольких документов с помощью find()

find() возвращает курсор — ленивый итератор по всем совпадающим документам. Данные с сервера не запрашиваются до начала итерации.

Получить все документы

# Iterate every document in the collection
for doc in col.find():
    print(doc["name"], doc["age"])

Ожидаемый вывод (порядок может отличаться без явной сортировки):

Alice 28
Bob 34
Carol 22
David 40
Eve 34

Фильтрация по точному совпадению

Передайте словарь в качестве первого аргумента find():

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

Проекции — выбор возвращаемых полей

По умолчанию MongoDB возвращает все поля, включая _id. Проекция позволяет включать или исключать конкретные поля, что снижает нагрузку на сеть и потребление памяти.

Передайте проекцию в качестве второго позиционного аргумента (или именованного аргумента 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'}
# ...

Правила проекций:

  • Используйте 1 для включения поля, 0 — для исключения.
  • Нельзя смешивать включение и исключение в одной проекции, кроме поля _id (которое всегда можно явно установить в 0).

Операторы запросов

MongoDB предоставляет широкий набор операторов для фильтрации документов. Передавайте их внутри словаря фильтра.

Операторы сравнения

ОператорЗначениеПример
$eqРавно (по умолчанию){"age": {"$eq": 34}}
$neНе равно{"city": {"$ne": "Paris"}}
$gtБольше{"age": {"$gt": 30}}
$gteБольше или равно{"age": {"$gte": 34}}
$ltМеньше{"age": {"$lt": 30}}
$lteМеньше или равно{"age": {"$lte": 28}}
$inЗначение в списке{"city": {"$in": ["London", "Berlin"]}}
$ninЗначение не в списке{"city": {"$nin": ["Paris"]}}

Пример — клиенты старше 30 лет:

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}

Пример — клиенты из Лондона или Берлина:

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'}

Логические операторы

Неявный AND — указание нескольких ключей в одном словаре фильтра означает, что все условия должны выполняться:

# 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 необходим, когда нужно применить два разных условия к одному и тому же полю:

# 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 — должно выполняться хотя бы одно условие:

# 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'}

Сопоставление по шаблону с $regex

Используйте $regex для сопоставления строковых полей с регулярным выражением:

# 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'}

Для поиска без учёта регистра добавьте $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'}

Сортировка результатов

Используйте .sort() на курсоре. Передайте имя поля и константу направления:

  • pymongo.ASCENDING (или 1) — от A до Z, от меньшего к большему
  • pymongo.DESCENDING (или -1) — от Z до A, от большего к меньшему
# 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}

Для сортировки по нескольким полям передайте список кортежей (поле, направление):

# 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}

Ограничение результатов

.limit(n) ограничивает количество возвращаемых документов. Это удобно для отображения топ-N результатов.

# 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}

Пропуск документов (пагинация)

.skip(n) пропускает первые n документов. В сочетании с .limit() это обеспечивает постраничную пагинацию:

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}]

Для больших коллекций предпочтительнее использовать курсорную пагинацию (фильтрацию по последнему увиденному _id) вместо skip(), поскольку skip() вынужден сканировать и пропускать документы, что замедляется по мере роста смещения.

Подсчёт совпадающих документов

Используйте count_documents() с фильтром для подсчёта совпадений без загрузки документов:

london_count = col.count_documents({"city": "London"})
print(london_count)  # 2

total = col.count_documents({})
print(total)  # 5

Избегайте устаревшего метода .count() на курсорах — он был помечен устаревшим в PyMongo 3.7 и удалён в PyMongo 4.

Проверка существования документа

Если нужно лишь убедиться, что хотя бы один документ соответствует условию, используйте find_one() (это дешевле, чем подсчёт):

exists = col.find_one({"city": "Berlin"}) is not None
print(exists)  # True

Типичные ошибки

Курсор исчерпывается после одной итерации. Если вы дважды итерируете один и тот же курсор, второй цикл не вернёт ничего. Вызовите find() снова или преобразуйте курсор в список:

cursor = col.find({"city": "Paris"})
results = list(cursor)   # materialise once
print(len(results))      # 2
# Now you can iterate `results` as many times as you like

find_one() vs find() — выбирайте правильный метод. Если вы знаете, что совпадение может быть не более одного (например, запрос по уникальному полю, такому как email), используйте find_one(). Использование find() заставляет вас итерировать курсор, даже когда нужен лишь один результат.

Фильтр None vs пустой словарь. Как find(), так и find({}) возвращают все документы. Избегайте явной передачи None — для ясности используйте {}.

Связанные главы

  • MongoDB Insert — вставка документов в коллекцию перед их запросом
  • MongoDB Query — более глубокое рассмотрение выражений запросов и шаблонов фильтрации
  • MongoDB Sort — подробное описание многополевой сортировки
  • MongoDB Limit — ограничение результатов и его взаимодействие с индексами
  • MongoDB Update — изменение найденных документов
  • MongoDB Delete — удаление совпадающих документов
Was this page helpful?