Pymongo para utilizar MongoDB desde Python

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.

Soy Snell Rojas | Consultor SEO

Soy un experimentado Consultor SEO con 5 años de experiencia trabajando con equipos de marketing y desarrollando estrategias SEO para más de 100 sitios web. Mis principales habilidades son diseñar e implementar cambios en la estrategia de SEO que pueden mejorar los objetivos deseados, mejorar aspectos técnicos de SEO (ya que tengo 3 años de experiencia como desarrollador back-end), también me siento muy bien trabajando de la mano con mis clientes para optimizar sus páginas web.