1

I am using GraphQL MongoDB Mongoose together and I have 2 collections .. users and categories like below.

Category.js

const mongoose = require('mongoose');
const Schema = mongoose.Schema;

const categorySchema = new mongoose.Schema({
    title:{
        type: String,
        required: true
    },
    userid: {
        type: Schema.Types.ObjectId,
        ref: 'User',
        required: true,        
    },
    
    
});

module.exports = mongoose.model('Category',categorySchema);

`


User.js

const mongoose = require('mongoose');
const Schema = mongoose.Schema;
const userSchema = new mongoose.Schema({
    name:{
        type: String,
        required: true
    },
    email:{
        type: String,
        required: true,
        unique: true
    },
    password:{
        type: String,
        required: true
    }
    
});

module.exports = mongoose.model('User',userSchema);

Resolver.js

const User = require('../models/User');
const Category = require('../models/Category');

const resolvers = {
    
    getUsers: async () => {
        try {
            const users = await User.find().populate('categories');            
            return users;
        } catch(err){
            throw new Error(err);            
        }
    },
    

    
};

module.exports = resolvers;

As you can see .. I have 2 collections . and inside Categories collection.. I am adding my data with userid .. everything is working fine .. But I am unable to get categories as its keep showing null in response.

Here below is my GraphQL query.

query{
  getUsers{
    id,
    name
  }
}

And here is what I get in response evenif I have data with userid same from users collection.

{
  "data": {
    "getUsers": [
      {
        "id": "65d316bdab8179475bdb0fef",
        "name": "mittul",
        
      }
    ]
  }
}

UPDATE ..

Getting this response.

{
  "data": {
    "getUsers": null
  }
}



 try {
                var resources = {};

            const users = await User.aggregate([{
                    $group : {_id : null, totalPop: { $sum: "$pop" }}
                },{
                    $lookup: {
                        from: "Category", // from collection name
                        localField: "userid",
                        foreignField: "_id",
                        as: "categories"
                    }
                }]);
                console.log(users);
        } catch(err){
            console.log(err);
            throw new Error(err);            
        }

I have also tried below code but its giving me null in response and nothing in console.log.

  User.find().populate("categories")
        .then(users => {
            console.log(users);
            //res.send(users);
        }).catch(err => {
                    
            throw new Error(err);            
        });

Below are my 2 collections.

enter image description here

enter image description here

Can anyone guide me what I am missing here ?

Thanks

26
  • Does each User document contain an ObjectId as the value to the User.categories property which corresponds to a matching Category._id? Commented Feb 19, 2024 at 23:02
  • @jQueeny no .. why would we need that ? it should not be .. right ? Commented Feb 20, 2024 at 4:51
  • The populate method takes an ObjectId and uses this to do a $lookup into another collection to find a document that has an _id value equal to the referenced ObjectId. You need to manually add this Objectid into the parent document in order for populate to work. If there is no Objectid given for each User.categories how do you expect mongoose to find the referenced Category document from the categories collection? Commented Feb 20, 2024 at 7:56
  • Is there any other way ? @jQueeny without using populate ? Commented Feb 20, 2024 at 8:28
  • You can write an aggregation query but that will use $lookup and you will still need to store an identifier in one of the documents in order to match one document with the other. I don't know how you expect to make the relationship otherwise? MongoDB allows you store one document directly inside another document so that you don't need to populate. You should read about subdocuments. Commented Feb 20, 2024 at 14:37

2 Answers 2

1

Here is a simple aggregation that will allow you to query the users collection. In this example a $match is made for the user.name property but you can query the users for anything you want.

Then you do a $lookup to search the categories collection for any Category that has a userid value equal to the _id value of the User that you found by searching the users collection.

The $set stage will look at the categories array property returned from the $lookup and if its an empty array then it will be removed, otherwise it will be set it itself.

const users = await User.aggregate([
  {
    $match: {
      _id: ObjectId("5a934e000102030405000001"),
      name: "mittul"
    }
  },
  {
    $lookup: {
      from: "categories",
      localField: "_id",
      foreignField: "userid",
      as: "categories"
    }
  },
  {
    $set: {
      "categories": {
        $cond: {
          if: {
            $eq: [
              "$categories",
              []
            ]
          },
          then: "$$REMOVE",
          else: "$categories"
        }
      }
    }
  }
])

See HERE for a working example.

Sign up to request clarification or add additional context in comments.

23 Comments

Is it required to use match ?
I removed match and i am getting error saying ` "Cannot return null for non-nullable field User.id`
Because I can have null values .. categories are not mandatory for each user.
In my terminal .. I am able to see [] array but in GraphiQL I am getting error saying "Cannot return null for non-nullable field User.id.",
@MittulAtTechnoBrave glad you got it working. If you wanted the _id to be id then a $project would have done the trick but looks like you solved it anyway.
|
0

OK Guys .. Eventually I have found an answer by updating an aggregate query like below.

getUsers: async (req,res) => {

       try {
            const users = await User.aggregate([
                {
                    $match: {
                        _id: { $ne: null } // Filter out documents where _id is null
                    }
                },
                {
                    $lookup: {
                        from: "categories",
                        localField: "_id",
                        foreignField: "userid",
                        as: "categories"
                    }
                },
                {
                    $addFields: {
                        "id": { $toString: "$_id" }, // Convert _id to string and assign it to id
                        "categories": {
                            $map: {
                                input: "$categories",
                                as: "category",
                                in: {
                                    id: { $toString: "$$category._id" }, // Convert _id of category to string and assign it to id
                                    title: "$$category.title"
                                }
                            }
                        }
                    }
                }
            ]);
            return users;
         } catch(err){
            throw new Error("Error retrieving user");            
        }

This works in all the scenarios whether I add id or not for users or categories like below.

query{
  getUsers{
    
    id,name,email,categories{
      id,
      title
    }
      
  }
}

Thanks

Comments

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.