Skip to content

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)

Установка

Для загрузки зависимостей используйте следующий адрес:

sh
$ go get go.mongodb.org/mongo-driver/mongo

Подключение

Ниже приведён простой пример подключения клиента MongoDB к серверу.

go
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:

go
// Упорядоченное представление BSON-документа
type D = primitive.D

// Пара ключ-значение, основная единица упорядоченного представления BSON-документа
type E = primitive.E

// Неупорядоченное представление BSON-документа
type M = primitive.M

// Упорядоченное представление BSON-данных
type A = primitive.A

Их фактические типы:

go
// Упорядоченное представление 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)

Запрос документов

Официальный пример запроса: mongo-go-driver/examples/documentation_examples/examples.go at master · mongodb/mongo-go-driver (github.com)

Сначала создайте базу данных user и вставьте следующие данные в коллекцию users:

sql
> use user
> db.users.insertMany([
    {
        name: "mike",
        age: 12,
    },
    {
        name: "jenny",
        age: 14,
    },
    {
        name: "jack",
        age: 18,
        address: "usa"
    }
])

Запрос одного документа

go
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)

Этот запрос эквивалентен:

sql
db.users.findOne({
    address: "usa"
})

Вывод:

{Name:jack Age:18 Address:usa}

Запрос нескольких документов

go
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)

Эквивалентно:

sql
db.users.find({})

Вывод:

[{Name:jack Age:18 Address:usa} {Name:mike Age:12 Address:} {Name:jenny Age:14 Address:}]

При构造лении условий запроса также можно использовать options:

go
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)

Пример создания одного документа:

go
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")

Пример создания нескольких документов:

go
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:

go
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)

Эквивалентно:

sql
db.users.updateOne({
    name: "lili"
}, {
    $set: {
        name: "mark"
    },
})

Вывод:

&{MatchedCount:1 ModifiedCount:1 UpsertedCount:0 UpsertedID:<nil>}

Пример обновления нескольких документов, обновление адреса для пользователей возрастом 10 лет на cn:

go
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. Разница между ними заключается в том, что первый обновляет поля документа, а второй полностью заменяет документ. Например, в следующем коде операторы больше не нужны:

go
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 для получения и обновления документов:

go
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)

Пример удаления одного документа:

go
result, err := client.Database("user").Collection("users").DeleteOne(ctx, bson.D{
    {"name", "jack"},
})
if err != nil {
    log.Panicln(err)
}
fmt.Println(result.DeletedCount)

Пример удаления нескольких документов:

go
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:

go
type Pipeline []bson.D
go
pipline := 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, затем сортирует их по возрасту.

Golang by www.golangdev.cn edit