Mongoose $or condition for nested array - javascript

There are my schemas
//ProjectModel
const ProjectSchema: Schema = new Schema(
owner: { type: Schema.Types.ObjectId, ref: "User" },
users: [{type: Schema.Types.ObjectId, ref: "ProjectUser", unique: true }]
);
//Project User model
const ProjectUserSchema = new Schema(
{
user: { type: Schema.Types.ObjectId, ref: "User", require: true },
role: {
type: String,
default: 'basic',
enum: ["basic", "projectuser", "moderator", "admin"]
},
project: { type: Schema.Types.ObjectId, ref: "Project", require: true },
},
{
timestamps: true,
usePushEach: true,
}
);
The User model has common fields like password, name, etc.
I want to find User of a ProjectModel either among the owner (UserSchema) or among users (ProjectUserSchema)
ProjectModel.findOne()
.or([{ owner: req.params.user }, { "users.user": req.params.user }])
.then(project => {
res.json(project);
});
But it returns null. And condition .or([{ owner: req.params.user }, { "users._id": "PROJECT USER ID" }]) doesn't work either.
What should I do?

You need to convert the incoming req.params.user from string to ObjectId. Try this:
const mongoose = require('mongoose');
const getProjectByUser = (req, res) => {
let userId = mongoose.Types.ObjectId(req.params.user);
ProjectModel
.findOne({
$or: [
{ "owner": userId },
{ "users": userId }
]
})
.then(project => {
res.json(project);
})
.catch(e => {
res.json({ error: "Error!" });
});
}

Related

Remove object from nested array failed

A team has many projects. Im trying to delete a project, and so I need to delete it in marcsEquipa[] too.
TEAM SCHEMA
const EquipaSchema = new mongoose.Schema({
trab1: {
type: Schema.Types.ObjectId,
required: true,
ref: 'Trab'
},
trab2: {
type: Schema.Types.ObjectId,
required: true,
ref: 'Trab'
},
trab3: {
type: Schema.Types.ObjectId,
required: true,
ref: 'Trab'
},
teamName: {
type: String,
required: true
},
marcsEquipa: [{
type: Schema.Types.ObjectId,
ref: 'Marcacao'
}]
},
{collection: 'Equipas'})
Function Delete Project
exports.deleteMarc = async (req,res) => {
console.log("Deleting Project..");
console.log(req.params._id);
console.log(req.params.equipa);
try{
console.log("1");
const equipa = await
Equipas.updateOne({ _id: req.params.equipa}, { $pull: { marcsEquipa: { _id: req.params._id}}}, { multi: true });
equipa.save();
console.log("1");
//await Marcacao.deleteOne({_id: req.params._id});
res.status(200).json();
console.log("1");
}catch(err) {
res.status(400).json({message: err});
}
}
I've tried this and it manages to delete the Project but it doesn't delete it within the array of Projects in Team. Can anyone help?
You are trying to pull the item with _id property, but the items are just the string representation of ObjectId. So, instead of this:
{ $pull: { marcsEquipa: { _id: req.params._id }}}
do this:
{ $pull: { marcsEquipa: req.params._id }}

why Virtual Populate not working on Node js and mongoose? Scenario : Product and Review by user

