Рубрики
Без рубрики

Как автоматизировать миграцию базы данных в MongoDB

Введение разработчика программного обеспечения в какой-то момент, возможно, вам может придеться справиться с миграциями базы данных. Поскольку программное обеспечение или приложения развиваются и улучшаются в течение времени, ваша база данных также должна. И мы должны убедиться, что данные остаются последовательными в приложении. Есть А.

Автор оригинала: Shailesh Shekhawat.

Вступление

В качестве разработчика программного обеспечения в какой-то момент или иной момент или иначе придется иметь дело с миграциями базы данных.

Поскольку программное обеспечение или приложения развиваются и улучшаются в течение времени, ваша база данных также должна. И мы должны убедиться, что данные остаются последовательными в приложении.

Существует ряд различных способов того, что схема может измениться из одной версии вашего приложения к следующему.

  • Добавлен новый участник
  • Участник удален
  • Член переименован
  • Тип участника изменен
  • Представление члена изменилось

Так как вы обрабатываете все вышеперечисленные изменения?

через гипю

Есть две стратегии:

  • Напишите сценарий, который позаботится о модернизации схемы, а также понижение его до предыдущих версий
  • Обновите ваши документы Как они используются

Второй гораздо больше, зависит от кода и должен оставаться в вашей кодовой базе. Если код каким-то образом удален, то многие документы не обновляются.

Например, если произошло 3 версии документа, [1, 2 и 3], и мы удаляем код обновления из версии 1 до версии 2, любые документы, которые все еще существуют в качестве версии 1, не обновляются. Я лично вижу это как накладные для поддержания кода, и он становится негибким.

Поскольку эта статья о автоматизации миграций, я собираюсь показать вам, как вы можете написать простой скрипт, который позаботится о изменениях схемы, а также и тесты на единицу.

Участник был добавлен

Когда член был добавлен в схему, существующий документ не будет иметь информацию. Таким образом, вам нужно запросить все документы, где этот член не существует и их обновляется.

Давайте продолжим написание некоторых кода.

Доступно уже несколько модулей NPM, но я использовал библиотеку Узел миграция Отказ Я тоже пробовал других, но некоторые из них больше не поддерживаются, и я столкнулся с проблемами, созданными другими.

Предпосылки

  • Узел миграция – абстрактные миграционные рамки для узла
  • Монгодб – родной водитель MongoDB для Nodejs
  • Моча – Функция тестирования
  • Чай – Утверждение библиотеки для написания тестовых случаев
  • Bluebird : Обещание библиотеки для обработки вызовов Async API
  • mkdirp : Как mkdir -p но в Node.js.
  • Римраф : RM -RF для узла

Состояние миграции

Состояние миграции является наиболее важным ключом для отслеживания вашей текущей миграции. Без этого мы не сможем отслеживать:

  • Сколько миграций было сделано
  • Какова была последняя миграция
  • Какая текущая версия схемы Мы используем

И без государств нет никакого способа откатиться, модернизировать и наоборот в другом состоянии.

Создание миграции

Создать миграцию, выполнить Миграция Создать Le> с названием.

По умолчанию файл в ./migrations/ будет создан со следующим контентом:

'use strict'

module.exports.up = function (next) {
  next()
}

module.exports.down = function (next) {
  next()
}

Давайте возьмем пример Пользователь Схема, где у нас есть недвижимость Имя который включает в себя оба Первый и Последнее имя.

Теперь мы хотим изменить схему, чтобы иметь отдельный Последнее Имя свойства.

Так что для того, чтобы автоматизировать это, мы будем читать Имя Во время выполнения и извлечь фамилию и сохранить его как новое свойство.

Создайте миграцию с помощью этой команды:

$ migrate create add-last-name.js

Этот звонок создаст ./migrations/proftertimestamp в milliseconds} -add-last-name.js под миграция Папка в корневом каталоге.

Давайте напишем код для добавления фамилии в схему, а также для удаления его.

Миграция

Мы найдем всех пользователей, где фамилия Собственность не существует и создает новую недвижимость фамилия в этих документах.

'use strict'
const Bluebird = require('bluebird')
const mongodb = require('mongodb')
const MongoClient = mongodb.MongoClient
const url = 'mongodb://localhost/Sample'
Bluebird.promisifyAll(MongoClient)

