简体   繁体   中英

How can i iterate mongoose returned documents array in loop using mongoose?

I have a node.js(express based) server in which i have a function which returns all users . Here is the function.

export async function findAllUser() {
  let users = await User.find({}).exec()
  return users
}

In my node.js applicaiton i have two models(schema) of Users and Referrals like this.

 var User = mongoose.model( "users", new Schema({ first_name: String, last_name: String, name: String, email: String, password: String, roleId: { type: Number, default: 0 }, country: String, token: String, createdAt: String, updatedAt: String, tempToken: String, verificationCode: String, fbUserId: String, isFbUser: { type: Boolean, default: false }, isActive: { type: Boolean, default: true }, isEmailVerified: { type: Boolean, default: false }, rememberme: Boolean, }, { toJSON: { virtuals: true }, toObject: { virtuals: true } }) ); User.virtual("referrals", { ref: "referralLinks", foreignField: "userId", localField: "_id" });

export var ReferralLink = mongoose.model(
    "referralLinks",
    new Schema({
        referral_link: String,
        referral_code: String,
        isLink: Number,
        offer_name: String,
        offer_desc: String,
        user_email: String,
        companyId: { type: Schema.Types.ObjectId, ref: 'companies' },
        addedByAdmin: { type: Boolean, default: true },
        number_of_clicks: Number,
        referral_country: String,
        link_status: String,
        categoryId: { type: Schema.Types.ObjectId, ref: 'categories' },
        number_of_clicks: { type: Number, default: 0 },
        createdAt: String,
        updatedAt: String,
        userId: { type: Schema.Types.ObjectId, ref: 'users' }
    })
);

I have my separate api.route.js file in which i have get all users route like this

router.get("/", log, getAllUsers);

And i my api.controller.js file i have getAllUsers like this

export async function getAllUsers(req, res) {
try {
    let Users = await findAllUser()
    if (Users) {
        generateResponse(true, "All Users fetched", Users, res)
    } else {
        generateResponse(false, "No Users found", null, res)
    }
 } catch (err) {
    generateResponse(false, 'Error occured, 404 not found!', err, res)
 }
}

And in my api.handler.js file i have findAllUser function like this

 export async function findAllUser() { let users = await User.find({}).populate("referrals").exec() return users }

Single user can have more than one Referrals . But unfortunately i don't have 'Referrals' reference _id in Users document. Now, i want to get all users with their respective Referrals

I am getting all users correctly but for each user i also want to fetch all their respective referrals . So for that i definitely can't use for or forEach loop because of async nature of mongoose find. So what should i use instead of for or forEach loop?

My desired results

 results = [ { first_name: "Fahad", last_name: "subzwari", email: "fahadsubzwari@gmail.com", password: "***", referrals: [ { //referral object 1 }, { //referral object 2... } ] }, { first_name: "Alex", last_name: "Hales", email: "alex@gmail.com", password: "***", referrals: [ { //referral object 1 }, { //referral object 2... }, { //referral object 3... } ] }, ]

To be able to access referrals from user you need to use virtual populate .

So your userSchema must be like this:

const userSchema = new Schema(
  {
    first_name: String,
    last_name: String,
    name: String,
    email: String,
    password: String,
    roleId: { type: Number, default: 0 },
    country: String,
    token: String,
    createdAt: String,
    updatedAt: String,
    tempToken: String,
    verificationCode: String,
    fbUserId: String,
    isFbUser: { type: Boolean, default: false },
    isActive: { type: Boolean, default: true },
    isEmailVerified: { type: Boolean, default: false },
    rememberme: Boolean
  },
  {
    toJSON: { virtuals: true },
    toObject: { virtuals: true }
  }
);

// Virtual populate
userSchema.virtual("referrals", {
  ref: "referralLinks",
  foreignField: "userId",
  localField: "_id"
});

var User = mongoose.model("users", userSchema);

And now you can use this route to access referrals from users:

router.get("/", async (req, res) => {

  const result = await User.find({}).populate("referrals");

  res.send(result);
});

The result will be like this: ( I excluded some fields for simplicity)

[
    {
        "_id": "5dd6819201419f5930d02334",
        "name": "User 1",
        "email": "user1@gmail.com",
        "password": "123123",
        "__v": 0,
        "referrals": [
            {
                "_id": "5dd6829831b95a6b2cd58fca",
                "referral_link": "referral_link 1",
                "userId": "5dd6819201419f5930d02334",
                "__v": 0
            },
            {
                "_id": "5dd682a031b95a6b2cd58fcb",
                "referral_link": "referral_link 2",
                "userId": "5dd6819201419f5930d02334",
                "__v": 0
            }
        ],
        "id": "5dd6819201419f5930d02334"
    },
    {
        "_id": "5dd681a101419f5930d02335",
        "name": "User 2",
        "email": "user2@gmail.com",
        "password": "123123",
        "__v": 0,
        "referrals": [
            {
                "_id": "5dd682a731b95a6b2cd58fcc",
                "referral_link": "referral_link 3",
                "userId": "5dd681a101419f5930d02335",
                "__v": 0
            }
        ],
        "id": "5dd681a101419f5930d02335"
    }
]

UPDATE:

Here is the steps for your project setup:

api.handler.js:

exports.findAllUser = async function() {
  console.log("api handler inside");
  let users = await User.find({})
    .populate("referrals")
    .exec();
  console.log("in handler: ", users);
  return users;
};

api.controller.js:

const handler = require("./api.handler");

exports.getAllUsers = async function(req, res) {
  console.log("userController.getAllUsers");
  try {
    let Users = await handler.findAllUser();
    if (Users) {
      return res.send(Users);
      generateResponse(true, "All Users fetched", Users, res);
    } else {
      generateResponse(false, "No Users found", null, res);
    }
  } catch (err) {
    generateResponse(false, "Error occured, 404 not found!", err, res);
  }
};

api.route.js

const apiController = require("../controllers/api.controller");
router.get("/",  log, apiController.getAllUsers);

You say "i don't have 'Referrals' reference _id in Users" so I assume you have a reference to the user in the Referrals schema?

Otherwise, with no way to link them you are lost at sea I'm afraid... :-(

If you do then you would do it in a separate query:

const userIds = users.map(user => user._id);

const referrals = await Referrals.find({ userId: { $in: userIds } })

The $in operator will grab any field where the user id is included in the array.

EDIT: In response to your update - yes the above should work fine. Then you can do what you want with them eg map the referrals to the user objects, or use them individually etc. etc.

EDIT2: Yep this is the way. At this point you have an array of users and an array of referrals so you just need to put them together.

users.map(user => ({ 
   // add props from user obj
   ...user, 
   // add all referrals that with matching userId
   referrals: referrals.filter(referral => referral.userId === user._id)
 }))

Remember that as you are dealing with asynchronous calls and promises so you will either need to use the async/await keywords, or parse the results in the promise callback.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM