Este tutorial es la continuación de una edición anterior que fue el tutorial de MongoDB primeros pasos, un tutorial te muestro cómo instalar MongoDB utilizando Docker.
La finalidad de este tutorial “Pymongo para utilizar MongoDB desde Python” es crear unos datos de prueba e insertarlos en el MongoDB instalado en el tutorial anterior.
Para lograr todo esto nos vamos a servir de PyMongo, una librería de Python que permite crear conexiones a MongoDB.
¿Cómo instalar PyMongo?
Antes de instalar esta librería lo primero es tener todo organizado, usa este comando de consola para crear una carpeta donde tengas los scripts y el entorno virtual que vamos a utilizar en este tutorial en un solo sitio.
$ mkdir ~/pymongo_tutorial && cd ~/pymongo_tutorial && python3 -m venv pymongo_venv && source pymongo_venv/bin/activate
El comando anterior lo que hace es crear en tu directorio raíz la carpeta pymongo_tutorial
, entrar en esa carpeta, crear un entorno virtual llamado pymongo_venv
y activar dicho entorno virtual, ahora tu consola debe tener entre paréntesis (pymongo_venv)
indicando que el entorno virtual está activo.
Nota: para desactivar el entorno virtual solo debes ejecutar por consola el siguiente comando: deactivate
Instalando PyMongo en el entorno virtual
Para instalar PyMongo solo resta ejecutar el siguiente comando por consola:
python3 -m pip install pymongo
Y eso es todo, ya tienes disponible PyMongo y puedes comenzar a crear scripts que se conecten a mongo (ojo, siempre y cuando tengas este entorno virtual activo, o PyMongo instalado en el entorno virtual que estés utilizando).
Es momento entonces de que continúe la fiesta y comiences a meter documentos en tu MongoDB, para ello vamos a crear unos documentos sobre los que podamos hacer pruebas gracias a la magia de Python.
Creando e insertando datos de prueba
Para conectarnos a MongoDB usando PyMongo vamos a crear un pequeño script de python que haga el trabajo.
Lo primero será crear el archivo con el que vamos a estar trabajando, así que ejecuta el siguiente comando por consola:
touch mongo_writer.py
Una vez ejecutado puedes abrir el documento con tu editor de texto favorito, elige uno de los siguientes comando por consola:
code mongo_writer.py #(recomendado)
nano mongo_writer.py #(avanzado)
vim mongo_writer.py #(para frikis como yo)
El objetivo del script que vamos a escribir es crear 10 documentos de tipo JSON e insertarlos en el MongoDB que habíamos creado en el tutorial anterior sobre cómo instalar MongoDB.
Así que lo primero que haremos será conectarnos a MongoDB, para ello necesitamos importar un par de utilidades desde PyMongo.
import pymongo
from pymongo import MongoClient
Ahora sí, podemos crear la conexión a MongoDB:
client = MongoClient('mongodb://localhost:27018/')
db = client["db_pruebas"]
collection = db["coleccion_pruebas"]
El argumento de MongoClient es el MongoDB al cuál nos vamos a conectar, en este caso la dirección localhost:27018/ le dice que use nuestra máquina local en el puerto 27018.
Ahora con un ciclo for vamos a crear los 10 documentos de tipo JSON e iremos insertando uno por uno en nuestro MongoDB.
for i in range(10):
doc = {}
doc["titulo"] = f"Titulo de pruebas número {i}"
doc["numero"] = str(i)
doc["contenido"] = f"Este es el contenido del documento número {i}"
collection.insert_one(doc) # aquí se insertan los documentos en MongoDB
Me gustaría recordarte del tutorial anterior a este que ahí mencioné la “flexibilidad” de MongoDB como base de datos orientada al almacenamiento de documentos.
En este script nos estamos conectando a la base de datos db_pruebas
y a la colección que se llama coleccion_pruebas
pero ninguna de las dos existe actualmente en nuestro MongoDB.
Y es la flexibilidad de la que hablaba en el tutorial anterior quien entra en juego en esta ecuación, ya que, si MongoDB no encuentra la base de datos, o colección que le estás pidiendo, directamente la crea sin más.
Todo lo anterior junto en un solo script se ve de la siguiente manera:
import pymongo
from pymongo import MongoClient
client = MongoClient('mongodb://localhost:27018/')
db = client["db_pruebas"]
collection = db["coleccion_pruebas"]
for i in range(10):
doc = {}
doc["titulo"] = f"Titulo de pruebas número {i}"
doc["numero"] = str(i)
doc["contenido"] = f"Este es el contenido del documento número {i}"
collection.insert_one(doc)
Solo te queda guardar el script y ejecutar por consola:
python3 mongo_writer.py
Con eso ya tendrás 10 documentos de tipo JSON almacenados en tu MongoDB dentro de la colección llamada coleccion_pruebas
que se encuentra dentro de la base de datos db_pruebas
.
Algunas querys/consultas en MongoDB usando Pymongo
Continuando con el tutorial ahora vamos a ver un par de querys para que puedas buscar documentos en tu MongoDB.
Estas querys se deben adaptar a la morfología de tus documentos, que en otras palabras quiere decir que deben adecuarse a los campos que contengan, ya que, las que estaremos usando aquí en este tutorial se adaptan es a los documentos de pruebas que hemos creado en la sección anterior.
Primero vamos a abrir la consola de python con el entorno virtual activado y colocaremos los comandos de la conexión a Mongo.
NOTA: usaremos la terminal de python para ir más rápido con las explicaciones, pero todo esto lo puedes hacer tú usando un script.
>>> import pymongo
>>> from pymongo import MongoClient
>>> client = MongoClient('mongodb://localhost:27018/')
>>> db = client["db_pruebas"]
>>> collection = db["coleccion_pruebas"]
Ya con todo esto podemos comenzar a consultar nuestra coleccion_pruebas
y ver los documentos que tenemos ahí almacenados. para ello basta con hacer:
>>> docs = collection.find()
El comando find(query)
al tener una query
vacía nos devolverá todos los documentos de la colección, cuidado con este comando porque si son millones de documentos igual y terminas tumbando el MongoDB.
Veamos qué tipo de datos tiene ahora la variable docs:
>>> type(docs)
<class 'pymongo.cursor.Cursor'>
Como puedes apreciar es un “Cursor”. Este cursor lo que contiene es un suerte de “lista” con todos los documentos que fueron encontrados con la query, si no encontró ninguno devolverá NoneType
.
Vamos a ver qué métodos se pueden aplicar sobre este “Cursor”.
>>> dir(docs)
['_Cursor__address', '_Cursor__allow_disk_use',
'_Cursor__batch_size', '_Cursor__check_okay_to_chain',
'_Cursor__codec_options', '_Cursor__collation',
'_Cursor__collection', '_Cursor__collname',
'_Cursor__comment', '_Cursor__data', '_Cursor__dbname',
'_Cursor__die', '_Cursor__empty', '_Cursor__exhaust',
'_Cursor__explain', '_Cursor__explicit_session',
'_Cursor__has_filter', '_Cursor__hint', '_Cursor__id',
'_Cursor__killed', '_Cursor__let', '_Cursor__limit',
'_Cursor__max', '_Cursor__max_await_time_ms',
'_Cursor__max_scan', '_Cursor__max_time_ms', '_Cursor__min',
'_Cursor__ordering', '_Cursor__projection',
'_Cursor__query_flags', '_Cursor__query_spec',
'_Cursor__read_concern', '_Cursor__read_preference',
'_Cursor__retrieved', '_Cursor__return_key',
'_Cursor__send_message', '_Cursor__session',
'_Cursor__set_hint', '_Cursor__show_record_id',
'_Cursor__skip', '_Cursor__snapshot', '_Cursor__sock_mgr',
'_Cursor__spec', '__class__', '__class_getitem__',
'__copy__', '__deepcopy__', '__del__', '__delattr__',
'__dict__', '__dir__', '__doc__', '__enter__', '__eq__',
'__exit__', '__format__', '__ge__', '__getattribute__',
'__getitem__', '__gt__', '__hash__', '__init__',
'__init_subclass__', '__iter__', '__le__', '__lt__',
'__module__', '__ne__', '__new__', '__next__',
'__orig_bases__', '__parameters__', '__reduce__',
'__reduce_ex__', '__repr__', '__setattr__', '__sizeof__',
'__slots__', '__str__', '__subclasshook__', '__weakref__',
'_clone', '_clone_base', '_deepcopy', '_getmore_class',
'_is_protocol', '_query_class', '_read_preference',
'_refresh', '_unpack_response', 'add_option', 'address',
'alive', 'allow_disk_use', 'batch_size', 'clone', 'close',
'collation', 'collection', 'comment', 'cursor_id',
'distinct', 'explain', 'hint', 'limit', 'max',
'max_await_time_ms', 'max_scan', 'max_time_ms', 'min',
'next', 'remove_option', 'retrieved', 'rewind', 'session',
'skip', 'sort', 'where']
Como puedes apreciar todos tienen que ver con el manejo de documentos (orden, límite, etc) y los tiempos de conexión con MongoDB.
Como dije antes, el Cursor
es una suerte de “lista” sobre la que podemos iterar para ver nuestros documentos. Así que vamos a usar un ciclo for para ver qué fue lo que creamos con el script que insertaba documentos.
>>> for doc in docs:
... print(doc)
…
NOTA: el Cursor
es en realidad un iterador, es decir, una vez que haz iterado sobre él ya no existe y debes hacer la consulta de nuevo, así que mucho cuidado con la manipulación del Cursor, sobre todo si se obtiene de una consulta pesada.
En consola se te deben haber mostrado todos los documentos, aquí voy a mostrar uno solo por fines prácticos de la explicación:
{'_id': ObjectId('6342e0d5bddca892967fc006')
, 'titulo': 'Titulo de pruebas número 0'
, 'numero': '0'
, 'contenido': 'Este es el contenido del documento número 0'}
Usando los métodos que permite el Cursor de PyMongo podemos modificar la salida de la consulta, por ejemplo limitando a 3 el número de resultados:
>>> docs = collection.find().limit(3)
Ahora docs contiene 3 documentos en lugar de 10, para saber el número de documentos que tiene un Cursor de PyMongo basta con hacer:
>>> len(list(docs))
3
>>>
Vamos a probar ahora un par de querys para buscar documentos dentro de MongoDB usando PyMongo:
Supongamos que quiero el documento cuyo numero
es de valor 5, la query sería la siguiente:
query = {‘numero’:’5’}
Y para buscarlo vamos a usar find_one
en lugar de find
:
doc5 = collection.find_one(query)
Esto por consola se vería así:
>>> query = {'numero':'5'}
>>> doc5 = collection.find_one(query)
>>> doc5['numero']
'5'
>>>
Podemos buscar varios documentos a la vez utilizando find
y una query que devuelva varios resultados, por ejemplo:
Supongamos ahora que necesito los documentos cuyo campo numero
sea 1,7 o 9, la query sería la siguiente:
query = {‘numero’:{‘$in’:[‘1’,’7’,’9’]}}
Y para buscar los documentos usamos find
:
docs_1_7_9 = collection.find(query)
Por consola quedaría algo como:
>>> query = {'numero':{'$in':['1','7','9']}}
>>> docs_1_7_9 = collection.find(query)
>>> for i in docs_1_7_9:
... print(i['numero'])
...
1
7
9
>>>
Las querys pueden ser tan complejas como tú lo necesites, o cómo tus documentos lo permitan, ya queda a tu imaginación y/o necesidad, de momento sigamos avanzando con el tutorial.
Modificando documentos de MongoDB usando Pymongo
Como última sección de esta introducción a PyMongo te voy a mostrar cómo puedes modificar documentos dentro de una colección.
Lo primero es saber buscar el documento que queremos modificar y para eso en el apartado anterior te he mostrado cómo realizar querys.
Para ahorrar tiempo, vamos a usar la última query que usamos en el ejemplo anterior que nos daba como resultado un Cursor
con los documentos cuyo campo número eran 1, 7 o 9.
Supongamos que a estos documentos les queremos modificar el campo número, para este ejemplo en concreto vamos a agregar una decena más (sumaremos 10 a cada número).
Por consola quedaría algo como:
>>> query = {'numero':{'$in':['1','7','9']}}
>>> docs_1_7_9 = collection.find(query)
>>> for i in docs_1_7_9:
... new_num = int(i["numero"]) + 10
... collection.update_one({"_id":i["_id"]},{"$set":{"numero":new_num}},upsert=False)
...
<pymongo.results.UpdateResult object at 0x7f885867ef10>
<pymongo.results.UpdateResult object at 0x7f885867ef40>
<pymongo.results.UpdateResult object at 0x7f885867ef10>
>>>
Para verificar el cambio basta con volver a buscar los documentos cuyo número es 1, 7 o 9 para darnos cuenta que ya no existen (porque los hemos modificado).
>>> docs_1_7_9 = collection.find(query)
>>> len(list(docs_1_7_9))
0
>>>
Ahora son documentos con el campo numero
11, 17 o 19. Para verificar esto basta con modificar la query de la siguiente forma:
>>> query = {'numero':{'$in':[11,17,19]}}
>>> docs_11_17_19 = collection.find(query)
>>> len(list(docs_11_17_19))
3
>>>
Cómo te has podido dar cuenta las comillas de los números han desaparecido de la query, esto es porque cuando actualizamos el campo cambiamos el tipo de datos de string
a int
.
Esta misma forma de actualizar usando el update_one
junto con el {“$set”:{“campo”:”valor”}}
lo podemos utilizar para agregar nuevos campos a los documentos, por ejemplo, para nuestros recién modificados documentos podemos agregar el nuevo campo nuevo_campo
:
>>> docs_11_17_19 = collection.find(query)
>>> for i in docs_11_17_19:
... collection.update_one({"_id":i["_id"]},{"$set":{"nuevo_camp
o":"Hola Mundo"}},upsert=False)
...
<pymongo.results.UpdateResult object at 0x7f8858612ac0>
<pymongo.results.UpdateResult object at 0x7f8858612550>
<pymongo.results.UpdateResult object at 0x7f8858612c40>
>>> docs_11_17_19 = collection.find(query)
>>> for i in docs_11_17_19:
... print(i["nuevo_campo"])
...
Hola Mundo
Hola Mundo
Hola Mundo
>>>
Y con esto dejamos el tutorial hasta aquí, no me quiero despedir sin hacer énfasis en que declaramos la variable docs
que contiene el Cursor de PyMongo cada vez que la vamos a utilizar porque el Cursor es un iterador y desaparece cada vez que iteras sobre él.
Ahora sí, me despido esperando que leas el siguiente tutorial de esta saga de MongoDB donde te muestro cómo utilizar Studio 3T para visualizar los documentos con dicho IDE.