Mongo migrator is a library that provides migration management operations.
go get -v github.com/Devoter/mongo-migrator
The following instructions are only recommendations, of course, you can use the library as like as you wish.
To use the library you should create migrations functions. It is intuitive to declare migrations
package in your project. All your migrations should be placed in one slice of type []migration.Migration
.
There is a simple way to declare the migrations slice:
// migrations/migrations.go
package migrations
import "github.com/Devoter/mongo-migrator/migration"
// Migrations is a list of all available migrations.
var Migrations = []migration.Migration{}
It is recommended to put all migrations functions in separate files named like <number>.<name>.go
. Each migration file should contain init
function that appends current migration to the global migrations slice. Every migration must have two functions: up
and down
that applies or rolls back the migration respectively.
// migrations/1.init.go
package migrations
import (
"context"
"go.mongodb.org/mongo-driver/mongo"
"github.com/Devoter/mongo-migrator/migration"
)
func init() {
Migrations = append(Migrations, migration.Migration{
Version: 1,
Name: "init",
Up: migration.DummyUpDown, // built-in dummy migration function
Down: Down1,
})
}
// Down1 applies `down` migration.
func Down1(db *mongo.Database) (err error) {
majOpts := migration.MajorityOpts()
collections := []string{"groups", "rules", "templates", "users"}
for _, collection := range collections {
if err = db.Collection(collection, majOpts).Drop(context.TODO()); err != nil {
return
}
}
return
}
As you can see up
and down
functions use an official MongoDB Go driver. It is a good practice to create idempotent migrations.
The migrator supports six commands: init
, up
, down
, reset
, version
, and set_version
.
The current migration version must be saved in the database migrations
collection. You don't have to create it manually, just call the init
command of the migrator:
package usage_example
import (
"go.mongodb.org/mongo-driver/mongo"
"github.com/Devoter/mongo-migrator/migrator"
"github.com/Devoter/mongo-migrator/migration"
)
// InitializeMigrations initializes the database migrations collection.
func InitializeMigrations(client *mongo.Client, dbName string, migrations []migration.Migration) {
// Creating a migrator instance. The constructor sorts the migrations slice automatically
// and appends the `zero` migration.
migrator := migrator.NewMigrator(client, migrations)
// Creating the `migrations` collection and initialing it with the zero database structure version.
if _, _, err := migrator.Run(dbName, "init"); err != nil {
panic(err)
}
}
It is so easy to apply all migrations:
package usage_example
import (
"fmt"
"go.mongodb.org/mongo-driver/mongo"
"github.com/Devoter/mongo-migrator/migrator"
"github.com/Devoter/mongo-migrator/migration"
)
// UpAuto applies all unapplied migrations.
func UpAuto(client *mongo.Client, dbName string, migrations []migration.Migration) {
// Creating a migrator instance. The constructor sorts the migrations slice automatically
// and appends the `zero` migration.
migrator := migrator.NewMigrator(client, migrations)
// Applying all unapplied migrations.
old, current, err := migrator.Run(dbName, "up")
if err != nil {
panic(err)
}
// Printing old and current versions. If there are no unapplied migrations `old` and `current` are equal.
fmt.Printf("Old version: %d, current version: %d\n", old, current)
}
Certainly you may want to set the targe version of the database structure. It is pretty simple: just add an additional parameter with the target version.
package usage_example
import (
"fmt"
"go.mongodb.org/mongo-driver/mongo"
"github.com/Devoter/mongo-migrator/migrator"
"github.com/Devoter/mongo-migrator/migration"
)
// UpTo applyies all unapplied migrations to the target.
func UpTo(client *mongo.Client, dbName string, migrations []migration.Migration, target string) {
// Creating a migrator instance. The constructor sorts the migrations slice automatically
// and appends the `zero` migration.
migrator := migrator.NewMigrator(client, migrations)
// Applying all unapplied migrations up to the target.
old, current, err := migrator.Run(dbName, "up", target)
if err != nil {
panic(err)
}
// Printing old and current versions. If there are no unapplied migrations `old` and `current` are equal.
fmt.Printf("Old version: %d, current version: %d\n", old, current)
}
The down
command removes the last migration (calls the migration down
function). This command does not support any additional parameters. It is very similar to the up
command.
package usage_example
import (
"fmt"
"go.mongodb.org/mongo-driver/mongo"
"github.com/Devoter/mongo-migrator/migrator"
"github.com/Devoter/mongo-migrator/migration"
)
// Down reverts the last applied migration.
func Down(client *mongo.Client, dbName string, migrations []migration.Migration) {
// Creating a migrator instance. The constructor sorts the migrations slice automatically
// and appends the `zero` migration.
migrator := migrator.NewMigrator(client, migrations)
// Reverting the last applied migration.
old, current, err := migrator.Run(dbName, "down")
if err != nil {
panic(err)
}
// Printing old and current versions. If there are no applied migrations `old` and `current` are equal.
fmt.Printf("Old version: %d, current version: %d\n", old, current)
}
This command reverts all applied migrations and sets the database structure version to zero.
package usage_example
import (
"fmt"
"go.mongodb.org/mongo-driver/mongo"
"github.com/Devoter/mongo-migrator/migrator"
"github.com/Devoter/mongo-migrator/migration"
)
// Reset reverts all applied migrations.
func Reset(client *mongo.Client, dbName string, migrations []migration.Migration) {
// Creating a migrator instance. The constructor sorts the migrations slice automatically
// and appends the `zero` migration.
migrator := migrator.NewMigrator(client, migrations)
// Reverting all applied migrations.
old, current, err := migrator.Run(dbName, "reset")
if err != nil {
panic(err)
}
// Printing old and current versions. If there are no applied migrations `old` and `current` are equal.
fmt.Printf("Old version: %d, current version: %d\n", old, current)
}
This command returns the current database structure version.
package usage_example
import (
"fmt"
"go.mongodb.org/mongo-driver/mongo"
"github.com/Devoter/mongo-migrator/migrator"
"github.com/Devoter/mongo-migrator/migration"
)
// Version outputs the current database structure version.
func Version(client *mongo.Client, dbName string, migrations []migration.Migration) {
// Creating a migrator instance. The constructor sorts the migrations slice automatically
// and appends the `zero` migration.
migrator := migrator.NewMigrator(client, migrations)
// Getting the current database structure version.
_, current, err := migrator.Run(dbName, "version")
if err != nil {
panic(err)
}
// Printing the current version.
fmt.Printf("Current version: %d\n", current)
}
The set_version
command changes the current version without applying migrations.
package usage_example
import (
"fmt"
"go.mongodb.org/mongo-driver/mongo"
"github.com/Devoter/mongo-migrator/migrator"
"github.com/Devoter/mongo-migrator/migration"
)
// SetVersion changes the current database structure version number to the target.
func SetVersion(client *mongo.Client, dbName string, migrations []migration.Migration, target string) {
// Creating a migrator instance. The constructor sorts the migrations slice automatically
// and appends the `zero` migration.
migrator := migrator.NewMigrator(client, migrations)
// Changing the database structure version.
old, current, err := migrator.Run(dbName, "set_version", target)
if err != nil {
panic(err)
}
// Printing old and current versions.
fmt.Printf("Old version: %d, current version: %d\n", old, current)
}
In contrast, you can call commands functions directly. Be careful, direct calls receives target
argument of type int64
instead of string
and mongo.Database
pointer instead of database name.
package usage_example
import (
"fmt"
"go.mongodb.org/mongo-driver/mongo"
"github.com/Devoter/mongo-migrator/migrator"
"github.com/Devoter/mongo-migrator/migration"
)
func DirectCalls(client *mongo.Client, dbName string, migrations []migration.Migration, target int64) {
// Creating a migrator instance. The constructor sorts the migrations slice automatically
// and appends the `zero` migration.
migrator := migrator.NewMigrator(client, migrations)
// Getting a database pointer.
base := m.client.Database(dbName)
// Creating the `migrations` collection and initialing it with the zero database structure version.
if _, _, err := migrator.Init(base); err != nil {
panic(err)
}
// Applying all unapplied migrations up to the target.
old, current, err := migrator.Up(base, target)
if err != nil {
panic(err)
}
// Printing old and current versions.
fmt.Printf("Old version: %d, current version: %d\n", old, current)
// Applying all unapplied migrations.
old, current, err = migrator.Up(base, -1)
if err != nil {
panic(err)
}
fmt.Printf("Old version: %d, current version: %d\n", old, current)
// Reverting the last applied migration.
old, current, err = migrator.Down(base)
if err != nil {
panic(err)
}
fmt.Printf("Old version: %d, current version: %d\n", old, current)
// Reverting all applied migrations.
old, current, err = migrator.Reset(base)
if err != nil {
panic(err)
}
fmt.Printf("Old version: %d, current version: %d\n", old, current)
// Changing the database structure version to target.
old, current, err = migrator.SetVersion(base, target)
if err != nil {
panic(err)
}
fmt.Printf("Old version: %d, current version: %d\n", old, current)
// Getting the current database structure version.
_, current, err = migrator.Version(base)
if err != nil {
panic(err)
}
fmt.Printf("Current version: %d\n", current)
// Changing the database structure version to zero.
old, current, err = migrator.SetVersion(base, 0)
if err != nil {
panic(err)
}
fmt.Printf("Old version: %d, current version: %d\n", old, current)
}