Criação de Índices no MongoDB com pymongo, flask_pymongo e mongoengine

A definição de índices em coleções do MongoDB é essencial para otimizar consultas e garantir unicidade. Este artigo aborda três formas de criar índices em uma aplicação Flask: via script com pymongo, integração com flask_pymongo e através do mongoengine.

1. Via script pymongo (conexão direta)

Usando a biblioteca pymongo (versão 3.7) para conectar ao MongoDB local e criar um índice único no campo image_id da coleção image no banco annosys.

from pymongo import MongoClient, ASCENDING

conn = MongoClient('mongodb://localhost:27017/')
db = conn.annosys
img_collection = db['image']
img_collection.create_index([('image_id', ASCENDING)], unique=True)
indexes = sorted(list(img_collection.index_information()))
print(indexes)

Caso o código esteja em um contêiner Docker (web) e o MongoDB em outro (mongodb), é necessário usar o nome do serviço no endereço. Exemplo com docker-compose:

version: '3'
services:
  web:
    build: ./web
    container_name: "web_app"
    ports:
      - "8080:80"
    depends_on:
      - mongodb
    networks:
      - mynet

  mongodb:
    image: mongo:6
    container_name: "mongo_db"
    environment:
      MONGO_INITDB_DATABASE: annosys
    volumes:
      - ./data/db:/data/db
    networks:
      - mynet

networks:
  mynet:
    driver: bridge

Nese caso, a string de conexão deve ser "mongodb://mongodb:27017/".

2. Via extensão flask_pymongo

Configuração no Flask usando a extensão flask_pymongo:

# models_second.py
from flask_pymongo import PyMongo
mongo = PyMongo()

# app.py
app = Flask(__name__)
app.config["MONGO_URI"] = "mongodb://localhost:27017/annosys"
from models_second import mongo
mongo.init_app(app)

# views.py
from models_second import mongo

# Exemplo de consulta e remoção em lote
annotations = mongo.db.image_annotation.find({"task_id": id})
ids = [a['_id'] for a in annotations]
mongo.db.image_annotation.remove({'_id': {'$in': ids}})

3. Via mongoengine (definição nos modelos)

Com mongoengine, os índices são declarados na classe do documento através do atributo meta. Exemplo simplificado:

from flask_mongoengine import MongoEngine
db = MongoEngine()

class User(db.Document):
    username = db.StringField(required=True, max_length=100)
    password = db.StringField(required=True, max_length=100)
    created_at = db.DateTimeField(default=datetime.utcnow)
    
    meta = {
        'indexes': [
            {'fields': ['username'], 'unique': True}
        ]
    }

class Task(db.Document):
    task_title = db.StringField(required=True, max_length=200)
    task_type = db.StringField(required=True, max_length=50)
    
    meta = {
        'indexes': [
            {'fields': ['task_type', 'task_title'], 'unique': True}
        ]
    }

Os índices são automaticamente criados quando o modelo é sincronizado com o MongoDB.

4. Combinação híbrida: mongoengine + pymongo

Se o mongoengine não der conta de operações em lote, é possível usar pymongo sem abrir uma nova conexão, reutilizando a conexão do mongoengine com get_db(). Exemplo de inserção em massa:

from mongoengine.connection import get_db, connect
from pymongo import UpdateOne, InsertOne
from pymongo.errors import BulkWriteError

connect()  # conecta com as configurações padrão

# ... dentro de um loop de leitura de arquivo
batch_size = 1000
image_batch = []
annotation_batch = []

for i, line in enumerate(lines):
    record = json.loads(line)
    image_batch.append(UpdateOne(
        {'image_id': record['id']},
        {'$set': {'url': record['url'], 'width': record['width'], 'height': record['height']},
         '$addToSet': {'datasets': dataset_id}},
        upsert=True
    ))
    if (i + 1) % batch_size == 0:
        get_db()['image'].bulk_write(image_batch)
        image_batch = []

if image_batch:
    get_db()['image'].bulk_write(image_batch)

Para cenários com Docker, a configuração de conexão pode usar connect=False para lazy connection:

app.config['MONGODB_SETTINGS'] = {
    'host': 'mongodb',
    'port': 27017,
    'db': 'annosys',
    'connect': False
}
client = MongoClient('mongodb://mongodb:27017/')

É importante criar os índices com pymongo antes das inserções em massa, pois o mongoengine com connect=False não os cria automaticamente.

Observações finais

  • Se o projeto usa exclusivamente mongoengine, defina todos os índices nos modelos.
  • Para operações avançadas que exigem pymongo, mas a conexão é gerenciada pelo mongoengine, use get_db().
  • Em ambientes com múltiplos contêineres, ajuste as strings de conexão para usar os nomes dos serviços.

Tags: mongodb pymongo flask_pymongo mongoengine índices

Publicado em 6-29 01:15