MongoDB mongoose 子文档创建了两次
Posted
技术标签:
【中文标题】MongoDB mongoose 子文档创建了两次【英文标题】:MongoDB mongoose subdocuments created twice 【发布时间】:2017-03-15 13:07:12 【问题描述】:我正在使用一个简单的表格,可用于将文章注册到网站。
后端如下所示:
// Post new article
app.post("/articles", function(req, res)
var newArticle = ;
newArticle.title = req.body.title;
newArticle.description = req.body.description;
var date = req.body.date;
var split = date.split("/");
newArticle.date = split[1]+'/'+split[0]+'/'+split[2];
newArticle.link = req.body.link;
newArticle.body = req.body.body;
var platforms = req.body.platforms;
console.log(platforms);
Article.create(newArticle, function(err, createdArticle)
if(err)
console.log(err.message);
else
var counter=0;
platforms.forEach(function(platform)
var platformed=mongoose.mongo.ObjectID(platform);
Platform.findById(platformed, function(err, foundPlatform)
if(err)
console.log(err);
else
counter++;
foundPlatform.articles.push(createdArticle);
foundPlatform.save();
createdArticle.platforms.push(foundPlatform);
createdArticle.save();
if(counter==platforms.length)
res.redirect('articles/' + createdArticle._id);
);
);
);
);
platforms 字段作为字符串数组传递到后端,一个字符串是一个 objectID。当平台仅包含 1 个字符串(即要链接到的 1 个平台)时,一切正常。当平台包含多个字符串时。创建的文章具有每个平台的副本。或者有时只是某些平台的副本
有什么想法吗?
更新 1: 文章架构: var mongoose = require("mongoose");
var articleSchema = new mongoose.Schema(
title : String,
description : String,
link : String,
date : String,
body : String,
platforms : [
type: mongoose.Schema.Types.ObjectId,
ref: "Platform"
]
)
module.exports = mongoose.model("Article", articleSchema);
平台架构:
var mongoose = require("mongoose");
var platformSchema = new mongoose.Schema(
name : String,
category : String,
contacts : [
type: mongoose.Schema.Types.ObjectId,
ref: "Contact"
],
website : String,
country : String,
contactInformation : String,
businessModelNotes : String,
source : String,
generalNotes : String,
projects : [
type: mongoose.Schema.Types.ObjectId,
ref: "Project"
],
articles : [
type: mongoose.Schema.Types.ObjectId,
ref: "Article"
],
privacy : String,
comments : [
type: mongoose.Schema.Types.ObjectId,
ref: "Comment"
]
);
module.exports = mongoose.model("Platform", platformSchema);
【问题讨论】:
您能否edit 向我们展示Article
和Platform
模型的架构定义?我可以看到你在一个循环中进行异步调用,这可能解释了为什么你会得到重复。
更新了! @chridam
【参考方案1】:
移动你的保存功能
if(counter==platforms.length)
createdArticle.save(function(err, savedObject)
if(err || !savedObject) console.log(err || "not saved");
else
res.redirect('articles/' + savedObject._id.toString());
);
============= 编辑
这是因为您只需调用一次 article.save,而不是在每个循环中。此外,您使用 save() 作为同步函数,但它是异步的。
我认为你应该直接使用更新功能:
else
var counter=0;
// map plateform array id with ObjectID
var idarray = platforms.map(function(e)return mongoose.mongo.ObjectID(e););
// update all plateform with article id
Platform.update(_id:$in: idarray, $push:articles: createdArticle, multi:true, upsert:false, function(err, raw)
if(err)
// error case
return res.status(403).json();
// retrieve plateform
Platform.find(_id:$in: idarray, function(err, results)
if(err || !results)
// error case
return res.status(403).json();
Article.update(_id: createdArticle._id.toString(), $push:platforms:$each: results, multi:false, upsert:false, function(err, saved)
if(err || !saved)
// error
return res.status(403).json();
res.redirect('articles/' + savedObject._id.toString());
);
);
);
但是存储完整的对象是个坏主意,为什么不只存储 id 呢??
【讨论】:
非常感谢。 2个快速问题。我是否必须为其他保存功能做同样的事情。这里出了什么问题 是的,因为你在循环中使用了异步函数。见编辑 所以我应该用这个替换我 else 中的所有内容?【参考方案2】:您尝试中的 forEach 循环在下一次迭代之前无法识别 findById()
异步方法的回调完成。您需要使用任何async
库方法async.each
、async.whilst
或async.until
,它们等效于for 循环,并且将等到调用异步回调,然后再进行下一次迭代(换句话说,一个将产生的 for 循环)。
例如:
var platform_docs = [];
async.each(platforms, function(id, callback)
Platform.findById(id, function(err, platform)
if (platform)
platform_docs.push(platform);
callback(err);
);
, function(err)
// code to run on completion or err
console.log(platform_docs);
);
对于整个操作,您可以使用 async.waterfall()
方法,该方法允许每个函数将其结果传递给下一个函数。
方法中的第一个函数创建新文章。
第二个函数使用async.each()
实用函数遍历平台列表,为每个id 执行异步任务以使用findByIdAndUpdate()
更新平台,当它们都完成时将对象变量中的更新查询结果返回给下一个函数。
最后一个函数将使用上一个管道中的平台 ID 更新新创建的文章。
类似于以下示例:
var newArticle = ,
platforms = req.body.platforms,
date = req.body.date,
split = date.split("/");
newArticle.title = req.body.title;
newArticle.description = req.body.description;
newArticle.date = split[2]+'/'+split[0]+'/'+split[2];
newArticle.link = req.body.link;
newArticle.body = req.body.body;
console.log(platforms);
async.waterfall([
// Create the article
function(callback)
var article = new Article(newArticle);
article.save(function(err, article)
if (err) return callback(err);
callback(null, article);
);
,
// Query and update the platforms
function(articleData, callback)
var platform_ids = [];
async.each(platforms, function(id, callback)
Platform.findByIdAndUpdate(id,
"$push": "articles": articleData._id ,
"new": true ,
function(err, platform)
if (platform)
platform_ids.push(platform._id);
callback(err);
);
, function(err)
// code to run on completion or err
if (err) return callback(err);
console.log(platform_ids);
callback(null,
"article": articleData,
"platform_ids": platform_ids
);
);
,
// Update the article
function(obj, callback)
var article = obj.article;
obj.platform_ids.forEach(function(id) article.platforms.push(id); );
article.save(function(err, article)
if (err) return callback(err);
callback(null, article);
);
], function(err, result)
/*
This function gets called after the above tasks
have called their "task callbacks"
*/
if (err) return next(err);
console.log(result);
res.redirect('articles/' + result._id);
);
【讨论】:
如果您正在编辑链接文章@chridam的平台,您将如何应用此功能 您能为此创建一个新问题吗? 我想知道您是否介意查看我发布的关于同一问题的新问题。 question2@chridam以上是关于MongoDB mongoose 子文档创建了两次的主要内容,如果未能解决你的问题,请参考以下文章
MongoDB:如何使用 Mongoose 添加或更新子文档?
MongoDB/Mongoose - 与 geoNear 和子文档的聚合