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分类 路线
类别路线与产品路线相同,我只是使用类别代替产品,反之亦然。
 后端开发教程 - Java、Spring Boot 实战 - msg200.com
            后端开发教程 - Java、Spring Boot 实战 - msg200.com