【问题标题】:How to model a collection in nodejs+mongodb如何在nodejs+mongodb中建模一个集合
【发布时间】:2022-01-03 08:12:43
【问题描述】:

您好,我是 nodejs 和 mongodb 的新手。

我有 3 个模型:

  1. “用户”字段为“姓名电话”
  2. 带有“名称、地址”字段的“商店”
  3. 带有“商店用户状态”字段的“会员”。 (商店和用户持有各自收藏的“id”)。

现在,当我创建“商店”api 来获取所有商店时,我需要添加不属于模型的额外字段“isShopJoined”。如果看到该商店的用户加入,则此额外字段将为 true,否则将为 false。

当我与 Android/iOS 等前端开发人员共享我的模型时会出现问题,他们在看到 API 响应之前不会知道那个额外的字段。

那么,如果我在不属于模型的商店列表中添加额外的字段,可以吗?还是我需要在模型中添加那个额外的字段?

【问题讨论】:

  • 真正的问题是:你有任何理由在模型中添加字段吗?
  • @GaëtanBoyals 据我了解,如果我在“Shop”模型中添加“isShopJoined”字段,那么我将在该字段中保存什么,例如如果我有“name”字段,那么名称显然会包含店铺名称并且店铺只有一个名称,但在这种情况下,多个用户可以加入同一店铺,因为我有单独的“会员”模型/系列。希望你能理解。

标签: node.js mongodb mongoose


【解决方案1】:

重要提示

下面的所有代码都经过测试(但是,当我可以设置一个最小的环境时,我会这样做)并且应该适应您的项目。请记住,我不是 MongoDB 聚合方面的专家,更不用说 Mongoose,这里的代码只是为了掌握大致的思想和算法。

如果我理解正确,您无需执行任何操作,因为信息存储在 Member 集合中。但它强制前端执行额外请求(或许多额外请求)以同时拥有Shops 的列表并检查(一一)当前登录的用户是否是商店的Member .

请记住,前端通常是由数据驱动的(API/后端也是如此),而不是相反。前端必须适应你给它的东西。

如果你对现有的东西感到满意,你可以保持这种状态,它会起作用,但这可能不是很有效。

假设:

import mongoose from "mongoose";

const MemberSchema = new mongoose.Schema({
  shopId: {
    type: ObjectId,
    ref: 'ShopSchema',
    required: true
  },
  userId: {
    type: ObjectId,
    ref: 'UserSchema',
    required: true
  },
  status: {
    type: String,
    required: true
  }
});

const ShopSchema = new mongoose.Schema({
  name: {
    type: String,
    required: true
  },
  address: {
    //your address model
  }
});

const UserSchema = new mongoose.Schema({
  name: {
    type: String,
    required: true
  },
  phone: {
    type: String,
    required: true,
  },
  // Add something like this
  shopsJoined: {
    type: Array,
    default: [],
    required: true
  }
});

你可以通过两种方式解决这个问题:

MongoDB 聚合

在检索(后端)商店列表时,如果您知道发出请求的用户,而不是简单地返回 Shops 的列表,您可以返回 ShopsMembers 的聚合生成包含ShopsModels 信息的混合文档。这样一来,前端就可以通过一个后端请求获得所需的所有信息。

重要提示

以下代码可能无法按原样运行,您必须对其进行调整,我目前没有什么可以测试它的。请记住,我对聚合不是很熟悉,更不用说 Mongoose,但您可以通过查看代码和 cmets 了解大致的概念。

const aggregateShops = async (req, res, next) => {
  try {
    // $lookup will merge the "Model" and "Shop" documents into one
    // $match will return only the results matching the condition
      const aggreg = await Model.aggregate({$lookup: {
        from: 'members', //the name of the mongodb collection
        localField: '_id', //the "Shop" field to match with foreign collection
        foreignField: 'shopId', //the "Member" field to match with local collection
        as: 'memberInfo' //the field name in which to store the "Member" fields; 
      }, {
        $match: {memberInfo: {userId: myUserId}}

      }});
      // the result should be an array of object looking like this:
      /*{
        _id: SHOP_OBJECT_ID,
        name: SHOP_NAME,
        address: SHOP_ADDRESS,
        memberInfo: {
          shopId: SHOP_OBJECT_ID,
          userId: USER_OBJECT_ID,
          status: STATUS_JOINED_OR_NOT
        }
      }*/
      // send back the aggregated result to front-end
  } catch (e) {
    return next(e);
  }
}

删除Members 集合并将信息存储在别处

本能地,我会走这条路。这个想法是在User 模型中存储一个数组字段shopsJoined,或者在Shops 模型中存储一个membersJoined 数组字段。这样,无论如何都会检索信息,因为您仍然需要检索 Shops 并且您已经拥有 User

// Your PATCH route should look like this
const patchUser = async (req, res, next) => {
  try {
    // How you chose to proceed here is up to you
    // I tend to facilitate front-end work, so get them to send you (via req.body) the shopId to join OR "un-join"
    // They should already know what shops are joined or not as they have the User
    // For example, req.body.shopId = "+ID" if it's a join, or req.body.shopId = "-ID" if it's an un-join

    if (req.body.shopId.startsWith("+")) {
      await User.findOneAndUpdate(
        { _id: my_user_id },
        { $push: { shopsJoined: req.body.shopId } }
      );
    } else if (req.body.shopId.startsWith("-")) {
      await User.findOneAndUpdate(
        { _id: my_user_id },
        { $pull: { shopsJoined: req.body.shopId } }
      );
    } else {
      // not formatted correctly, return error
    }
    // return OK here depending on the framework you use
  } catch (e) {
    return next(e);
  }
};

当然,上面的代码是针对User模型的,但是你可以对Shop模型做同样的事情。

有用的链接:

MongoDB aggregation pipelines

Mongoose aggregates

MongoDB $push operator

MongoDB $pull operator

【讨论】:

  • “前端必须适应你给它的东西”,感谢您的宝贵意见
  • 我有一个想法,我是否将列表模型和创建/更新模型分开? @Gaëtan Boyals
  • 好吧,如果您谈论转换为 POST、GET、PATCH、DELETE http 方法的 CRUD 操作(创建、读取、更新、删除),它应该在您的后端自动分离。否则,我并没有真正理解你想说什么。我将在我的答案中添加示例以使其更清楚。
【解决方案2】:

是的,您必须将字段添加到模型中,因为将其添加到响应中只是键的临时显示,但是如果您将来或某些列表过滤器中需要它怎么办,所以添加它很好到模型。

如果您认为必须通知前端,那么就去吧,您也可以为“isShopJoined”键设置一些默认值,让它暂时保持不变。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2019-08-07
    • 2013-08-15
    • 1970-01-01
    • 1970-01-01
    • 2021-09-30
    • 1970-01-01
    • 1970-01-01
    • 2021-01-01
    相关资源
    最近更新 更多