2

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 : "[email protected]",
                password : "***",
                referrals  : [
                    {
                        //referral object 1
                    },
                    {
                        //referral object 2 ... 
                    }
                ]
            },
            {
                first_name : "Alex",
                last_name : "Hales",
                email : "[email protected]",
                password : "***",
                referrals  : [
                    {
                        //referral object 1
                    },
                    {
                        //referral object 2 ... 
                    },
                    {
                        //referral object 3 ... 
                    }
                ]
            },
        ]

4
  • You say " i don't have 'Referrals' reference _id in Users". Do referrals have a reference to the User? You will need some way to link them. Commented Nov 21, 2019 at 11:56
  • Can you add code for Users and Referrall schema? Also some example documents would be fine. Commented Nov 21, 2019 at 11:59
  • @JamesTrickey yes i have Users _id in Referrals Commented Nov 21, 2019 at 12:01
  • 1
    @JamesTrickey i have updated my question please see that Commented Nov 21, 2019 at 12:07

2 Answers 2

1

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": "[email protected]",
        "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": "[email protected]",
        "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);
Sign up to request clarification or add additional context in comments.

8 Comments

is it necessary to put this { toJSON: { virtuals: true }, toObject: { virtuals: true } } in my User schema?
@FahadHassanSubzwari yes it must be in the schema. But this is the way to use virtual populate. I tried this code myself, and it is working. Did you try?
and should i enclosed my all other keys of schema in an object like this { name: String, email: String, password: String } ?? because see my schema in my question
@FahadHassanSubzwari yes, also about the previos comment, I tried only with toJSON: { virtuals: true }, toObject may be removed, but toJSON is required.
It is saying User.virtual is not a function
|
0

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 e.g. 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.

4 Comments

see my updated post. I don't think that using your solution i can get the desired results.
This is what you want - you just need to parse results - i have updated
"Otherwise, with no way to link them you are lost at sea I'm afraid." => there is a way called virtual populate, you can check my answer.
@SuleymanSah - I stated that you cant do it if userId is not defined in the referral. That is correct - you need that field to use virtual populate. I stand by my answer as his Schemas are already written and this shows what is going on under the hood.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.