MongoDB
MongoDB — это документоориентированная база данных. Её основной единицей данных является документ, формат хранения — BSON (Binary JSON), структура, похожая на JSON. Свободная структура позволяет хранить данные разных типов, что делает MongoDB более гибкой по сравнению с реляционными базами данных. В качестве языка скриптов используется JavaScript, что позволяет выполнять комбинированные операции через скрипты. В этой статье рассматривается использование официального драйвера mongo для работы с MongoDB на Go. Это не учебник по MongoDB, поэтому если у вас нет опыта работы с MongoDB, пожалуйста, ознакомьтесь с ним самостоятельно.
Документация MongoDB: Introduction to MongoDB — MongoDB Manual
Драйвер
Библиотек для работы с MongoDB на Go немного. Раньше существовали библиотеки, поддерживаемые сообществом, но позже они были прекращены. Официальный драйвер mongo полностью достаточен для использования.
Репозиторий: mongodb/mongo-go-driver: The Official Golang driver for MongoDB (github.com)
Документация: mongodb/mongo-go-driver: The Official Golang driver for MongoDB (github.com)
Установка
Для загрузки зависимостей используйте следующий адрес:
$ go get go.mongodb.org/mongo-driver/mongoПодключение
Ниже приведён простой пример подключения клиента MongoDB к серверу.
package main
import (
"context"
"fmt"
"go.mongodb.org/mongo-driver/mongo"
"go.mongodb.org/mongo-driver/mongo/options"
"go.mongodb.org/mongo-driver/mongo/readpref"
"log"
)
func main() {
ctx := context.Background()
// Подключение с использованием URI
client, err := mongo.Connect(ctx, options.Client().ApplyURI("mongodb://admin:123456@192.168.48.138:27017/"))
if err != nil {
log.Panicln(err)
}
// Закрытие подключения
defer client.Disconnect(ctx)
// Проверка подключения через ping
fmt.Println(client.Ping(ctx, readpref.Primary()))
}BSON
В Go MongoDB использует следующие типы для отображения документов базы данных, расположенные в bson/bson.go:
// Упорядоченное представление BSON-документа
type D = primitive.D
// Пара ключ-значение, основная единица упорядоченного представления BSON-документа
type E = primitive.E
// Неупорядоченное представление BSON-документа
type M = primitive.M
// Упорядоченное представление BSON-данных
type A = primitive.AИх фактические типы:
// Упорядоченное представление BSON-документа
type D []E
// Пара ключ-значение, основная единица упорядоченного представления BSON-документа
type E struct {
Key string
Value interface{}
}
// Неупорядоченное представление BSON-документа
type M map[string]interface{}
// Упорядоченное представление BSON-данных
type A []interface{}С помощью этих типов можно как构造ить запросы SQL, так и отображать данные.
TIP
В каталоге examples драйвера содержится множество примеров использования. Официальная документация очень подробно демонстрирует использование этих четырёх типов.
Запрос документов
Официальный пример запроса: mongo-go-driver/examples/documentation_examples/examples.go at master · mongodb/mongo-go-driver (github.com)
Сначала создайте базу данных user и вставьте следующие данные в коллекцию users:
> use user
> db.users.insertMany([
{
name: "mike",
age: 12,
},
{
name: "jenny",
age: 14,
},
{
name: "jack",
age: 18,
address: "usa"
}
])Запрос одного документа
type User struct {
Name string `bson:"name"`
Age int `bson:"age"`
Address string `bson:"address"`
}
var user User
result := client.Database("user"). // Выбор базы данных
Collection("users"). // Выбор коллекции
FindOne(ctx, bson.D{{"address", "usa"}}) // Условие фильтра
// Десериализация
if err := result.Decode(&user); err != nil {
log.Panicln(err)
}
fmt.Printf("%+v\n", user)Этот запрос эквивалентен:
db.users.findOne({
address: "usa"
})Вывод:
{Name:jack Age:18 Address:usa}Запрос нескольких документов
type User struct {
Name string `bson:"name"`
Age int `bson:"age"`
Address string `bson:"address"`
}
var users []User
cursor, err := client.Database("user"). // Выбор базы данных
Collection("users"). // Выбор коллекции
Find(ctx, bson.D{}) // Условие фильтра
if err != nil {
log.Panicln(err)
}
if err := cursor.All(ctx, &users); err != nil {
log.Panicln(err)
}
fmt.Printf("%+v\n", users)Эквивалентно:
db.users.find({})Вывод:
[{Name:jack Age:18 Address:usa} {Name:mike Age:12 Address:} {Name:jenny Age:14 Address:}]При构造лении условий запроса также можно использовать options:
type User struct {
Name string `bson:"name"`
Age int `bson:"age"`
Address string `bson:"address"`
}
var users []User
find := options.Find()
find.SetSort(bson.M{"age": 1})
find.SetLimit(1)
cursor, err := client.Database("user"). // Выбор базы данных
Collection("users"). // Выбор коллекции
Find(ctx, bson.D{}, find) // Условие фильтра
if err != nil {
log.Panicln(err)
}
if err := cursor.All(ctx, &users); err != nil {
log.Panicln(err)
}
fmt.Printf("%+v\n", users)Эквивалентно:
db.users.find({}).sort({age:1}).limit(1)Вывод:
[{Name:mike Age:12 Address:}]Создание документов
Официальный пример создания: mongo-go-driver/examples/documentation_examples/examples.go at master · mongodb/mongo-go-driver (github.com)
Пример создания одного документа:
one, err := client.Database("user").Collection("users").InsertOne(ctx, User{
Name: "lili",
Age: 20,
Address: "china",
})
if err != nil {
log.Panicln(err)
}
fmt.Println(one.InsertedID)После успешного создания возвращается ObjectID документа:
ObjectID("64c60fa01e2548d9e4de6cf4")Пример создания нескольких документов:
users := []any{User{
Name: "john",
Age: 10,
Address: "usa",
}, User{
Name: "pop",
Age: 30,
Address: "uk",
}}
one, err := client.Database("user").Collection("users").InsertMany(ctx, users)
if err != nil {
log.Panicln(err)
}
fmt.Println(one.InsertedIDs)После успешного создания возвращается набор ObjectID:
[ObjectID("64c610d5aec2618d6ca0b515") ObjectID("64c610d5aec2618d6ca0b516")]Эти два фрагмента кода эквивалентны db.users.insertOne и db.users.insertMany.
Обновление документов
Официальный пример обновления: mongo-go-driver/examples/documentation_examples/examples.go at master · mongodb/mongo-go-driver (github.com)
Пример обновления одного документа, переименование пользователя с именем lili в mark:
upres, err := client.Database("user").Collection("users").UpdateOne(ctx, bson.D{
{"name", "mark"},
},
bson.D{
{"$set", bson.D{
{"name", "lili"},
}},
})
if err != nil {
log.Panicln(err)
}
fmt.Printf("%+v", upres)Эквивалентно:
db.users.updateOne({
name: "lili"
}, {
$set: {
name: "mark"
},
})Вывод:
&{MatchedCount:1 ModifiedCount:1 UpsertedCount:0 UpsertedID:<nil>}Пример обновления нескольких документов, обновление адреса для пользователей возрастом 10 лет на cn:
upres, err := client.Database("user").Collection("users").UpdateMany(ctx, bson.D{
{"age", 10},
},
bson.D{
{"$set", bson.D{
{"address", "cn"},
}},
})
if err != nil {
log.Panicln(err)
}
fmt.Printf("%+v", upres)Помимо Update, MongoDB также предоставляет Replace. Разница между ними заключается в том, что первый обновляет поля документа, а второй полностью заменяет документ. Например, в следующем коде операторы больше не нужны:
upres, err := client.Database("user").Collection("users").ReplaceOne(ctx, bson.D{
{"age", 10},
},
bson.D{
{"address", "cn"},
})
if err != nil {
log.Panicln(err)
}
fmt.Printf("%+v", upres)MongoDB также предоставляет FindOneAndUpdate и FindOneAndReplace для получения и обновления документов:
res := client.Database("user").Collection("users").FindOneAndReplace(ctx, bson.D{
{"address", "cn"},
},
bson.D{
{"address", "uk"},
})
if err := res.Err(); err != nil {
log.Panicln(err)
}
var user User
res.Decode(&user)
fmt.Printf("%+v", user)Вывод:
Name: Age:0 Address:cn}Эта операция сначала запрашивает документ, затем modifies его.
Удаление документов
Официальный пример удаления: mongo-go-driver/examples/documentation_examples/examples.go at master · mongodb/mongo-go-driver (github.com)
Пример удаления одного документа:
result, err := client.Database("user").Collection("users").DeleteOne(ctx, bson.D{
{"name", "jack"},
})
if err != nil {
log.Panicln(err)
}
fmt.Println(result.DeletedCount)Пример удаления нескольких документов:
result, err := client.Database("user").Collection("users").DeleteMany(ctx, bson.D{
{"age", "10"},
})
if err != nil {
log.Panicln(err)
}
fmt.Println(result.DeletedCount)Агрегация
Официальный пример агрегации: mongo-go-driver/examples/documentation_examples/examples.go at master · mongodb/mongo-go-driver (github.com)
Операция агрегации использует тип mongo.Pipeline, который по сути является []bson.D:
type Pipeline []bson.Dpipline := mongo.Pipeline{
{
{"$match", bson.D{
{"address", "uk"},
}},
},
{
{"$sort", bson.D{
{"age", 1},
}},
},
}
aggregate, err := client.Database("user").Collection("users").Aggregate(ctx, pipline)
if err != nil {
log.Panicln(err)
}
var users []User
if err := aggregate.All(ctx, &users); err != nil {
log.Panicln(err)
}
log.Println(users)Вывод:
[{lili 20 uk} {kak 30 uk}]Эта операция агрегации сопоставляет всех пользователей с address равным uk, затем сортирует их по возрасту.