I have review and product model.If user give review on specific product(id) then it is stored in review model database but i donot like to store user review in product model database .so, i used virtual populate in product model instead of child referencing.After using virtual properties,if we use product id to see details,we can see review of user in json format but not saved in database.But the problem is my virtual properties (In Product Model) not working as it doesnt show review of user in json format when i send the request in that product id which already have review by user(stored in review model database).what is the problem here?
User Review on Product (id) stored in database
Sending Request of that product id to see review of user in json format using virtual properties(but no review found in json)
In Product Model
const productSchema = new Schema({
name: {
type: String,
required: true,
trim: true,
},
slug: {
type: String,
required: true,
unique: true,
},
price: {
type: String,
required: true,
},
quantity: {
type: Number,
required: true,
},
description: {
type: String,
required: true,
trim: true,
},
offer: {
type: Number,
},
discount: {
type: Number,
},
productPictures: [{
img: {
type: String,
},
}, ],
mainCategory: {
type: mongoose.Schema.Types.ObjectId,
ref: "category",
required: [true, "It is a required field"],
},
sub1Category: {
type: mongoose.Schema.Types.ObjectId,
ref: "category",
required: [true, "It is a required field"],
},
sub2Category: {
type: mongoose.Schema.Types.ObjectId,
ref: "category",
required: [true, "It is a required field"],
},
createdBy: {
type: mongoose.Schema.Types.ObjectId,
ref: "admin",
required: true,
},
vendor: {
type: mongoose.Schema.Types.ObjectId,
ref: "vendor",
},
createdAt: {
type: String,
default: moment().format("DD/MM/YYYY") + ";" + moment().format("hh:mm:ss"),
},
updatedAt: {
type: String,
default: moment().format("DD/MM/YYYY") + ";" + moment().format("hh:mm:ss"),
},
},
{
toJson: { virtuals: true },
toObject: { virtuals: true },
}
);
productSchema.virtual("reviews", {
ref: "review",
foreignField: "product",
localField: "_id",
// justOne: true
});
const Product = mongoose.model("product", productSchema);
module.exports = Product;
In Review Model
const mongoose = require("mongoose");
const Schema = mongoose.Schema;
const moment = require("moment");
const reviewSchema = new Schema({
user: {
type: mongoose.Schema.Types.ObjectId,
ref: "user",
required: [true, "Review must belong to user"],
},
product: {
type: mongoose.Schema.Types.ObjectId,
ref: "product",
required: [true, "Review must belong to the product"],
},
review: {
type: String,
required: [true, "Review cannot be empty"],
},
rating: {
type: Number,
min: 1,
max: 5,
},
createdAt: {
type: String,
default: moment().format("DD/MM/YYYY") + ";" + moment().format("hh:mm:ss"),
},
updateddAt: {
type: String,
default: moment().format("DD/MM/YYYY") + ";" + moment().format("hh:mm:ss"),
},
}, {
toJson: { virtuals: true },
toObject: { virtuals: true },
});
// pre middleware and populating user and product(we can also do populate in getAllReview in controller)
reviewSchema.pre(/^find/, function(next) {
// ^find here is we use regex and can able to find,findOne ...etc
this.populate({
path: "product",
select: " _id name",
}).populate({
path: "user",
select: " _id fullName",
});
next()
});
const Review = mongoose.model("review", reviewSchema);
module.exports = Review;
In Review.js
const Review = require("../../models/Review.Models")
exports.createReview = async(req, res) => {
const review = await Review.create(req.body)
return res.status(201).json({
status: true,
review
})
}
exports.getAllReviews = async(req, res) => {
try {
const reviews = await Review.find()
return res.status(200).json({
status: true,
totalReviews: reviews.length,
reviews
})
} catch (error) {
return res.status(400).json({
status: false,
error
})
}}
In Product.js
const Product = require("../../models/Product.Models");
exports.getProductDetailsById = async(req, res) => {
try {
const { productId } = req.params;
// const { productId } = req.body;
if (productId) {
const products = await Product.findOne({ _id: productId })
.populate('reviews')
return res.status(200).json({
status: true,
products,
});
} else {
console.log("error display");
return res.status(400).json({
status: false,
error: "params required...",
});
}
} catch (error) {
return res.status(400).json({
status: false,
error: error,
});
}
try this in Product.js
try {
if (productId) {
const products = await Product.findOne({ _id: productId }).populate(
"reviews"
);
console.log(products);
if (products) {
return res.status(200).json({
status: true,
message: "Products is listed",
products,
reviw: products.reviews,
});
only need to add on response sending
return res.status(200).json({
status: true,
message: "Products is listed",
products,
reviw: products.reviews,
});

posts.populate is not a function

I'm trying to populate my post's author fields (which is are object ids) with the corresponding author objects which are in a separate collection.
My controller code is as follows:
exports.readPosts = async (req, res) => {
try {
const posts = await Post.find({ board: req.params.board });
await posts.populate("author").execPopulate();
res.send(posts);
} catch (err) {
res.status(400).send(err.message);
}
};
I'm at a loss as to why this isn't working as I have very similar code in another controller method that is working just fine.
All help greatly appreciated.
Below is the relevant Model file:
const mongoose = require("mongoose");
const postSchema = new mongoose.Schema(
{
title: {
type: String,
required: true,
trim: true,
},
content: { type: String, required: true, trim: true },
comments: [
{
comment: {
type: String,
required: true,
trim: true,
},
user: {
type: mongoose.Schema.Types.ObjectId,
ref: "User",
},
date: {
type: Date,
default: Date.now(),
},
},
],
author: {
type: mongoose.Schema.Types.ObjectId,
required: true,
ref: "User",
},
board: {
type: mongoose.Schema.Types.ObjectId,
required: true,
ref: "Board",
},
},
{ timestamps: true }
);
const Post = mongoose.model("Post", postSchema);
module.exports = Post;
posts is an array of models. populate must be called on a model. The preferred way to do this is at query time. It probably works on your other controller because you are using a findOne so it is returning the model, not the Array.
const posts = Post
.find({ board: req.params.board })
.populate('author')
.exec();

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

Mongoose populate not populating

I am trying to populate my users car inventory. All the cars have a userId attached to them when they are created but when I go to populate the inventory it doesn't work and I get no errors.
Here are my models:
User.js
let UserSchema = mongoose.Schema({
username: {
type: String,
required: true,
unique: true
},
password: {
type: String,
required: true
},
inventory: [{ type: mongoose.Schema.Types.ObjectId, ref: 'Car' }]
});
let User = mongoose.model('User', UserSchema);
models.User = User;
Cars.js
let CarSchema = mongoose.Schema({
userId: {
type: mongoose.Schema.Types.ObjectId,
ref: 'User'
},
make: {
type: String,
required: true
},
model: {
type: String,
required: true
},
year: {
type: String
}
});
let Car = mongoose.model('Car', CarSchema);
models.Car = Car;
Here is the populate code:
router.route('/users/:user/inventory').get((req, res) => {
User.findById(userId)
.populate('inventory')
.exec((err, user) => {
if (err) {
console.log("ERRROORRR " + err)
return res.send(err);
}
console.log('Populate ' + user)
res.status(200).json({message: 'Returned User', data: user});
});
});
};
This is what a car object looks like in the database:
{
"_id": ObjectId("5759c00d9928cb581b5424d0"),
"make": "dasda",
"model": "dafsd",
"year": "asdfa",
"userId": ObjectId("575848d8d11e03f611b812cf"),
"__v": 0
}
Any advice would be great! Thanks!
Populate in Mongoose currently only works with _id's, though there's a long-standing issue to change this. You'll need to make sure your Car model has an _id field and that the inventory field in User is an array of these _id's.
let CarSchema = new mongoose.Schema(); //implicit _id field - created by mongo
// Car { _id: 'somerandomstring' }
let UserSchema = new mongoose.Schema({
inventory: [{
type: mongoose.Schema.Types.ObjectId,
ref: 'Car'
}]
});
// User { inventory: ['somerandomstring'] }
User.populate('inventory')

Categories