how to model a collection in nodejsmongodb?

Important note

All the code below has NOT been tested (yet, I’ll do it when I can setup a minimal environment) and should be adapted to your project. Keep in mind that I’m no expert when it comes to aggregation with MongoDB, let alone with Mongoose, the code is only here to grasp the general idea and algorithm.

If I understood correctly, you don’t have to do anything since the info is stored in the Member collection. But it forces the front-end to do an extra-request (or many extra-requests) to have both the list of Shops and to check (one by one) if the current logged user is a Member of the shop.

Keep in mind that the front-end in general is driven by the data (and so, the API/back-end), not the contrary. The front-end will have to adapt to what you give it.

If you’re happy with what you have, you can just keep it that way and it will work, but that might not be very effective.

Assuming this:

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
  }
});

You could tackle this problem via 2 ways:

MongoDB Aggregates

When retrieving (back-end side) the list of shops, if you know the user that made the request, instead of simply returning the list of Shops, you could return an aggregate of Shops and Members resulting in an hybrid document containing both the info of Shops and Models. That way, the front-end have all the info it needs with one back-end request.

Important note

The following code might not work as-is and you’ll have to adapt it, I currently have nothing to test it against. Keep in mind I’m not very familiar with aggregates, let alone with Mongoose, but you’ll get the general idea by looking the code and comments.

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);
  }
}

Drop the Members collection and store the info elsewhere

Instinctively, I would’ve gone this way. The idea is to either store an array field shopsJoined in the User model, or a membersJoined array field in the Shops model. That way, the info is retrieved no matter what, since you still have to retrieve the Shops and you already have your 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);
  }
};

Of course, the above code is for the User model, but you can do the same thing for the Shop model.

Useful links:

MongoDB aggregation pipelines

Mongoose aggregates

MongoDB $push operator

MongoDB $pull operator

CLICK HERE to find out more related problems solutions.

Leave a Comment

Your email address will not be published.

Scroll to Top