I'm trying to filter my pets by category, I have the following model of pets:
const Pet = mongoose.model(
'Pet',
new Schema({
name: {
type: String,
required: true,
},
age: {
type: Number,
required: true,
},
description: {
type: String,
},
weight: {
type: Number,
required: true,
},
color: {
type: String,
required: true,
},
images: {
type: Array,
required: true,
},
available: {
type: Boolean,
},
category: Object,
user: Object,
adopter: Object,
}, { timestamps: true }),
);
module.exports = Pet;
when I try to get the data through postman it returns an empty array as a response.
my code to filter by category:
static async getByCategory(req, res) {
const id = req.params.id;
// check if id is valid
if (!ObjectId.isValid(id)) {
res.status(422).json({ msg: 'Invalid ID' });
return;
}
const pets = await Pet.find({ 'category._id': id }).sort('-createdAt');
if (!pets) {
res.status(404).json({ msg: 'Pets not found!' });
return;
}
res.status(200).json({ pets });
}
it's my first time using mongodb so i'm not sure what's wrong.
id being passed from the client side is string and the one which is saved in the db is ObjectId. Convert the string to Mongoose ObjectId before Pet.find().
const id = mongoose.Types.ObjectId(req.params.id);
const pets = await Pet.find({ 'category._id': id }).sort('-createdAt');
Don't forget to import 'mongoose'.
Could you check that your MongoDB indeed has a field category._id?
I need to get a nested object within a certain document (searched by user ID) that also has an object inside of it (there's no guarantee that this object will be the same object).
I have the User model to be:
const mongoose = require('mongoose');
const { bool } = require('#hapi/joi');
const monitoringSchema = new mongoose.Schema({
type: Object,
default: {}
})
const hubSchema = new mongoose.Schema({
hubID: {
type: String,
default: ""
},
isSetup: {
type: Boolean,
default: false
},
monitoring: {
type: monitoringSchema
}
}, {strict:false})
const finalUserSchema = new mongoose.Schema({
username: {
type: String,
required: true,
max: 255
},
email: {
type: String,
required: true,
max: 255,
},
password: {
type: String,
required: true,
min: 10,
max: 1024,
},
date: {
type: Date,
default: Date.now
},
isVerified: {
type: Boolean,
default: false
},
hub: {
type: hubSchema
}
}, {strict:false});
module.exports = mongoose.model('User', finalUserSchema);
OR It has the layout:
_id: "id"
isVerified: true
username: "nathan"
email: "email#email.com"
hub:
hubID: "id"
monitoring: // WHOLE OBJECT I NEED TO RETREIVE
exampleObject:
exampleValue: exampleKey
I have an array of user IDs I need to update and I tried the query:
for(i in usersToUpdate){
User.findOne({_id: usersToUpdate[i], "hub.monitoring": {}}, {}, callbackResponse);
function callbackResponse(err, data){
if(err) return console.log(err)
console.log(data)
}
}
But it returns null as the data so obviously the query is wrong. I know the error is:
{_id: usersToUpdate[i], "hub.monitoring": {}}
more specifically:
"hub.monitoring": {}
I'm using {} to reference an object within monitoring, what's the correct reference to reference an unknown object and get it's values back, like a wildcard? I've tried:
{_id: usersToUpdate[i], "hub.monitoring": Object}
and it still doesn't work. I've seen this answer, however they reference a value that they already know, like a name?
To retrieve only the monitoring object, aggregation pipeline can be used.
Using $match to filter and $project to output/ supress fields.
User.aggregate([
{
$match: {
_id: mongoose.Types.ObjectId(usersToUpdate[i]),
},
},
{
$project: {
monitoring: "$hub.monitoring",
_id: 0,
},
},
]).exec(callbackResponse);
Playground example
You can try using the 2 object form of findOne where the first object is the query and the second object is the projection of what you want to return.
User.findOne({_id: usersToUpdate[i]}, {"hub.monitoring": {$exists: true}}, callbackResponse);
function callbackResponse(err, data){
if(err) return console.log(err)
console.log(data)
}
This way, the object will be returned if the monitoring object exist.
I try to check if a value is in array's objects. After that I push the object is the value is not in the array. How can I do this ?
router.post('/save', (req, res) => {
let userId = req.user.id
let dataPushSave = req.body.idSave
let dataPushSaveObj = {idSave: dataPushSave}
User.findById(userId, (err, user) => {
if (user.favorites.idSave !== dataPushSave) {
user.favorites.push(dataPushSaveObj)
user.save()
}
})
My mongoose model:
const User = new Schema({
firstName: {
type: String,
required: true
},
favorites: [{
_id: Object,
idSave: String
}]
});
const User = new Schema({
firstName: {
type: String,
required: true
},
favorites: [{
_id: Object,
idSave: String
}]
});
From the above schema, remove _id: Object from favorites.
I would recommend below schema
const User = new Schema({
firstName: {
type: String,
required: true
},
favorites: {
type: [new Schema({
idSave: { type: String },
}, { _id: false })]
}
});
Then use $addToSet operator to make sure there are no duplicates in the favorites array.
let user;
User.findByIdAndUpdate(
userId,
{ $addToSet: { favorites: dataPushSaveObj } },
{ new: true }, // this option will make sure you get the new updated docc
(err, doc) => {
if (err) console.error(err);
user = doc;
}
);
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 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.
I have a little question. I have a User schema which contains a table fields redirecting to the Table schema.
A Table element can contain a name, I want this name to be unique per User but not between User.
Example:
User1 -> Table1.name: "foo"
User2 -> Table1.name: "foo"
But this User1 -> Table2.name: "foo" cannot be possible.
This is the User Schema:
var UserSchema = new mongoose.Schema({
username: { type: String, required: true, index: {
unique: true } },
email: { type: String, required: true, index: {unique: true } },
password: { type: String, required: true },
tables: [{ type: Schema.Types.ObjectId, ref: 'Table' }],
resetPasswordToken: String,
resetPasswordExpires: Date,
uuid: String,
});
This is the Table schema:
var TableSchema = Schema({
name: { type: String, required: true, index: { unique: true }},
logos: [{ type: Schema.Types.ObjectId, ref: 'Logo'}],
});
And this is where I do the queries:
app.post('/newTab', function(req, res){
var use = req.user.username;
var newTab = new Table({
name: req.body.name,
});
newTab.save(function(err, docs){
if (err)
{
console.error(err);
res.writeHead(500);
res.end();
}
else
{
User.findOne({username: req.user.username}, function(err, docs) {
if(err) {throw (err);}
else
{
docs.tables.push(newTab);
docs.save(function(err, docs){
if (err) return console.error(err);
res.writeHead(200);
res.end(userId);
});
}
});
}
});
For now, I cannot add the same table name for two different User ..
I also tried something with sparse index but can't figure how it works