MySQL в MongoDB преобразует целочисленные идентификаторы в идентификаторы объектов и перестраивает ссылки - PullRequest
0 голосов
/ 24 мая 2019

Я создаю новое приложение NodeJS на основе существующей базы данных.Я преобразовал существующую базу данных из MySQL в MongoDB.Я использовал MySQL Workbench для экспорта данных sql в JSON, а затем использовал mongorestore для восстановления данных в MongoDB.Это работает.

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

Например, таблица "people" имеет первичный ключ "PeopleID",целое число от 0 до примерно трех цифр, например, 1, 12 или 123.

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

В связанных таблицах первичный ключ хранится как внешний ключ, так же, как стандартная реляционная база данных.

Вот недавно импортированный документ в MongoDB.Монго сгенерировал _id для каждого документа.

{
    "_id": "5ce89632c15df953bbe163e1", // newly created MongoDB ObjectID
    "PersonID": 1, // old primary key
    "LocationID": 12, // old foreign key
    "FirstName": "John",
    "MiddleName": "",
    "LastName": "Smith"
}

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

{
    "_id": "5ce89632c15df953bbe163e1", // Use this as the reference instead of the old "PersonID"
    "PersonID": 1, // old primary key
    "LocationID": "5ce8358ec15df953bab163ea", // example ObjectID as a reference
    "FirstName": "John",
    "MiddleName": "",
    "LastName": "Smith"
}

Можно ли программно перестроить отношения, используя ссылки с ObjectID вместо старых целочисленных значений?

1 Ответ

1 голос
/ 27 мая 2019

Мне удалось построить ссылки на основе целочисленных внешних ключей, написав набор запросов в Mongoose. Первым шагом является создание массива словарей для сопоставления каждого устаревшего внешнего ключа с его ObjectID. Следующим шагом является перебор каждого документа в целевой коллекции, для каждого элемента ищите ObjectID в idmap, созданном ранее. Последний шаг - обновить целевой документ новым ObjectID.

В этом случае я создал карту LocationIDs для ObjectID и обновил коллекцию People новым Location_id. Я настроил его для работы в качестве запроса GET в маршрутизаторе. Это быстрое решение, которое работает только для одного сопоставления за раз. Когда у меня есть шанс, я могу сделать его более масштабируемым и параметризованным и, возможно, даже обернуть его в модуль. Дайте мне знать, если кто-то считает, что это полезное начало. Я буду продолжать использовать его по мере необходимости для миграции с MySQL на MongoDB.

const express = require('express');
const router = express.Router();

/**
 * *** INSTRUCTIONS ***
 *
 * Convert a MySQL relationship to a NoSQL reference
 *
 * People table is the target table that contains the relational primary key PersonID
 * LocationID is a relational foreign key stored as an integer
 * Iterate all documents in People collection
 * Match LocationID integer in the Locations collection to get its unique ObjectID
 * Store the retrieved ObjectID as Location_id in People collection

    MySQL Relationship to convert

    PersonID: {
      type: DataTypes.INTEGER(10).UNSIGNED,
      allowNull: false,
      primaryKey: true,
      autoIncrement: true,
      unique: true
    },
    LocationID: {
      type: DataTypes.INTEGER(10).UNSIGNED,
      allowNull: true,
      references: {
        model: 'locations',
        key: 'LocationID'
      }

    MongoDB Reference to create

    // Relational primary key autoIncrement ID
    PersonID: {
      type: Number,
      index: true,
      unique: true
    },
    // Relational foreign key autoIncrement ID
    LocationID: {
      type: Number
    },
    // ObjectID reference
    Location_id: {
      type: Schema.Types.ObjectId,
      ref: 'Locations'
    }

 * People
 * Primary Key table = people
 * Foreign Key table = locations
 * Foreign Key field to convert = LocationID
 * New Foreign Key field  = Location_id
 *
 */

// Perform update if true if false read records and log only
const pktable = require('../../models/people');
const fktable = require('../../models/locations');
const prvfkfield = 'LocationID';
const newfkfield = 'Location_id';

router.get('/_id', (req, res, next) => {
  const origin = 'routes/migrate_id.js GET /';

  // Build a dictionary to map old Foreign Key integer to its ObjectID
  async function migrate() {

    // Initialize array to store map of Foreign Keys to ObjectIDs
    let idmap = [];
    // Initialize integer to store number of targets
    let targetcount = 0;
    // Initialize integer to store number of successful results
    let successcount = 0;
    // Initialize integer to store number of skipped results
    let skippedcount = 0;

    // Create a cursor on Foreign Key table
    const fkcursor = fktable.find({}, {[prvfkfield]: 1}).cursor();

    // Build a map of Foreign Keys to ObjectIDs
    await fkcursor.eachAsync(async function (id) {
      idmap.push(id.toObject());
    });

    // Create a cursor on Primary Key table
    const pkcursor = pktable.find().cursor();

    // Iterate each item in cursor and return target record to update
    await pkcursor.eachAsync(async function (target) {

      // Get Previous Foreign Key
      const prvfk = target[prvfkfield];

      targetcount = targetcount + 1;

      // Get ObjectID mapped to the Previous Foriegn Key field
      let objectid = idmap.find(item => item[prvfkfield] === prvfk);

      if (objectid) {
        // Set ObjectID on target document
        target[newfkfield] = objectid;

        try {
          await target.save();
          successcount = successcount + 1;
        } catch (saveerror) {
          console.error(`ERROR: ${JSON.stringify(saveerror)}`);
        }

      } else {
        skippedcount = skippedcount + 1;
      }

    });

    const result = {
      'idmapcount': idmap.length,
      'targetcount': targetcount,
      'successcount': successcount,
      'skippedcount': skippedcount
    };

    return result;

  }

  migrate().then((result) => {
    res.status(200).send(result);
  }).catch((error) => {
    console.error(`migrate failed ${error}`);
    res.status(500).send(`migrate failed ${error}`);
  });

});

module.exports = router;
...