MySQL 到 MongoDB 将整数 ID 转换为 ObjectID 并重建引用
MySQL to MongoDB convert integer IDs to ObjectIDs and rebuild references
我正在现有数据库上构建一个新的 NodeJS 应用程序。我已将现有数据库从 MySQL 转换为 MongoDB。我用MySQLWorkbench把JSON里的sql数据导出来,然后用mongorestore把数据恢复到MongoDB。这行得通。
现有的 MySQL 数据库使用自动增量 属性 为主键生成整数 ID 号。
例如,"people" table 有一个主键 "PeopleID",一个从 0 开始到大约三位数的整数,例如 1、12 或 123。
许多其他 table 使用相同的技术。 "Location" table 有一个相同格式的 "LocationID" 自动递增。
在相关的table中,主键存储为外键,就像标准关系数据库一样。
这是 MongoDB 中新导入的文档。 Mongo 为每个文档生成一个 _id。
{
"_id": "5ce89632c15df953bbe163e1", // newly created MongoDB ObjectID
"PersonID": 1, // old primary key
"LocationID": 12, // old foreign key
"FirstName": "John",
"MiddleName": "",
"LastName": "Smith"
}
我想使用 ObjectID 而不是自动递增的整数来重建所有引用。因此,理想情况下,新文档应如下所示,将 "foreign key" 作为 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 的引用而不是旧的整数值以编程方式重建关系?
通过在 Mongoose 中编写一组查询,我能够基于整数外键构建引用。第一步是构建一个字典数组以将每个遗留外键映射到它的 ObjectID。下一步是遍历目标集合中的每个文档,对于每个项目,在之前创建的 idmap
中查找 ObjectID。最后一步是使用新的 ObjectID 更新目标文档。
在本例中,我创建了 LocationID 到 ObjectID 的映射,并使用新 Location_id 更新了 People 集合。我将其设置为在路由器中作为 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;
我正在现有数据库上构建一个新的 NodeJS 应用程序。我已将现有数据库从 MySQL 转换为 MongoDB。我用MySQLWorkbench把JSON里的sql数据导出来,然后用mongorestore把数据恢复到MongoDB。这行得通。
现有的 MySQL 数据库使用自动增量 属性 为主键生成整数 ID 号。
例如,"people" table 有一个主键 "PeopleID",一个从 0 开始到大约三位数的整数,例如 1、12 或 123。
许多其他 table 使用相同的技术。 "Location" table 有一个相同格式的 "LocationID" 自动递增。
在相关的table中,主键存储为外键,就像标准关系数据库一样。
这是 MongoDB 中新导入的文档。 Mongo 为每个文档生成一个 _id。
{
"_id": "5ce89632c15df953bbe163e1", // newly created MongoDB ObjectID
"PersonID": 1, // old primary key
"LocationID": 12, // old foreign key
"FirstName": "John",
"MiddleName": "",
"LastName": "Smith"
}
我想使用 ObjectID 而不是自动递增的整数来重建所有引用。因此,理想情况下,新文档应如下所示,将 "foreign key" 作为 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 的引用而不是旧的整数值以编程方式重建关系?
通过在 Mongoose 中编写一组查询,我能够基于整数外键构建引用。第一步是构建一个字典数组以将每个遗留外键映射到它的 ObjectID。下一步是遍历目标集合中的每个文档,对于每个项目,在之前创建的 idmap
中查找 ObjectID。最后一步是使用新的 ObjectID 更新目标文档。
在本例中,我创建了 LocationID 到 ObjectID 的映射,并使用新 Location_id 更新了 People 集合。我将其设置为在路由器中作为 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;