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.