MongoDB es una base de datos NoSQL popular que almcaena datos en un formato similar a JSON llamado BSON (Binary JSON). A diferencia de las bases de datos relacionales, MongoDB utiliza el concepto de colecciones (equivalente a tablas en SQL) y documentos (equivalente a filas en SQL).
Instalación y Configuración de MongoDB
Para instalar MongoDB en sistemas basados en RHEL/CentOS, primero configure el repositorio adecuado:
vim /etc/yum.repos.d/mongodb-org-4.2.repo
[mongodb-org-4.2]
name=MongoDB Repository
baseurl=https://repo.mongodb.org/yum/redhat/$releasever/mongodb-org/4.2/x86_64/
gpgcheck=1
enabled=1
gpgkey=https://www.mongodb.org/static/pgp/server-4.2.asc
Luego, instale el paquete y inicie el servicio:
sudo yum install -y mongodb-org
systemctl start mongod
Comandos Básicos de MongoDB
use <database_name>;: Cambiar a una base de datos (la crea si no existe).db.createCollection("<collection_name>");: Crear una colección.db.createUser({...});: Crear un usuario con roles.mongo --port <port> -u "<username>" -p "<password>" --authenticationDatabase "<auth_db>": Iniciar sesión en MongoDB.db.<collection_name>.createIndex({field: 1});: Crear un índice en un campo.db.<collection_name>.insertOne({...});: Insertar un solo documento.db.<collection_name>.find({...});: Buscar documentos.db.<collection_name>.update({...}, {...});: Actualizar un documento.db.<collection_name>.deleteOne({...});: Eliminar un solo documento.
Integración con Go
Instalación del Driver de Go para MongoDB
go get go.mongodb.org/mongo-driver/mongo
Conexión a la Base de Datos
La URI de conexión sigue el formato: mongodb://[username:password@]host1[:port1][,...hostN[:portN]][/[defaultauthdb][?options]].
El siguiente código demuestra cómo establecer una conexión segura con un tiempo de espera:
package main
import (
"context"
"fmt"
"log"
"time"
"go.mongodb.org/mongo-driver/mongo"
"go.mongodb.org/mongo-driver/mongo/options"
)
// User representa la estructura de un usuario en la base de datos.
type User struct {
ID uint `bson:"_id,omitempty"` // ID único del usuario, omitido si está vacío al insertar
Name string `bson:"name"` // Nombre del usuario
Age int `bson:"age"` // Edad del usuario
Gender int8 `bson:"gender"` // Género del usuario (ej: 1 para masculino, 2 para femenino)
BirthDate time.Time `bson:"birth_date"` // Fecha de nacimiento del usuario
}
var (
userCollection *mongo.Collection // Referencia a la colección de usuarios
ctx context.Context // Contexto para operaciones con tiempo de espera
cancel context.CancelFunc // Función para cancelar el contexto
)
func init() {
// Configura un contexto con un tiempo de espera de 5 segundos para la conexión.
ctx, cancel = context.WithTimeout(context.Background(), 5*time.Second)
// URI de conexión a MongoDB.
connectionURI := "mongodb://localhost:27017/" // Reemplaza con tu URI si es diferente
// Configura las opciones del cliente, incluyendo la URI, el tiempo de espera y la autenticación.
clientOptions := options.Client().ApplyURI(connectionURI).
SetConnectTimeout(5 * time.Second). // Tiempo de espera para la conexión
SetAuth(options.Credential{ // Credenciales de autenticación (si son necesarias)
Username: "myuser",
Password: "mypassword",
})
// Conecta al servidor de MongoDB.
client, err := mongo.Connect(ctx, clientOptions)
if err != nil {
log.Fatal("Error al conectar a MongoDB:", err)
}
// Verifica que la conexión sea exitosa.
err = client.Ping(ctx, nil)
if err != nil {
log.Fatal("Error al hacer ping a MongoDB:", err)
}
// Obtiene una referencia a la colección 'users' en la base de datos 'testdb'.
userCollection = client.Database("testdb").Collection("users")
log.Println("Conexión a MongoDB exitosa y colección lista.")
// Nota: El cierre de la conexión (client.Disconnect) se maneja típicamente al final del programa principal
// o en una función defer en main() si el cliente es global. Aquí, para el ejemplo de init,
// asumimos que el programa principal gestionará el ciclo de vida del cliente.
}
func main() {
// Asegúrate de que el contexto se cancele al finalizar la función principal.
defer cancel()
// Ejemplo de uso: Insertar un nuevo usuario.
newUser := User{
Name: "Alice",
Age: 30,
Gender: 2, // Femenino
BirthDate: time.Now().AddDate(-30, 0, 0), // Aproximadamente 30 años atrás
}
// Inserta un solo documento.
insertResult, err := userCollection.InsertOne(ctx, newUser)
if err != nil {
log.Fatal("Error al insertar usuario:", err)
}
fmt.Printf("Usuario insertado con ID: %v\n", insertResult.InsertedID)
// Ejemplo de búsqueda: Buscar al usuario recién insertado.
var foundUser User
filter := bson.D{{Key: "name", Value: "Alice"}} // Filtro por nombre
err = userCollection.FindOne(ctx, filter).Decode(&foundUser)
if err != nil {
log.Fatal("Error al buscar usuario:", err)
}
fmt.Printf("Usuario encontrado: %+v\n", foundUser)
// Ejemplo de actualización: Incrementar la edad del usuario.
updateFilter := bson.D{{Key: "_id", Value: insertResult.InsertedID}} // Busca por el ID insertado
update := bson.D{{Key: "$inc", Value: bson.D{{Key: "age", Value: 1}}}}{} // Incrementa la edad en 1
updateResult, err := userCollection.UpdateOne(ctx, updateFilter, update)
if err != nil {
log.Fatal("Error al actualizar usuario:", err)
}
fmt.Printf("Documentos actualizados: %d\n", updateResult.ModifiedCount)
// Ejemplo de eliminación: Eliminar al usuario.
deleteResult, err := userCollection.DeleteOne(ctx, updateFilter)
if err != nil {
log.Fatal("Error al eliminar usuario:", err)
}
fmt.Printf("Documentos eliminados: %d\n", deleteResult.DeletedCount)
}
Operaciones Comunes
Creación de Documentos
Se pueden insertar uno o múltiples documentos a la vez.
package main
import (
"context"
"fmt"
"log"
"time"
"go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/mongo"
"go.mongodb.org/mongo-driver/mongo/options"
)
// User struct definition remains the same as in the connection example.
type User struct {
ID uint `bson:"_id,omitempty"`
Name string `bson:"name"`
Age int `bson:"age"`
Gender int8 `bson:"gender"`
BirthDate time.Time `bson:"birth_date"`
}
var (
userCollection *mongo.Collection
ctx context.Context
cancel context.CancelFunc
)
// init function remains the same as in the connection example.
func init() {
ctx, cancel = context.WithTimeout(context.Background(), 5*time.Second)
connectionURI := "mongodb://localhost:27017/"
clientOptions := options.Client().ApplyURI(connectionURI).
SetConnectTimeout(5 * time.Second).
SetAuth(options.Credential{
Username: "myuser",
Password: "mypassword",
})
client, err := mongo.Connect(ctx, clientOptions)
if err != nil {
log.Fatal("Error al conectar a MongoDB:", err)
}
err = client.Ping(ctx, nil)
if err != nil {
log.Fatal("Error al hacer ping a MongoDB:", err)
}
userCollection = client.Database("testdb").Collection("users")
log.Println("Conexión a MongoDB exitosa y colección lista.")
}
func main() {
defer cancel()
// Ensure client disconnect logic is handled appropriately in a real application,
// potentially in main() using defer if client is declared globally or passed around.
// Insertar un único usuario
singleUser := User{
ID: 1, // Note: _id is usually managed by MongoDB unless explicitly provided
Name: "Bob",
Age: 25,
Gender: 1, // Masculino
BirthDate: time.Now().AddDate(-25, 0, 0),
}
insertResult, err := userCollection.InsertOne(ctx, singleUser)
if err != nil {
log.Fatal("Error al insertar un solo usuario:", err)
}
fmt.Println("Insertado un usuario con ID:", insertResult.InsertedID)
// Insertar múltiples usuarios
usersToInsert := []interface{}{
User{Name: "Charlie", Age: 22, Gender: 1, BirthDate: time.Now().AddDate(-22, 0, 0)},
User{Name: "Diana", Age: 28, Gender: 2, BirthDate: time.Now().AddDate(-28, 0, 0)},
User{Name: "Ethan", Age: 35, Gender: 1, BirthDate: time.Now().AddDate(-35, 0, 0)},
}
insertManyResult, err := userCollection.InsertMany(ctx, usersToInsert)
if err != nil {
log.Fatal("Error al insertar múltiples usuarios:", err)
}
fmt.Println("Insertados múltiples usuarios con IDs:", insertManyResult.InsertedIDs)
}
Actualización de Documentos
Se pueden actualizar uno o todos los documentos que coincidan con el filtro.
package main
import (
"context"
"fmt"
"log"
"time"
"go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/mongo"
"go.mongodb.org/mongo-driver/mongo/options"
)
// User struct definition remains the same.
type User struct {
ID uint `bson:"_id,omitempty"`
Name string `bson:"name"`
Age int `bson:"age"`
Gender int8 `bson:"gender"`
BirthDate time.Time `bson:"birth_date"`
}
var (
userCollection *mongo.Collection
ctx context.Context
cancel context.CancelFunc
)
// init function remains the same.
func init() {
ctx, cancel = context.WithTimeout(context.Background(), 5*time.Second)
connectionURI := "mongodb://localhost:27017/"
clientOptions := options.Client().ApplyURI(connectionURI).
SetConnectTimeout(5 * time.Second).
SetAuth(options.Credential{
Username: "myuser",
Password: "mypassword",
})
client, err := mongo.Connect(ctx, clientOptions)
if err != nil {
log.Fatal("Error al conectar a MongoDB:", err)
}
err = client.Ping(ctx, nil)
if err != nil {
log.Fatal("Error al hacer ping a MongoDB:", err)
}
userCollection = client.Database("testdb").Collection("users")
log.Println("Conexión a MongoDB exitosa y colección lista.")
}
func main() {
defer cancel()
// Opciones para la actualización, permitiendo insertar si no se encuentra el documento.
updateOpts := options.Update().SetUpsert(true)
// Filtro para encontrar documentos con nombre "Bob".
filter := bson.D{{Key: "name", Value: "Bob"}}
// Operación de actualización: Establecer la edad a 666.
// Otra opción común es "$inc" para incrementar valores:
// update := bson.D{{Key: "$inc", Value: bson.D{{Key: "age", Value: 1}}}}
update := bson.D{{Key: "$set", Value: bson.D{{Key: "age", Value: 666}}}}
// Actualizar todos los documentos que coincidan con el filtro.
updateResult, err := userCollection.UpdateMany(ctx, filter, update, updateOpts)
if err != nil {
log.Fatal("Error al actualizar documentos:", err)
}
// Muestra las estadísticas de la operación.
fmt.Printf("Documentos coincidentes: %d, Documentos modificados: %d, Documentos insertados: %d, ID insertado: %v\n",
updateResult.MatchedCount, updateResult.ModifiedCount, updateResult.UpsertedCount, updateResult.UpsertedID)
}
Consulta de Documentos
Permite filtrar, ordenar y paginar los resultados.
package main
import (
"context"
"fmt"
"log"
"time"
"go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/mongo"
"go.mongodb.org/mongo-driver/mongo/options"
)
// User struct definition remains the same.
type User struct {
ID uint `bson:"_id,omitempty"`
Name string `bson:"name"`
Age int `bson:"age"`
Gender int8 `bson:"gender"`
BirthDate time.Time `bson:"birth_date"`
}
var (
userCollection *mongo.Collection
ctx context.Context
cancel context.CancelFunc
)
// init function remains the same.
func init() {
ctx, cancel = context.WithTimeout(context.Background(), 5*time.Second)
connectionURI := "mongodb://localhost:27017/"
clientOptions := options.Client().ApplyURI(connectionURI).
SetConnectTimeout(5 * time.Second).
SetAuth(options.Credential{
Username: "myuser",
Password: "mypassword",
})
client, err := mongo.Connect(ctx, clientOptions)
if err != nil {
log.Fatal("Error al conectar a MongoDB:", err)
}
err = client.Ping(ctx, nil)
if err != nil {
log.Fatal("Error al hacer ping a MongoDB:", err)
}
userCollection = client.Database("testdb").Collection("users")
log.Println("Conexión a MongoDB exitosa y colección lista.")
}
func main() {
defer cancel()
// Definir criterios de ordenación: por edad descendente.
sortOrder := bson.D{{Key: "age", Value: -1}}
// Definir el filtro de búsqueda. Un filtro vacío ({}) busca todos los documentos.
filter := bson.D{{}} // Equivalente a bson.M{}
// Configurar las opciones de búsqueda: límite, omisión y ordenación.
findOptions := options.Find().
SetSort(sortOrder). // Aplicar ordenación
SetLimit(2). // Limitar a 2 resultados
SetSkip(0) // Omitir los primeros 0 resultados (para paginación, se puede ajustar)
// Ejecutar la consulta.
cursor, err := userCollection.Find(ctx, filter, findOptions)
if err != nil {
log.Fatal("Error al ejecutar la consulta:", err)
}
defer cursor.Close(ctx) // Asegurarse de cerrar el cursor
// Iterar sobre los resultados.
fmt.Println("Resultados de la búsqueda:")
for cursor.Next(ctx) {
var user User
if err := cursor.Decode(&user); err != nil {
log.Fatal("Error al decodificar el documento:", err)
}
fmt.Printf(" %+v\n", user)
}
// Verificar si hubo errores durante la iteración.
if err := cursor.Err(); err != nil {
log.Fatal("Error en el cursor:", err)
}
}
Eliminación de Documentos
Se pueden eliminar uno o todos los documentos que coincidan con el filtro.
package main
import (
"context"
"fmt"
"log"
"time"
"go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/mongo"
"go.mongodb.org/mongo-driver/mongo/options"
)
// User struct definition remains the same.
type User struct {
ID uint `bson:"_id,omitempty"`
Name string `bson:"name"`
Age int `bson:"age"`
Gender int8 `bson:"gender"`
BirthDate time.Time `bson:"birth_date"`
}
var (
userCollection *mongo.Collection
ctx context.Context
cancel context.CancelFunc
)
// init function remains the same.
func init() {
ctx, cancel = context.WithTimeout(context.Background(), 5*time.Second)
connectionURI := "mongodb://localhost:27017/"
clientOptions := options.Client().ApplyURI(connectionURI).
SetConnectTimeout(5 * time.Second).
SetAuth(options.Credential{
Username: "myuser",
Password: "mypassword",
})
client, err := mongo.Connect(ctx, clientOptions)
if err != nil {
log.Fatal("Error al conectar a MongoDB:", err)
}
err = client.Ping(ctx, nil)
if err != nil {
log.Fatal("Error al hacer ping a MongoDB:", err)
}
userCollection = client.Database("testdb").Collection("users")
log.Println("Conexión a MongoDB exitosa y colección lista.")
}
func main() {
defer cancel()
// Filtro para eliminar documentos con nombre "Charlie".
// Se puede usar bson.D o bson.M para definir filtros.
// filter := bson.D{{Key: "name", Value: bson.D{{Key: "$eq", Value: "Charlie"}}}}
filter := bson.M{"name": "Charlie"} // Forma más concisa
// Eliminar un solo documento que coincida con el filtro.
// deleteResult, err := userCollection.DeleteOne(ctx, filter)
// Eliminar todos los documentos que coincidan con el filtro.
deleteResult, err := userCollection.DeleteMany(ctx, filter)
if err != nil {
log.Fatal("Error al eliminar documentos:", err)
}
fmt.Printf("Número de documentos eliminados: %d\n", deleteResult.DeletedCount)
}
Tipos BSON en Go
El driver de Go para MongoDB proporciona varias estructuras para trabajar con BSON:
bson.M: Un mapa sin orden de pares clave-valor (map[string]interface{}). Ideal para condiciones de consulta y operaciones de actualización.bson.D: Una lista ordenada de pares clave-valor ([]bson.E). Útil cuando el orden de los campos es importante, como en comandos de base de datos o pipelines de agregación.bson.A: Un array BSON ([]interface{}). Para almacenar coleciones de valores.bson.E: Un único par clave-valor, usado para construirbson.D.
Tabla Comparativa de Tipos BSON
| Tipo | Orden | Uso Común | Ejemplo |
|---|---|---|---|
bson.M |
Sin ordenar | Filtros de consulta, actualizaciones | bson.M{"name": "Alice", "age": bson.M{"$gt": 20}} |
bson.D |
Ordenado | Comandos, pipelines de agregación | bson.D{{"$match", bson.D{{"status", "active"}}}} |
bson.A |
Ordenado | Arrays, listas de valores | bson.A{"red", "green", "blue"} |
bson.E |
Ordenado | Componente de bson.D |
bson.E{Key: "name", Value: "Alice"} |
Etiquetas BSON en Estructuras Go
Las etiquetas BSON permiten mapear campos de estructuras Go a campos de documentos MongoDB y controlar cómo se serializan/deserializan.
- `bson:"field_name"``: Mapea un campo a un nombre específico en MongoDB.
bson:"_id,omitempty"``: Define el campo como el identificador único_id, omitiéndolo si está vacío al insertar. Requiere usarprimitive.ObjectID` como tipo.- `bson:"-"``: Ignora el campo, no se incluye en el documento MongoDB.
- `bson:",inline"``: Embebe el contenido de la estructura o mapa del campo directamante en el documento MongoDB.
bson:",omitempty"``: Omite el campo si su valor es el "valor cero" (por ejemplo,0,"",nil`).
Ejemplo de Estructura con Etiquetas BSON
import "go.mongodb.org/mongo-driver/bson/primitive"
type Product struct {
ID primitive.ObjectID `bson:"_id,omitempty"`
Name string `bson:"productName"`
Description string `bson:"description,omitempty"`
Price float64 `bson:"price"`
Stock int `bson:"stock,omitempty"`
Details map[string]string `bson:"details,inline"` // Embebe el mapa 'Details'
InternalCode string `bson:"-"` // Este campo no se guardará en MongoDB
}
Usar las etiquetas BSON correctamente facilita la interacción entre tus modelos de Go y la estructura de tus documentos MongoDB.