BSON: JSON Binario — El Formato Nativo de MongoDB
BSON (Binary JSON) es un formato de serialización de codificación binaria creado por MongoDB Inc. en 2009 como el formato de datos nativo para MongoDB. BSON extiende JSON con tipos de datos adicionales (datos binarios, ObjectId, Date, Decimal128, expresiones regulares, código JavaScript) y los codifica en un formato binario que prioriza la velocidad de recorrido y la actualización de campos en su lugar sobre la compacidad bruta.
Entender BSON es esencial para trabajar con MongoDB a un nivel más profundo que la API del driver — para diseño de esquemas, planificación de índices, optimización de pipelines de agregación y diagnóstico de problemas de límite de tamaño de documentos.
Objetivos de Diseño de BSON
BSON fue diseñado con dos objetivos primarios que a veces entran en conflicto con la compacidad:
-
Recorrido rápido — cada elemento va prefijado con su tipo y tamaño, por lo que el parser BSON puede saltar elementos sin decodificarlos, permitiendo búsquedas de campos por nombre en O(n) sin un árbol de análisis.
-
Actualización en su lugar — MongoDB puede actualizar documentos en su lugar, y BSON reserva espacio para elementos con prefijo de tamaño. Esto significa que algunos documentos BSON son en realidad más grandes que sus equivalentes JSON — un compromiso deliberado por rendimiento en escritura.
Sistema de Tipos de BSON
BSON define 20 tipos de datos más allá de los 6 de JSON:
| Tipo | ID Tipo BSON | Descripción |
|---|---|---|
| Double | 0x01 | Flotante IEEE 754 64 bits |
| String | 0x02 | String UTF-8 (con prefijo de longitud) |
| Document | 0x03 | Documento embebido |
| Array | 0x04 | Array (almacenado como documento, claves "0", "1", "2"...) |
| Binary | 0x05 | Datos binarios con subtipo |
| ObjectId | 0x07 | Identificador único de 12 bytes |
| Boolean | 0x08 | true/false |
| UTC datetime | 0x09 | int64 milisegundos desde época Unix |
| Null | 0x0A | null |
| Regex | 0x0B | Patrón + strings de opciones |
| Int32 | 0x10 | Entero con signo de 32 bits |
| Int64 | 0x12 | Entero con signo de 64 bits |
| Decimal128 | 0x13 | Flotante decimal IEEE 754 de 128 bits |
ObjectId: La Clave Primaria de MongoDB
El ObjectId es el tipo más distintivo de BSON — un identificador único globalmente de 12 bytes generado por el driver MongoDB sin coordinar con el servidor:
Estructura ObjectId (12 bytes):
[4 bytes timestamp Unix][3 bytes ID máquina][2 bytes ID proceso][3 bytes contador]
TTTTTTTT MMMMMM PPPP CCCCCC
Ejemplo: ObjectId("507f1f77bcf86cd799439011")
50 7F 1F 77 ← Timestamp Unix: 1350393207 (2012-10-16)
BC F8 6C ← ID de máquina
D7 99 ← ID de proceso
43 90 11 ← Contador
Propiedades de ObjectId:
- Monotónicamente creciente dentro de un segundo — los ObjectIds se ordenan cronológicamente
- Único globalmente con probabilidad de colisión astronómicamente baja
- Incrusta el tiempo de creación —
ObjectId.generation_timeda el timestamp de creación sin un campo separado - 12 bytes / 24 caracteres hex — más compacto que UUID (16 bytes / 36 chars con guiones)
from bson import ObjectId
from datetime import datetime
oid = ObjectId()
print(oid) # 65f4a2b3c1d8e7f902a3b4c5
print(oid.generation_time) # 2024-03-15 10:30:43+00:00
print(oid.binary) # b'\x65\xf4\xa2\xb3...' (12 bytes)
# Convertir string a ObjectId
oid2 = ObjectId("507f1f77bcf86cd799439011")
print(oid2.generation_time) # 2012-10-16
Python: pymongo y bson
from pymongo import MongoClient
from bson import ObjectId, Decimal128, Binary, Regex
import datetime
client = MongoClient("mongodb://localhost:27017/")
db = client["comercio"]
# Insertar un documento con tipos BSON ricos
pedido = {
"cliente_id": ObjectId("507f1f77bcf86cd799439011"),
"estado": "pendiente",
"importe": Decimal128("149.99"), # Decimal exacto (sin redondeo flotante)
"creado_en": datetime.datetime.utcnow(), # Almacenado como int64 ms
"articulos": [
{"sku": "WIDGET-A", "cant": 2, "precio": Decimal128("49.99")},
{"sku": "GADGET-B", "cant": 1, "precio": Decimal128("50.01")},
],
"etiquetas": ["electronica", "premium"],
"imagen": Binary(b'\x89PNG...', subtype=0x00), # Datos binarios
"patron_notas": Regex("urgente|prioridad", "i"), # Regex nativo
}
resultado = db.pedidos.insert_one(pedido)
print(f"Insertado: {resultado.inserted_id}")
# Consultar con ObjectId
doc = db.pedidos.find_one({"_id": resultado.inserted_id})
print(f"Importe: {doc['importe']}")
# Agregación
pipeline = [
{"$match": {"estado": "pendiente"}},
{"$group": {
"_id": None,
"total": {"$sum": "$importe"},
"cantidad": {"$sum": 1},
}},
]
for res in db.pedidos.aggregate(pipeline):
print(f"Pedidos pendientes: {res['cantidad']}, Total: {res['total']}")
# Serialización/deserialización BSON directa
import bson
documento = {"x": 1, "y": ObjectId(), "ts": datetime.datetime.utcnow()}
bytes_bson = bson.encode(documento)
print(f"Tamaño BSON: {len(bytes_bson)} bytes")
decodificado = bson.decode(bytes_bson)
Límites de Tamaño de Documentos
MongoDB impone un límite máximo de 16 MB por documento. Esta es una restricción a nivel BSON, impuesta por el servidor. Causas comunes de alcanzar este límite:
- Arrays que crecen sin límite (ej., añadir eventos a un documento en lugar de usar una colección de eventos separada)
- Incrustar blobs binarios grandes en documentos
- Estructuras profundamente anidadas con muchos nombres de campo duplicados
La especificación GridFS maneja archivos de más de 16 MB dividiéndolos en chunks de 255 KB.
BSON vs JSON vs MessagePack
| Característica | BSON | JSON | MessagePack |
|---|---|---|---|
| Tipo ObjectId | ✅ Nativo | ❌ Solo string | ❌ Tipo ext |
| Date/datetime | ✅ int64 ms | ❌ String | ✅ Tipo ext |
| Decimal128 | ✅ Nativo | ❌ Solo float | ❌ No estándar |
| Regex | ✅ Nativo | ❌ Solo string | ❌ No estándar |
| Datos binarios | ✅ Con subtipo | ❌ Base64 | ✅ Tipo bin |
| Tamaño vs JSON | A menudo mayor | Línea base | Generalmente menor |
| Actualización en su lugar | ✅ Diseñado para ello | ❌ No | ❌ No |
| Nativo de MongoDB | ✅ Sí | ❌ (modo JSON) | ❌ No |
Conclusión
BSON no es un formato de serialización de propósito general — es un formato operacional optimizado para los requisitos específicos de MongoDB: recorrido rápido a nivel de campo, actualizaciones en su lugar y soporte de tipos rico para casos de uso de base de datos (ObjectId para claves primarias, Decimal128 para datos financieros, Regex para coincidencia de patrones, Binary con subtipos para campos cifrados). Entender el sistema de tipos de BSON, la estructura de ObjectId y las implicaciones de tamaño es esencial para diseñar esquemas MongoDB eficientes y diagnosticar problemas de rendimiento en la capa de almacenamiento. Cuando trabajas con MongoDB, siempre estás trabajando con BSON, incluso cuando el driver presenta una API conveniente similar a JSON.
Conversiones relacionadas
Conversiones de documento que siguen este tema: