Node.js无法插入MongoDB特定JSON对象的问题求助
我写了一段Node.js代码,用来读取指定文件夹里的文件,把文件中存储的MongoDB文档插入到数据库中,但运行时出现了报错。
我的代码如下:
var fs = require("fs") var MongoClient = require('mongodb').MongoClient; var url = "mongodb://localhost:27017/"; MongoClient.connect(url, function(err, db) { if (err) throw err; var dbo = db.db("mydb"); const testFolder = 'dbbackups'; fs.readdir(testFolder, (err, files) => { files.forEach(file => { fs.readFile(testFolder+"/"+file, 'utf8', function (err,data) { if (err) { return console.log(err); } console.log("************************************************"); console.log(file); console.log(data); console.log("************************************************"); var dbo = db.db("mydb"); var myobj = data; dbo.collection(file.slice(0, -4)).insertOne(myobj, function(err, res) { if (err) throw err; console.log("1 document inserted"); db.close(); }); }); }); }); });
文件中的MongoDB文档示例:
{ "_id" : ObjectId("5a8be310e08d2d9fc276a3ac"), "name" : "facener", "firstname" : "facener", "lastname" : "facener", "username" : "facener", "password" : "$2a$10$nWPLXb4p5wEWvk3g.YRdfOw7d1kAvPuvd45P.1AEaJyv9E0TtZfAe", "multi_login" : true, "phoneNumber" : { "verified" : false, "type" : "phone", "contact" : "077441234567" }, "email" : { "verified" : true, "type" : "phone", "contact" : "facener" }, "user_meta" : { "role" : "superadmin" }, "systemuser" : true, "company" : 1, "tenant" : 1, "client_scopes" : [ { "consoleName" : "OPERATOR", "menus" : [ { "menuItem" : "TENANTMONITOR", "menuAction" : [ { "scope" : "tenant", "read" : true, "write" : true, "delete" : true } ] } ] } ], "user_scopes" : [], "contacts" : [], "__v" : 0, "birthday" : ISODate("1989-12-24T00:00:00.000Z"), "gender" : "male", "avatar" : "", "address" : { "number" : "77", "street" : "7 Road", "city" : "7 03", "province" : "pp", "country" : "usa", "zipcode" : "00300" }, "verified" : true, "group" : ObjectId("58466e3e9e5db600019768bb"), "locale" : "en", "app_meta" : { "subject" : "test2", "priority" : "urgent", "description" : "test2" }, "Active" : true, "security_level" : 1 }
运行时的报错信息:
if(docs[i]._id == null) docs[i]._id = self.s.pkFactory.createPk(); ^ TypeError: Cannot create property '_id' on string '{ "_id" : ObjectId("590091708ef913c5096e5494"),
我知道问题出在文件内容里包含了MongoDB的特定对象(比如ObjectId、ISODate),但不知道该怎么解决。想请教下这类场景的最优处理方式是什么?Node.js里有没有方法可以直接使用MongoDB兼容的JSON对象?
解决方案
首先得明确:你现在的核心问题是把文件里的字符串直接传给了insertOne,MongoDB的驱动没法把包含ObjectId、ISODate这类特殊BSON类型的字符串直接转换成对应的对象,所以才会报错说不能在字符串上创建_id属性。下面给你几个不同场景的解决方案:
1. 快速临时方案:用eval(不推荐生产用)
如果只是临时跑个脚本,而且能保证文件内容绝对安全,可以用eval把字符串解析成JS对象——因为这些ObjectId()、ISODate()本质上是MongoDB的构造函数调用,eval能直接执行它们:
// 替换原来的 var myobj = data; const myobj = eval(`(${data})`);
⚠️ 注意:这个方法非常危险,如果文件里有恶意代码,eval会直接执行,所以绝对不要在生产环境或者处理不可信文件时用。
2. 推荐脚本方案:使用mongodb-extended-json库
MongoDB官方有个专门处理扩展JSON的库,能完美解析包含ObjectId、ISODate这类特殊类型的字符串,转换成驱动能识别的BSON对象。
步骤如下:
- 先安装依赖:
npm install mongodb-extended-json
- 修改你的代码:
const fs = require("fs") const MongoClient = require('mongodb').MongoClient; const EJSON = require('mongodb-extended-json'); // 引入库 const url = "mongodb://localhost:27017/"; MongoClient.connect(url, function(err, db) { if (err) throw err; const dbo = db.db("mydb"); const testFolder = 'dbbackups'; fs.readdir(testFolder, (err, files) => { // 用计数器跟踪所有文件处理完成,避免提前关闭数据库 let completed = 0; const totalFiles = files.length; files.forEach(file => { fs.readFile(`${testFolder}/${file}`, 'utf8', function (err,data) { if (err) { console.log(err); completed++; if (completed === totalFiles) db.close(); return; } console.log("************************************************"); console.log(file); console.log(data); console.log("************************************************"); // 解析扩展JSON const myobj = EJSON.parse(data); dbo.collection(file.slice(0, -4)).insertOne(myobj, function(err, res) { if (err) throw err; console.log("1 document inserted"); completed++; // 所有文件处理完再关闭数据库 if (completed === totalFiles) db.close(); }); }); }); }); });
另外还要注意:你原来的代码在insertOne回调里直接调用db.close(),这会导致第一个文件插入完成就关闭数据库,后面的文件都会报错。上面的代码加了计数器,所有文件处理完才关闭连接,解决了这个问题。
3. 最优生产方案:用官方mongorestore工具
如果你的场景是备份恢复,那完全没必要自己写代码——MongoDB官方提供的mongorestore工具就是专门干这个的,它能自动识别所有BSON特殊类型,效率更高,稳定性也更强。
只需要在命令行里运行:
mongorestore --db mydb dbbackups/
这里dbbackups是你存放备份文件的文件夹,工具会自动把每个文件(去掉后缀)对应到集合,把文件里的内容插入到对应的集合中,全程不用写一行代码。
总结一下:如果是临时脚本,用mongodb-extended-json;如果是生产环境的备份恢复,直接用mongorestore是最优解,省心又靠谱。
内容的提问来源于stack exchange,提问作者gamage