module.exports.up = next => {
  let mClient = null
  return MongoClient.connect(url)
  .then(client => {
    mClient = client
    return client.db();
  })
  .then(db => {
    const User = db.collection('users')
    return User
      .find({ lastName: { $exists: false }})
      .forEach(result => {
        if (!result) return next('All docs have lastName')
        if (result.name) {
           const { name } = result
           result.lastName = name.split(' ')[1]
           result.firstName = name.split(' ')[0]
        }
        return db.collection('users').save(result)
     })
  })
  .then(() => {
    
    mClient.close()
    return next()
  })
   .catch(err => next(err))
}

Внешнее миграция

Точно так же давайте напишем функцию, где мы удалим фамилия :

module.exports.down = next => {
let mClient = null
return MongoClient
   .connect(url)  
   .then(client => {
    mClient = client
    return client.db()
  })
  .then(db =>
    db.collection('users').update(
    {
       lastName: { $exists: true }
    },
    {
      $unset: { lastName: "" },
    },
     { multi: true }
  ))
  .then(() => {
    mClient.close()
    return next()
  })
  .catch(err => next(err))

}

Бег миграции

Проверьте, как здесь выполняется миграции: Бег миграции Отказ

Написание пользовательского состояния хранения

По умолчанию мигрировать хранит состояние миграции, которые были запущены в файле ( .migrate ).

.migrate Файл будет содержать следующий код:

{
  "lastRun": "{timestamp in milliseconds}-add-last-name.js",
  "migrations": [
    {
      "title": "{timestamp in milliseconds}-add-last-name.js",
      "timestamp": {timestamp in milliseconds}
    }
  ]
}

Но вы можете предоставить пользовательский механизм хранения, если вы хотите сделать что-то другое, как хранить их в вашей базе данных выбора.

Двигатель хранения имеет простой интерфейс Загрузить (FN) и Сохранить (Set, Fn) Отказ

До тех пор, пока что идет как Установить выходит то же самое на нагрузка Тогда ты хорош!

Давайте создадим файл DB-Migrate-Store.js в корневом каталоге проекта.

const mongodb = require('mongodb')
const MongoClient = mongodb.MongoClient
const Bluebird = require('bluebird')

Bluebird.promisifyAll(MongoClient)
class dbStore {
   constructor () {
     this.url = 'mongodb://localhost/Sample' . // Manage this accordingly to your environment
    this.db = null
    this.mClient = null
   }
   connect() {
     return MongoClient.connect(this.url)
      .then(client => {
        this.mClient = client
        return client.db()
      })
   }
    load(fn) {
      return this.connect()
      .then(db => db.collection('migrations').find().toArray())
      .then(data => {
        if (!data.length) return fn(null, {})
        const store = data[0]
        // Check if does not have required properties
          if (!Object
               .prototype
               .hasOwnProperty
               .call(store, 'lastRun') 
                ||
              !Object
              .prototype
              .hasOwnProperty
             .call(store, 'migrations'))
            {
            return fn(new Error('Invalid store file'))
            }
        return fn(null, store)
      }).catch(fn)
    }
   save(set, fn) {
     return this.connect()
      .then(db => db.collection('migrations')
      .update({},
       {
         $set: {
           lastRun: set.lastRun,
         },
         $push: {
            migrations: { $each: set.migrations },
         },
      },
      {
         upsert: true,
         multi: true,
       }
      ))
       .then(result => fn(null, result))
       .catch(fn)
   }
}

module.exports = dbStore

Загрузить (FN) В этой функции мы просто проверяем, если существующий загруженный документ «Существующий миграционный» Ластран недвижимость и миграции множество.

Сохранить (Set, Fn) Здесь Установить предоставляется библиотекой, и мы обновляем Ластран ценность и добавление миграции к существующему массиву.

Вам может быть интересно, где вышеуказанный файл DB-Migrate-Store.js используется. Мы создаем его, потому что мы хотим хранить состояние в базе данных, а не в репозитории кода.

Ниже Тестовые примеры, где вы можете увидеть его использование.

Автоматизация миграционного тестирования

Установите Mocha:

$ npm install -g mocha

Состав

Чтобы настроить основные тесты, создайте новую папку, называемую «тест» в корне в проекте, то в этой папке добавляют папку, называемую миграция .

