Node JS 在单个 'transaction' 中将多条记录保存到 Mongo
Node JS Save Multiple Records to Mongo in single 'transaction'
你能告诉我解决我面临的问题的最佳方法吗?我知道给猫剥皮的方法有很多种。我尝试了很多不同的方法,但似乎找不到解决方案。
总结:
我的节点 js 应用程序有一个获取请求。当用户点击这个 GET URL 时,我希望它获取该用户的记录,但是,如果有 none,我想根据 [= 为该用户创建一组记录53=] 列表。 (我需要类似 factory 的东西来创建批次或记录并在完成后给出结果。)
创建单个记录有效。但我需要创建 4 个。如果我尝试创建多个,我会得到 ...
UnhandledPromiseRejectionWarning: Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the client 或者只是一个简单的错误,这取决于我的变体使用。
基本上,一旦您创建了 1 条以上的记录,该记录的创建 returns 一个响应 - 我想 'combine' 完成后将组合结果发回。
我的模型很简单。它看起来像这样:
/*____ _ _ __ __ _ _
| _ \ | | | | | \/ | | | | |
| |_) |_ _ ___| | _____| |_ | \ / | ___ __| | ___| |
| _ <| | | |/ __| |/ / _ \ __| | |\/| |/ _ \ / _` |/ _ \ |
| |_) | |_| | (__| < __/ |_ | | | | (_) | (_| | __/ |
|____/ \__,_|\___|_|\_\___|\__| |_| |_|\___/ \__,_|\___|_|
*/
const mongoose = require('mongoose');
const dbSchema = mongoose.Schema;
const bucketSchema = new dbSchema({
name: {
type: String,
required: true,
unique: false
},
createdby: {
type: dbSchema.Types.ObjectId,
ref: 'user'
},
createdon: {
type: Date,
default: Date.now
},
isforarchive: {
type: Boolean,
required: true,
default: false
},
cards: [
{
card: {
type: dbSchema.Types.ObjectId,
ref: 'card'
}
}
]
});
module.exports = bucket = mongoose.model('bucket', bucketSchema);
当我创建单个记录时,此代码有效。但是,我需要为用户创建 4 个存储桶:- ToDo、Busy、Done 和 Archive。
下面的代码可以工作,但只能创建一个桶 - ToDo。
const ToDo = new Bucket({
name: 'To-Do',
createdby: null,
createdon: Date.now(),
isforarchive: false,
cards: [ {} ]
});
// @route get:/api/tms/buckets
// @desc Gets user buckets or creates a set of buckets for the user if they do not exist.
// @access Private
router.get(
'/',
passport.authenticate('jwt', {
session: false
}),
(req, res) => {
console.log('About to search buckets...');
Bucket.find({ createdby: req.user.id })
.then((found) => {
console.log('Checking if found');
console.log(`Length ${found.length}`); // Zero when none found
if (found.length > 0) {
res.json(found);
} else {
console.log('Creating ToDo');
ToDo.createdby = req.user.id;
ToDo.save().then((saved) => res.json(saved));
}
})
.catch((err) => {
const errors = {};
errors.getbuckets = 'Encountered a problem getting buckets.';
errors.error = err;
res.status(400).json(errors);
});
}
);
为了尝试帮助我,我创建了一个助手 class ./buckethelper.js 来帮助我。我已经尝试过 promises 等,但无法解决创建第一个发送导致上述错误的响应的记录的问题。
我在助手 class 中尝试了很多不同的选项,但我无法开始工作。我尝试解决许多问题,其中之一是我希望代码以同步和串行方式 运行,而不是并行。
这是我的帮手class。
const Bucket = require('./model');
/*
//PLEASE NOTE THAT THIS SECTION IS COMMENTED OUT.
function CreateBucket(Name, CreatedBy, CreatedOn, IsForArchive, Cards) {
this.name = Name;
this.createdby = CreatedBy;
this.createdon = CreatedOn;
this.isforarchive = IsForArchive;
this.cards = Cards;
}
const Buckets = [];
Buckets.push(new CreateBucket('To-Do', '', Date.now(), false, [ {} ]));
Buckets.push(new CreateBucket('Busy', '', Date.now(), false, [ {} ]));
Buckets.push(new CreateBucket('Done', '', Date.now(), false, [ {} ]));
Buckets.push(new CreateBucket('Archive', '', Date.now(), true, [ {} ]));
*/
const ToDo = new Bucket({
name: 'To-Do',
createdby: null,
createdon: Date.now(),
isforarchive: false,
cards: [ {} ]
});
const Busy = new Bucket({
name: 'Busy',
createdby: null,
createdon: Date.now(),
isforarchive: false,
cards: [ {} ]
});
const Done = new Bucket({
name: 'Done',
createdby: null,
createdon: Date.now(),
isforarchive: false,
cards: [ {} ]
});
const Archive = new Bucket({
name: 'Archive',
createdby: null,
createdon: Date.now(),
isforarchive: true,
cards: [ {} ]
});
var result = [];
function fnSaveToDo(UserId) {
console.log(`Saving To Do ...`);
ToDo.createdby = UserId;
result.push(Bucket.save(ToDo));
}
function fnSaveBusy(UserId) {
Busy.createdby = UserId;
result.push(Bucket.insert(Busy));
}
function fnSaveDone(UserId) {
Done.createdby = UserId;
result.push(Bucket.insert(Done));
}
function fnSaveArchive(UserId) {
Archive.createdby = UserId;
result.push(Bucket.insert(Archive));
return result;//I want to return the combined results
}
//Synchronous call stack that execute in series not parallel
function CreateBuckets(UserId, fnSaveToDo, fnSaveBusy, fnSaveDone, fnSaveArchive) {
console.log('Started the saving process ...');
fnSaveToDo(UserId);
fnSaveBusy(UserId);
fnSaveDone(UserId);
fnSaveArchive(UserId);
}
CreateBuckets(
function() {
console.log('User id callback?');
},
function() {
console.log('ToDo saved ...');
},
function() {
console.log('Busy saved ...');
},
function() {
console.log('Done saved ...');
},
function() {
console.log('Archive saved ...');
}
);
module.exports = CreateBuckets;
调用我的助手的代码看起来像这样,它是上面代码的一个子集;
Bucket.find({ createdby: req.user.id })
.then((found) => {
console.log('Checking if found');
console.log(`Length ${found.length}`); // Zero when none found
if (found.length > 0) {
res.json(found);
} else {
//console.log('Creating ToDo');
res.json(CreateBuckets(req.user.id,fnSaveToDo,fnSaveBusy, fnSaveDone, fnSaveArchive));
// ToDo.createdby = req.user.id;
// ToDo.save().then((saved) => res.json(saved));
}
})
.catch((err) => {
const errors = {};
errors.getbuckets = 'Encountered a problem getting buckets.';
errors.error = err;
res.status(400).json(errors);
});
我希望我对问题的描述已经清楚了。
如果或当我找到解决方案时,我会post。已经做了几天了,没有任何成功。
亲切的问候
克雷格
您可以在此处使用 insertMany
方法进行批量插入并在您的操作中利用 async/await,因为方法 returns 是一个承诺.
例如,您的路线可以重写为
router.get('/', passport.authenticate('jwt', {session: false }), async (req, res) => {
try {
console.log('About to search buckets...');
const found = await Bucket.find({ createdby: req.user.id })
console.log('Checking if found');
console.log(`Length ${found.length}`); // Zero when none found
if (found.length > 0) {
res.json(found);
} else {
console.log('Creating ToDos');
const todos = ['To-Do', 'Busy', 'Done', 'Archive'].map(name => (
{
name,
createdby: req.user.id,
createdon: Date.now(),
isforarchive: (name == 'Archive'),
cards: [ {} ]
}
));
const saved = await ToDo.insertMany(todos);
res.json(saved);
}
} catch (err) {
const errors = {};
errors.getbuckets = 'Encountered a problem getting buckets.';
errors.error = err;
res.status(400).json(errors);
}
});
感谢您的解决方案。但是我无法让 await 工作。这是我的解决方案。我会重构它,但它有效。
const DefaultBuckets = [ 'To Do', 'Busy', 'Done', 'Archive' ];
// @route get:/api/tms/buckets
// @desc Gets user buckets or creates a set of buckets for the user if they do not exist.
// @access Private
router.get(
'/',
passport.authenticate('jwt', {
session: false
}),
(req, res) => {
console.log('About to search buckets...');
Bucket.find({ createdby: req.user.id })
.then((found) => {
console.log('Checking if found');
console.log(`Length ${found.length}`); // Zero when none found
if (found.length > 0) {
console.log('returning what is in db');
res.json(found);
} else {
// console.log('Creating buckets ...');
const defaults = DefaultBuckets.map((name) => ({
name,
createdby: req.user.id,
createdon: Date.now(),
isforarchive: name == 'Archive',
cards: [ {} ]
}));
Bucket.insertMany(defaults, (err, docs) => {
let errors = {};
// Docs are the saved records from the db
if (docs) {
res.json(docs);
}else{
errors.message = 'Could not create buckets';
errors.InsertMany = err;
res.status(400).json(errors);
}
});
}
})
.catch((err) => {
const errors = {};
errors.getbuckets = 'Encountered a problem getting buckets.';
errors.error = err;
res.status(400).json(errors);
});
}
);
你能告诉我解决我面临的问题的最佳方法吗?我知道给猫剥皮的方法有很多种。我尝试了很多不同的方法,但似乎找不到解决方案。
总结:
我的节点 js 应用程序有一个获取请求。当用户点击这个 GET URL 时,我希望它获取该用户的记录,但是,如果有 none,我想根据 [= 为该用户创建一组记录53=] 列表。 (我需要类似 factory 的东西来创建批次或记录并在完成后给出结果。)
创建单个记录有效。但我需要创建 4 个。如果我尝试创建多个,我会得到 ...
UnhandledPromiseRejectionWarning: Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the client 或者只是一个简单的错误,这取决于我的变体使用。
基本上,一旦您创建了 1 条以上的记录,该记录的创建 returns 一个响应 - 我想 'combine' 完成后将组合结果发回。
我的模型很简单。它看起来像这样:
/*____ _ _ __ __ _ _
| _ \ | | | | | \/ | | | | |
| |_) |_ _ ___| | _____| |_ | \ / | ___ __| | ___| |
| _ <| | | |/ __| |/ / _ \ __| | |\/| |/ _ \ / _` |/ _ \ |
| |_) | |_| | (__| < __/ |_ | | | | (_) | (_| | __/ |
|____/ \__,_|\___|_|\_\___|\__| |_| |_|\___/ \__,_|\___|_|
*/
const mongoose = require('mongoose');
const dbSchema = mongoose.Schema;
const bucketSchema = new dbSchema({
name: {
type: String,
required: true,
unique: false
},
createdby: {
type: dbSchema.Types.ObjectId,
ref: 'user'
},
createdon: {
type: Date,
default: Date.now
},
isforarchive: {
type: Boolean,
required: true,
default: false
},
cards: [
{
card: {
type: dbSchema.Types.ObjectId,
ref: 'card'
}
}
]
});
module.exports = bucket = mongoose.model('bucket', bucketSchema);
当我创建单个记录时,此代码有效。但是,我需要为用户创建 4 个存储桶:- ToDo、Busy、Done 和 Archive。
下面的代码可以工作,但只能创建一个桶 - ToDo。
const ToDo = new Bucket({
name: 'To-Do',
createdby: null,
createdon: Date.now(),
isforarchive: false,
cards: [ {} ]
});
// @route get:/api/tms/buckets
// @desc Gets user buckets or creates a set of buckets for the user if they do not exist.
// @access Private
router.get(
'/',
passport.authenticate('jwt', {
session: false
}),
(req, res) => {
console.log('About to search buckets...');
Bucket.find({ createdby: req.user.id })
.then((found) => {
console.log('Checking if found');
console.log(`Length ${found.length}`); // Zero when none found
if (found.length > 0) {
res.json(found);
} else {
console.log('Creating ToDo');
ToDo.createdby = req.user.id;
ToDo.save().then((saved) => res.json(saved));
}
})
.catch((err) => {
const errors = {};
errors.getbuckets = 'Encountered a problem getting buckets.';
errors.error = err;
res.status(400).json(errors);
});
}
);
为了尝试帮助我,我创建了一个助手 class ./buckethelper.js 来帮助我。我已经尝试过 promises 等,但无法解决创建第一个发送导致上述错误的响应的记录的问题。
我在助手 class 中尝试了很多不同的选项,但我无法开始工作。我尝试解决许多问题,其中之一是我希望代码以同步和串行方式 运行,而不是并行。
这是我的帮手class。
const Bucket = require('./model');
/*
//PLEASE NOTE THAT THIS SECTION IS COMMENTED OUT.
function CreateBucket(Name, CreatedBy, CreatedOn, IsForArchive, Cards) {
this.name = Name;
this.createdby = CreatedBy;
this.createdon = CreatedOn;
this.isforarchive = IsForArchive;
this.cards = Cards;
}
const Buckets = [];
Buckets.push(new CreateBucket('To-Do', '', Date.now(), false, [ {} ]));
Buckets.push(new CreateBucket('Busy', '', Date.now(), false, [ {} ]));
Buckets.push(new CreateBucket('Done', '', Date.now(), false, [ {} ]));
Buckets.push(new CreateBucket('Archive', '', Date.now(), true, [ {} ]));
*/
const ToDo = new Bucket({
name: 'To-Do',
createdby: null,
createdon: Date.now(),
isforarchive: false,
cards: [ {} ]
});
const Busy = new Bucket({
name: 'Busy',
createdby: null,
createdon: Date.now(),
isforarchive: false,
cards: [ {} ]
});
const Done = new Bucket({
name: 'Done',
createdby: null,
createdon: Date.now(),
isforarchive: false,
cards: [ {} ]
});
const Archive = new Bucket({
name: 'Archive',
createdby: null,
createdon: Date.now(),
isforarchive: true,
cards: [ {} ]
});
var result = [];
function fnSaveToDo(UserId) {
console.log(`Saving To Do ...`);
ToDo.createdby = UserId;
result.push(Bucket.save(ToDo));
}
function fnSaveBusy(UserId) {
Busy.createdby = UserId;
result.push(Bucket.insert(Busy));
}
function fnSaveDone(UserId) {
Done.createdby = UserId;
result.push(Bucket.insert(Done));
}
function fnSaveArchive(UserId) {
Archive.createdby = UserId;
result.push(Bucket.insert(Archive));
return result;//I want to return the combined results
}
//Synchronous call stack that execute in series not parallel
function CreateBuckets(UserId, fnSaveToDo, fnSaveBusy, fnSaveDone, fnSaveArchive) {
console.log('Started the saving process ...');
fnSaveToDo(UserId);
fnSaveBusy(UserId);
fnSaveDone(UserId);
fnSaveArchive(UserId);
}
CreateBuckets(
function() {
console.log('User id callback?');
},
function() {
console.log('ToDo saved ...');
},
function() {
console.log('Busy saved ...');
},
function() {
console.log('Done saved ...');
},
function() {
console.log('Archive saved ...');
}
);
module.exports = CreateBuckets;
调用我的助手的代码看起来像这样,它是上面代码的一个子集;
Bucket.find({ createdby: req.user.id })
.then((found) => {
console.log('Checking if found');
console.log(`Length ${found.length}`); // Zero when none found
if (found.length > 0) {
res.json(found);
} else {
//console.log('Creating ToDo');
res.json(CreateBuckets(req.user.id,fnSaveToDo,fnSaveBusy, fnSaveDone, fnSaveArchive));
// ToDo.createdby = req.user.id;
// ToDo.save().then((saved) => res.json(saved));
}
})
.catch((err) => {
const errors = {};
errors.getbuckets = 'Encountered a problem getting buckets.';
errors.error = err;
res.status(400).json(errors);
});
我希望我对问题的描述已经清楚了。
如果或当我找到解决方案时,我会post。已经做了几天了,没有任何成功。
亲切的问候 克雷格
您可以在此处使用 insertMany
方法进行批量插入并在您的操作中利用 async/await,因为方法 returns 是一个承诺.
例如,您的路线可以重写为
router.get('/', passport.authenticate('jwt', {session: false }), async (req, res) => {
try {
console.log('About to search buckets...');
const found = await Bucket.find({ createdby: req.user.id })
console.log('Checking if found');
console.log(`Length ${found.length}`); // Zero when none found
if (found.length > 0) {
res.json(found);
} else {
console.log('Creating ToDos');
const todos = ['To-Do', 'Busy', 'Done', 'Archive'].map(name => (
{
name,
createdby: req.user.id,
createdon: Date.now(),
isforarchive: (name == 'Archive'),
cards: [ {} ]
}
));
const saved = await ToDo.insertMany(todos);
res.json(saved);
}
} catch (err) {
const errors = {};
errors.getbuckets = 'Encountered a problem getting buckets.';
errors.error = err;
res.status(400).json(errors);
}
});
感谢您的解决方案。但是我无法让 await 工作。这是我的解决方案。我会重构它,但它有效。
const DefaultBuckets = [ 'To Do', 'Busy', 'Done', 'Archive' ];
// @route get:/api/tms/buckets
// @desc Gets user buckets or creates a set of buckets for the user if they do not exist.
// @access Private
router.get(
'/',
passport.authenticate('jwt', {
session: false
}),
(req, res) => {
console.log('About to search buckets...');
Bucket.find({ createdby: req.user.id })
.then((found) => {
console.log('Checking if found');
console.log(`Length ${found.length}`); // Zero when none found
if (found.length > 0) {
console.log('returning what is in db');
res.json(found);
} else {
// console.log('Creating buckets ...');
const defaults = DefaultBuckets.map((name) => ({
name,
createdby: req.user.id,
createdon: Date.now(),
isforarchive: name == 'Archive',
cards: [ {} ]
}));
Bucket.insertMany(defaults, (err, docs) => {
let errors = {};
// Docs are the saved records from the db
if (docs) {
res.json(docs);
}else{
errors.message = 'Could not create buckets';
errors.InsertMany = err;
res.status(400).json(errors);
}
});
}
})
.catch((err) => {
const errors = {};
errors.getbuckets = 'Encountered a problem getting buckets.';
errors.error = err;
res.status(400).json(errors);
});
}
);