考虑到 Mongoose Node.js 中的引用,如何删除对象?
Posted
技术标签:
【中文标题】考虑到 Mongoose Node.js 中的引用,如何删除对象?【英文标题】:How to remove object taking into account references in Mongoose Node.js? 【发布时间】:2016-10-26 22:27:35 【问题描述】:这是我的 MongoDB 架构:
var partnerSchema = new mongoose.Schema(
name: String,
products: [
type: mongoose.Schema.Types.ObjectId,
ref: 'Product'
]
);
var productSchema = new mongoose.Schema(
name: String,
campaign: [
type: mongoose.Schema.Types.ObjectId,
ref: 'Campaign'
]
);
var campaignSchema = new mongoose.Schema(
name: String,
);
module.exports =
Partner: mongoose.model('Partner', partnerSchema),
Product: mongoose.model('Product', productSchema),
Campaign: mongoose.model('Campaign', campaignSchema)
我想知道如何在考虑到引用的情况下从任何文档中删除对象(也许我应该以某种方式从猫鼬中填充)?例如,如果我将删除 Product
,那么我假设我还将删除 Partner
中的引用 ID 以及属于此 Product
的所有 Campaigns
。
目前我以这种方式删除:
var campSchema = require('../model/camp-schema');
router.post('/removeProduct', function (req, res)
campSchema.Product.findOneAndRemove( _id: req.body.productId , function (err, response)
if (err) throw err;
res.json(response);
);
);
但是在 mongo 中仍然留下了引用。
【问题讨论】:
【参考方案1】:您必须嵌套调用才能从其他模型中删除产品 ID。例如,在您从Product
中删除产品的电话中
集合,您还可以再次调用以从结果回调中的Partner
模型中删除引用。默认情况下删除产品将删除其对Campaign
模型的引用。
下面的代码展示了上面的直觉:
var campSchema = require('../model/camp-schema');
router.post('/removeProduct', function (req, res)
campSchema.Product.findOneAndRemove( _id: req.body.productId , function (err, response)
if (err) throw err;
campSchema.Partner.update(
"products": req.body.productId ,
"$pull": "products": req.body.productId ,
function (err, res)
if (err) throw err;
res.json(res);
);
);
);
要删除关联的广告系列,您可能需要额外的删除操作,从给定的产品 ID 中获取关联的广告系列 ID。考虑一下以下肮脏的技巧,如果不小心回调嵌套,它可能会给你一张到callback hell 的单程票:
router.post('/removeProduct', function (req, res)
campSchema.Product.findOneAndRemove(
_id: req.body.productId ,
new: true ,
function (err, product)
if (err) throw err;
campSchema.Partner.update(
"products": req.body.productId ,
"$pull": "products": req.body.productId ,
function (err, res)
if (err) throw err;
var campaignList = product.campaign
campSchema.Campaign.remove( "_id": "$in": campaignList )
.exec(function (err, res)
if (err) throw err;
res.json(product);
)
);
);
);
虽然它有效,但可以通过使用 async/await 或 async
库来避免上述潜在陷阱。但首先,为了让您更好地理解在 async
模块中使用多个回调,让我们通过一个来自Seven Things You Should Stop Doing with Node.js 的示例来说明这一点,该示例使用回调来查找父实体的多个操作,然后查找属于父级的子实体:
methodA(function(a)
methodB(function(b)
methodC(function(c)
methodD(function(d)
// Final callback code
)
)
)
)
使用 async/await,您的调用将被重组为
router.post('/removeProduct', async (req, res) =>
try
const product = await campSchema.Product.findOneAndRemove(
_id: req.body.productId ,
new: true
)
await campSchema.Partner.update(
"products": req.body.productId ,
"$pull": "products": req.body.productId
)
await campSchema.Campaign.remove( "_id": "$in": product.campaign )
res.json(product)
catch(err)
throw err
)
使用异步模块,您可以使用系列方法来解决使用回调来嵌套多个方法的代码,这可能会导致Callback Hell:
Series:
async.series([
function(callback)
// code a
callback(null, 'a')
,
function(callback)
// code b
callback(null, 'b')
,
function(callback)
// code c
callback(null, 'c')
,
function(callback)
// code d
callback(null, 'd')
],
// optional callback
function(err, results)
// results is ['a', 'b', 'c', 'd']
// final callback code
)
或者waterfall:
async.waterfall([
function(callback)
// code a
callback(null, 'a', 'b')
,
function(arg1, arg2, callback)
// arg1 is equals 'a' and arg2 is 'b'
// Code c
callback(null, 'c')
,
function(arg1, callback)
// arg1 is 'c'
// code d
callback(null, 'd');
], function (err, result)
// result is 'd'
)
现在回到您的代码,使用异步瀑布方法,您可以将代码重构为
router.post('/removeProduct', function (req, res)
async.waterfall([
function (callback)
// code a: Remove Product
campSchema.Product.findOneAndRemove(
_id: req.body.productId ,
function (err, product)
if (err) callback(err);
callback(null, product);
);
,
function (doc, callback)
// code b: Remove associated campaigns
var campaignList = doc.campaign;
campSchema.Campaign
.remove( "_id": "$in": campaignList )
.exec(function (err, res)
if (err) callback(err);
callback(null, doc);
);
,
function (doc, callback)
// code c: Remove related partner
campSchema.Partner.update(
"products": doc._id ,
"$pull": "products": doc._id ,
function (err, res)
if (err) callback(err);
callback(null, doc);
);
], function (err, result)
if (err) throw err;
res.json(result); // OUTPUT OK
);
);
【讨论】:
我明白了。但在文档campaign
中仍然是分配给已移除产品的对象。我应该使用下一个回调删除他吗?
哦,您还想删除与产品关联的广告系列吗?我以为你只是想从其他模型中删除产品及其引用,即它的关系,而不是删除与已删除产品相关的模型?
那么,没有指定产品的活动是不必要的。所以我不应该把它保存在文档中。
从设计上看似乎没有必要,为什么不将模式嵌入Product
模型中,而不必创建自己的单独模型?这是因为根据您的要求,Campaign
直接依赖于Product
模型,删除产品也意味着删除其广告系列,因此嵌入它比将其保留为单独的重新引用模型更有意义。
您需要异步库吗?即var async = require('async');
?以上是关于考虑到 Mongoose Node.js 中的引用,如何删除对象?的主要内容,如果未能解决你的问题,请参考以下文章
无法使用 mongoose、Node.js 将数据插入到 mongoDB 中的 Document 字段中
使用 Node.js 和 Mongoose 将查询结果保存到模型/模式的属性
如何使用 Node.js 和 Mongoose 将对象添加到嵌套数组
Node.js Mongoose.js 字符串到 ObjectId 函数