Структура вашего файла/папки теперь должна выглядеть так:

├── package.json
├── app
│   ├── server.js
│   ├── models
│   │   └── user.js
│   └── routes
│       └── user.js
└── test
       migrations
        └── create-test.js
        └── up-test.js 
        └── down-test.js

Тест – создать миграцию

Цель: Он должен создать каталог миграций и файл.

$ мигрировать Создать добавку

Это подразумевает создание файла ./migrations/proftertimestamp в milliseconds} -add-last-name.js под миграции Папка в корневом каталоге.

Теперь добавьте следующий код для create-test.js файл:

const Bluebird = require('bluebird')
const { spawn } = require('child_process')
const mkdirp = require('mkdirp')
const rimraf = require('rimraf')
const path = require('path')
const fs = Bluebird.promisifyAll(require('fs'))

describe('[Migrations]', () => {
    const run = (cmd, args = []) => {
    const process = spawn(cmd, args)
    let out = ""
    return new Bluebird((resolve, reject) => {
       process.stdout.on('data', data => {
         out += data.toString('utf8')
       })
      process.stderr.on('data', data => {
        out += data.toString('utf8')
      })
      process.on('error', err => {
         reject(err)
      })
     process.on('close', code => {
      resolve(out, code)
     })
   })
 }
    
const TMP_DIR = path.join(__dirname, '..', '..', 'tmp')
const INIT = path.join(__dirname, '..', '..', 'node_modules/migrate/bin', 'migrate-init')
const init = run.bind(null, INIT)
const reset = () => {
   rimraf.sync(TMP_DIR)
   rimraf.sync(path.join(__dirname, '..', '..', '.migrate'))
}

beforeEach(reset)
afterEach(reset)
describe('init', () => {
   beforeEach(mkdirp.bind(mkdirp, TMP_DIR))

   it('should create a migrations directory', done => {
      init()
      .then(() => fs.accessSync(path.join(TMP_DIR, '..', 'migrations')))
      .then(() => done())
      .catch(done)
   })
 })
})

В приведенном выше тесте мы используем Миграция-init Команда для создания каталога миграции и удаление его после каждого тестового корпуса, используя Римраф который является RM -RF в Unix.

Позже мы используем Fscaccesssync Функция для проверки миграции папка существует или нет.

Тестовое задание – Миграция

Цель: Это должно добавить фамилия в схему и хранить миграцию.

Добавьте следующий код в Up-test.js файл:

const chance = require('chance')()
const generateUser = () => ({
   email: chance.email(),
   name: `${chance.first()} ${chance.last()}`
 })
const migratePath = path.join(__dirname, '..', '..', 'node_modules/migrate/bin', 'migrate')
const migrate = run.bind(null, migratePath)

describe('[Migration: up]', () => {
   before(done => {
     MongoClient
     .connect(url)
     .then(client => {
       db = client.db()
      return db.collection('users').insert(generateUser())
      })
      .then(result => {
       if (!result) throw new Error('Failed to insert')
       return done()
      }).catch(done)
   })
   it('should run up on specified migration', done => {
     migrate(['up', 'mention here the file name we created above', '--store=./db-migrate-store.js'])
    .then(() => {
       const promises = []
       promises.push(
        db.collection('users').find().toArray()
       )
     Bluebird.all(promises)
    .then(([users]) => {
       users.forEach(elem => {
         expect(elem).to.have.property('lastName')
      })
      done()
    })
   }).catch(done)
 })
after(done => {
    rimraf.sync(path.join(__dirname, '..', '..', '.migrate'))
    db.collection('users').deleteMany()
    .then(() => {
      rimraf.sync(path.join(__dirname, '..', '..', '.migrate'))
      return done()
   }).catch(done)
 })
})

Точно так же вы можете записать миграцию и до () и после () Функции остаются в основном одинаковыми.

Заключение

Надеюсь, вы теперь можете автоматизировать свои изменения схемы с правильным тестированием.:)

Возьмите последний код из Репозиторий Отказ

Не стесняйтесь хлопать, если вы считаете это стоящим чтением!

Оригинал: “https://www.freecodecamp.org/news/how-to-automate-database-migrations-in-mongodb-d6b68efe084e/”