MongoDB 中的多对多关系。Nodejs | Express | Mongoose
最近我正在做一个项目(Node、Express、MongoDB、Mongoose),我需要创建产品和类别之间的多对多关系,其中类别可以有多个产品,产品可以属于多个类别。
所以我开始着手解决这个问题,我使得添加、删除或更新产品也会自动更新类别,反之亦然。
然后我想看看其他人是怎么做的。于是我搜索了一下,却找不到类似的方法。这让我想到,为什么不分享我的方法,让其他人也能从中受益,或者有人能纠正我。
这就是我的做法,这是我的完整代码的简化版本。
模型
首先我创建了产品和类别模型。
产品型号
const mongoose = require("mongoose");
const Schema = mongoose.Schema;
const productSchema = new Schema({
name: { type: String, required: true},
price: { type: Number, required: true, min: 0 },
categories: [{ type: mongoose.Types.ObjectId, ref: 'Category' }],
});
module.exports = new mongoose.model('Product', productSchema);
在这里您可以看到我的产品模型有三个简单的字段,第一个是产品名称,第二个是产品价格,第三个是该产品所属的类别数组。
类别模型
const mongoose = require("mongoose");
const Schema = mongoose.Schema;
const categorySchema = new Schema({
name: { type: String, required: true },
products: [{ type: mongoose.Types.ObjectId, ref: 'Product' }],
});
module.exports = new mongoose.model('Category', categorySchema);
类别模型也很简单,有两个字段:第一个是类别的名称,第二个是此类别中所有产品的数组。
路线
为了简单起见,我将仅向您展示三条路线:创建、更新和删除路线。
1. 创建路线
假设我们要添加一款新产品 iPhone 12,并将其添加到“移动设备”、“智能手机”和“电子产品”三个类别中。因此,我们将使用以下数据发送请求。
{
product: {
"name": "iPhone 12",
"price": "1200",
"categories": ["606563248d90d1833f3cda0b", "606563334294906c9a9b9dfe", "6065633736dee94dfae613d7"]
}
}
这里的 categories 是我们要添加产品的类别的 ID。
为了处理这个请求,我创建了以下路由。
router.post('/', async function (req, res) {
const { product } = req.body;
const newProduct = await Product.create(product);
await Category.updateMany({ '_id': newProduct.categories }, { $push: { products: newProduct._id } });
return res.send(newProduct);
});
首先,我从 req.body 中提取产品数据。const { product } = req.body;
然后我根据这些数据创建了一个新产品。const newProduct = await Product.create(product);
创建产品后,我使用updateMany()查找该产品中的所有类别并更新它们以包含该产品。await Category.updateMany({ '_id': newProduct.categories }, { $push: { products: newProduct._id } });
删除路线
router.delete('/:id', async function (req, res) {
const _id = req.params.id;
const product = await Product.findOne({ _id });
await product.remove();
await Category.updateMany({ '_id': product.categories }, { $pull: { products: product._id } });
return res.redirect(product);
});
删除路线和创建路线一样简单。
首先,我通过 ID 找到产品,然后将其删除。await Product.findOne({ _id });
await product.remove();
然后更新该产品中的所有类别以删除该产品。await Category.updateMany({ '_id': product.categories }, { $pull: { products: product._id } });
更新路线
更新路径有点棘手。因为产品类别可能会更改,所以我们必须知道哪些类别被添加了,哪些被移除了。为此,我创建了这个函数。
function difference(A, B) {
const arrA = Array.isArray(A) ? A.map(x => x.toString()) : [A.toString()];
const arrB = Array.isArray(B) ? B.map(x => x.toString()) : [B.toString()];
const result = [];
for (const p of arrA) {
if (arrB.indexOf(p) === -1) {
result.push(p);
}
}
return result;
}
这个函数基本上接受两个数组 A 和 B,并返回一个新数组,该数组包含数组 A 中存在但不在数组 B 中的项目。类似于这样。
const arrA = ['a', 'b', 'c', 'd'];
const arrB = ['c', 'd', 'e', 'f'];
difference(arrA, arrB);
// Returns: ['a', 'b'] present in arrA but not in arrB
difference(arrB, arrA);
// Returns: ['e', 'f'] present in arrB but not in arrA
我用它来比较新旧类别,以了解哪些类别被删除,哪些类别被添加。
因此为了更新产品我创建了这条路线。
router.put('/:id', async function (req, res) {
const _id = req.params.id;
const { product } = req.body;
const newCategories = product.categories || [];
const oldProduct = await Product.findOne({ _id });
const oldCategories = oldProduct.categories;
Object.assign(oldProduct, product);
const newProduct = await oldProduct.save();
const added = difference(newCategories, oldCategories);
const removed = difference(oldCategories, newCategories);
await Category.updateMany({ '_id': added }, { $addToSet: { products: foundProduct._id } });
await Category.updateMany({ '_id': removed }, { $pull: { products: foundProduct._id } });
return res.send(newProduct);
});
在这里我首先从中获取 IDreq.params.id
和产品更新日期,然后从产品中req.body
获取。newCategories
之后,我通过 ID 找到产品并oldCategories
从中提取数据,以便将它们与新类别进行比较,并确定哪些类别被添加了,哪些类别被删除了。
然后,我将该产品的所有属性赋值给旧产品并保存。
更新产品后,我使用difference()
函数和newCategories
来oldCategories
获取已添加和已删除的类别,然后我updateMany
对类别使用了两个操作将该产品添加到added
类别中并将其从类别中删除removed
。
鏂囩珷鏉ユ簮锛�https://dev.to/nehalahmadkhan/many-to-many-relationship-in-mongodb-nodejs-express-mongoose-4djm分类 路线
类别路线与产品路线相同,我只是使用类别代替产品,反之亦然。