Интеграция MongoDB с Python с использованием PyMongo
В этом посте мы погрузимся в MongoDB как хранилище данных с точки зрения Python. Для этого мы напишем простой сценарий, демонстрирующий, чего мы можем достичь, и любые преимущества, которые мы можем извлечь из этого.
Веб-приложения, как и многие другие программные приложения, питаются от данных. Организация и хранение этих данных важны, поскольку они определяют, как мы взаимодействуем с различными приложениями, которые находятся в нашем распоряжении. Вид обрабатываемых данных также может влиять на то, как мы предпринимаем этот процесс.
Базы данных позволяют нам организовывать и хранить эти данные, а также контролировать, как мы храним, получаем доступ и защищаем информацию.
Базы данных NoSQL
Существует два основных типа баз данных - реляционные и нереляционные базы данных.
Реляционные базы данных позволяют нам хранить, получать доступ и манипулировать данными по отношению к другому фрагменту данных в базе данных. Данные хранятся в организованных таблицах со строками и столбцами со связями, связывающими информацию между таблицами. Для работы с этими базами данных мы используем язык структурированных запросов (SQL), например MySQL и PostgreSQL.
Нереляционные базы данных хранят данные ни в реляционной, ни в табличной форме, как в реляционных базах данных. Они также называются базами данных NoSQL, так как мы не используем SQL для взаимодействия с ними.
MongoDB и когда его использовать
MongoDB - это хранилище документов и нереляционная база данных. Это позволяет нам хранить данные в коллекциях, которые составлены из документов.
В MongoDB документ представляет собой просто JSON-подобный двоичный формат сериализации, называемый BSON или Binary-JSON, и имеет максимальный размер 16 мегабайт. Это ограничение размера используется для обеспечения эффективного использования памяти и полосы пропускания во время передачи.
MongoDB также предоставляет спецификацию GridFS в случае необходимости хранить файлы, размер которых превышает установленное ограничение.
Документы состоят из пар ключ-значение, как в обычных данных JSON. Однако этот формат BSON также может содержать больше типов данных, таких как Date
и Binary Data
. BSON был разработан, чтобы быть легким, легко проходимым и эффективным при кодировании и декодировании данных в и из BSON.
Будучи хранилищем данных NoSQL, MongoDB позволяет нам пользоваться преимуществами использования нереляционной базы данных по сравнению с реляционной. Одним из преимуществ является то, что он обеспечивает высокую масштабируемость за счет эффективного масштабирования по горизонтали за счет разделения данных и размещения их на нескольких компьютерах.
MongoDB также позволяет нам хранить большие объемы структурированных, полуструктурированных и неструктурированных данных без необходимости поддерживать отношения между ними.
Как и у любого другого решения, у MongoDB есть свои недостатки. Первый заключается в том, что он не поддерживает отношения между сохраненными данными. Из-за этого трудно выполнять транзакции ACID, которые обеспечивают согласованность.
Сложность увеличивается при попытке поддержки транзакций ACID. MongoDB, как и другие хранилища данных NoSQL, не настолько развит, как реляционные базы данных, и это может затруднить поиск экспертов.
Нереляционная природа MongoDB делает его идеальным для хранения данных в определенных ситуациях по сравнению с его реляционными аналогами. Например, сценарий, где MongoDB является более подходящим, чем реляционная база данных, - это когда формат данных является гибким и не имеет отношений.
С гибкими / нереляционными данными нам не нужно поддерживать свойства ACID при хранении данных, в отличие от реляционных баз данных. MongoDB также позволяет нам легко масштабировать данные в новые узлы.
Однако, несмотря на все свои преимущества, MongoDB не идеален, когда наши данные носят реляционный характер. Например, если мы храним записи клиентов и их заказы.
В этой ситуации нам понадобится реляционная база данных для поддержания отношений между нашими данными, которые важны. Также не подходит использовать MongoDB, если нам нужно соблюдать свойства ACID.
Взаимодействие с MongoDB через Mongo Shell
Для работы с MongoDB нам потребуется установить MongoDB Server, который можно загрузить с официальной домашней страницы. Для этой демонстрации мы будем использовать бесплатный сервер совместной работы.
MongoDB Server поставляется с оболочкой Mongo, которую мы можем использовать для взаимодействия с сервером через терминал.
Чтобы активировать оболочку, просто введите в терминале mongo
. Вы получите информацию о настройке сервера MongoDB, включая версию MongoDB и Mongo Shell, а также URL-адрес сервера.
Например, наш сервер работает на:
mongodb://127.0.0.1:27017
В MongoDB база данных используется для хранения коллекций, содержащих документы. Через оболочку Mongo мы можем создать новую базу данных или переключиться на существующую, используя команду use
:
> use SeriesDB
Каждая операция, которую мы выполняем после этого, будет выполняться в нашей базе данных SeriesDB
. В базе данных мы будем хранить коллекции, которые похожи на таблицы в реляционных базах данных.
Например, для целей данного руководства давайте добавим несколько рядов в базу данных:
> db.series.insertMany([
... { name: "Game of Thrones", year: 2012},
... { name: "House of Cards", year: 2013 },
... { name: "Suits", year: 2011}
... ])
И у нас получится:
{
"acknowledged" : true,
"insertedIds" : [
ObjectId("5e300724c013a3b1a742c3b9"),
ObjectId("5e300724c013a3b1a742c3ba"),
ObjectId("5e300724c013a3b1a742c3bb")
]
}
Для извлечения всех документов, хранящихся в нашей коллекции series
, мы используем db.inventory.find({})
, эквивалент SQL SELECT * FROM series
. Передача пустого запроса (т.е. {}
) вернет все документы:
> db.series.find({})
{ "_id" : ObjectId("5e3006258c33209a674d1d1e"), "name" : "The Blacklist", "year" : 2013 }
{ "_id" : ObjectId("5e300724c013a3b1a742c3b9"), "name" : "Game of Thrones", "year" : 2012 }
{ "_id" : ObjectId("5e300724c013a3b1a742c3ba"), "name" : "House of Cards", "year" : 2013 }
{ "_id" : ObjectId("5e300724c013a3b1a742c3bb"), "name" : "Suits", "year" : 2011 }
Мы также можем запросить данные, используя условие равенства, например, чтобы вернуть все сериалы, которые были показаны в 2013 году:
> db.series.find({ year: 2013 })
{ "_id" : ObjectId("5e3006258c33209a674d1d1e"), "name" : "The Blacklist", "year" : 2013 }
{ "_id" : ObjectId("5e300724c013a3b1a742c3ba"), "name" : "House of Cards", "year" : 2013 }
SQL-эквивалент будет SELECT * FROM series WHERE year=2013
.
MongoDB также позволяет нам обновлять отдельные документы с помощью db.collection.UpdateOne()
или выполнять пакетное обновление с помощью db.collection.UpdateMany()
. Например, чтобы обновить год выпуска для Suits
:
> db.series.updateOne({ name: "Suits" }, {
$set: { year: 2010 }
})
{ "acknowledged" : true, "matchedCount" : 1, "modifiedCount" : 1 }
Наконец, для удаления документов, Mongo Shell предлагает функции db.collection.deleteOne()
и db.collection.deleteMany()
.
Например, чтобы удалить все премьеры 2012
, мы запустили:
> db.series.deleteMany({ year: 2012 })
{ "acknowledged" : true, "deletedCount" : 2 }
Дополнительную информацию об операциях CRUD над MongoDB можно найти в онлайн-справке, включая дополнительные примеры, выполнение операций с условиями, атомарность и сопоставление понятий SQL с понятиями и терминологией MongoDB.
Интеграция Python с MongoDB
MongoDB предоставляет драйверы и инструменты для взаимодействия с хранилищем данных MongoDB с использованием различных языков программирования, включая Python, JavaScript, Java, Go и C#.
PyMongo является официальным драйвером MongoDB для Python, и мы будем использовать его для создания простого скрипта, который мы будем использовать для манипулирования данными, хранящимися в нашей базе данных SeriesDB
.
С установленными Python 3.6 + и Virtualenv, давайте создадим виртуальную среду для приложения и установить PyMongo с помощью pip:
virtualenv --python=python3 env --no-site-packages
source env/bin/activate
pip install pymongo
Используя PyMongo
, мы собираемся написать простой скрипт, который мы можем выполнить для выполнения различных операций с нашей базой данных MongoDB.
Подключение к MongoDB
Сначала мы импортируем pymongo
в mongo_db_script.py
и создаем клиент, подключенный к нашему локально работающему экземпляру MongoDB:
import pymongo
# Create the client
client = MongoClient('localhost', 27017)
# Connect to our database
db = client['SeriesDB']
# Fetch our series collection
series_collection = db['series']
Мы создали клиент, который подключается к нашему серверу MongoDB, и использовали его для получения нашей базы данных 'SeriesDB'. Затем мы получаем нашу коллекцию 'series' и сохраняем ее в переменной.
Создание документов
Чтобы сделать наш сценарий более удобным, мы напишем функции, которые позволяют нам легко манипулировать данными с PyMongo
. Мы будем использовать Python словари для представления документов и передадим эти словари нашим функциям. Во-первых, давайте создадим функцию для вставки данных в нашу коллекцию 'series':
# Imports truncated for brevity
def insert_document(collection, data):
""" Function to insert a document into a collection and
return the document's id.
"""
return collection.insert_one(data).inserted_id
Эта функция получает коллекцию и словарь данных и вставляет данные в предоставленную коллекцию. Затем функция возвращает идентификатор, который мы можем использовать для точного запроса отдельного объекта из базы данных.
Следует также отметить, что MongoDB добавляет дополнительный ключ _id
к нашим документам, когда они не предоставляются, при создании данных.
Теперь давайте попробуем добавить шоу, используя нашу функцию:
new_show = {
"name": "FRIENDS",
"year": 1994
}
print(insert_document(series_collection, new_show))
В консоли получим:
5e4465cfdcbbdc68a6df233f
Когда мы запускаем наш скрипт, _id
новой записи распечатывается в терминале, и мы можем использовать этот идентификатор для получения записи позже.
Мы можем предоставить значение _id
вместо того, чтобы назначать его автоматически, что мы предоставили бы в словаре:
new_show = {
"_id": "1",
"name": "FRIENDS",
"year": 1994
}
И если бы мы попытались сохранить документ с существующим _id
, нас встретит ошибка, подобная следующей:
DuplicateKeyError: E11000 duplicate key error index: SeriesDB.series.$id dup key: { : 1}
Получение документов
Для извлечения документов из базы данных мы будем использовать find_document()
, которая запрашивает нашу коллекцию для одного или нескольких документов. Наша функция получит словарь, который содержит элементы, по которым мы хотим фильтровать, и необязательный аргумент, чтобы указать, хотим ли мы один документ или несколько документов:
# Imports and previous code truncated for brevity
def find_document(collection, elements, multiple=False):
""" Function to retrieve single or multiple documents from a provided
Collection using a dictionary containing a document's elements.
"""
if multiple:
results = collection.find(elements)
return [r for r in results]
else:
return collection.find_one(elements)
А теперь давайте использовать эту функцию, чтобы найти некоторые документы:
result = find_document(series_collection, {'name': 'FRIENDS'})
print(result)
При выполнении нашей функции мы не предоставили параметр multiple
, и в результате мы получили один документ:
{'_id': ObjectId('5e3031440597a8b07d2f4111'), 'name': 'FRIENDS', 'year': 1994}
Когда параметр multiple
предоставлен, результатом является список всех документов в нашей коллекции, для которых установлен атрибут name
как FRIENDS
.
Обновление документов
Наша следующая функция update_document()
, будет использоваться для обновления одного конкретного документа. Мы будем использовать _id
документа и коллекцию, к которой он принадлежит, при его поиске:
# Imports and previous code truncated for brevity
def update_document(collection, query_elements, new_values):
""" Function to update a single document in a collection.
"""
collection.update_one(query_elements, {'$set': new_values})
Теперь давайте вставим документ:
new_show = {
"name": "FRIENDS",
"year": 1995
}
id_ = insert_document(series_collection, new_show)
Сделав это, давайте обновим документ, используя возвращенный _id
:
update_document(series_collection, {'_id': id_}, {'name': 'F.R.I.E.N.D.S'})
И, наконец, давайте посмотрим его, чтобы убедиться, что новое значение было установлено, и напечатаем результат:
result = find_document(series_collection, {'_id': id_})
print(result)
Когда мы выполняем наш скрипт, мы увидим, что наш документ обновлен:
{'_id': ObjectId('5e30378e96729abc101e3997'), 'name': 'F.R.I.E.N.D.S', 'year': 1995}
Удаление документов
И, наконец, давайте напишем функцию для удаления документов:
# Imports and previous code truncated for brevity
def delete_document(collection, query):
""" Function to delete a single document from a collection.
"""
collection.delete_one(query)
Поскольку мы используем метод delete_one
, только один документ может быть удален за вызов, даже если запрос соответствует нескольким документам.
Теперь давайте используем функцию для удаления записи:
delete_document(series_collection, {'_id': id_})
Если мы попытаемся получить тот же документ:
result = find_document(series_collection, {'_id': id_})
print(result)
Результатом будет:
None
Следующие шаги
Мы выделили и использовали несколько методов PyMongo
взаимодействия с нашим сервером MongoDB из скрипта Python. Тем не менее, мы не использовали все методы, доступные для нас через модуль.
Все доступные методы можно найти в официальной документации PyMongo и классифицировать в соответствии с подмодулями.
Мы написали простой скрипт, который выполняет элементарные функции CRUD для базы данных MongoDB. Хотя мы могли бы импортировать функции в более сложной кодовой базе или, например, в приложение Flask / Django, в этих средах есть библиотеки для достижения тех же результатов. Эти библиотеки делают его проще, удобнее и помогают нам более безопасно подключаться к MongoDB.