How to populate mongoose with obejectId which is defined as Number? - javascript

I'm trying to populate my user requests.profileId but it returns only nulls.
I have the following schemas:
First Schema:
const profileSchema = new mongoose.Schema({
_id: { type: Number }, //<- _id is defined as a number which represents mobile number (easier for me to handle)
first: { type: String },
second: { type: String },
});
module.exports = mongoose.model('Profile', profileSchema, 'profiles');
Second Schema:
const userSchema = new mongoose.Schema({
firstName: { type: String },
lastName: { type: String },
requests: [
{
profileId: {
type: mongoose.Schema.Types.ObjectId,
ref: 'Profile',
},
requestTime: { type: Date, default: Date.now },
},
],
});
module.exports = mongoose.model('User', userSchema, 'users');
Here is my code:
const user = await User.findById(req.user).populate('requests.profileId');
console.log(user.requests);
Here is the output:
[
{
_id: 6201633869648e2b74c00a10,
profileId: null,
requestTime: 2022-02-07T18:21:44.722Z
},
{
_id: 6201633b69648e2b74c00a11,
profileId: null,
requestTime: 2022-02-07T18:21:47.238Z
},
{
_id: 620238f9d2b5dd3dee6c41a2,
profileId: null,
requestTime: 2022-02-08T09:33:45.176Z
},
{
_id: 620239253220343dfd7cfdd9,
profileId: null,
requestTime: 2022-02-08T09:34:29.780Z
}
]
Here is the output without populate:
[
{
_id: 6201633869648e2b74c00a10,
profileId: 393732353235303134343330, //<- typeof profileId is obeject
requestTime: 2022-02-07T18:21:44.722Z
},
{
_id: 6201633b69648e2b74c00a11,
profileId: 393732353435353333313131,
requestTime: 2022-02-07T18:21:47.238Z
},
{
_id: 620238f9d2b5dd3dee6c41a2,
profileId: 393732353435353333313131,
requestTime: 2022-02-08T09:33:45.176Z
},
{
_id: 620239253220343dfd7cfdd9,
profileId: 393732353435353333313131,
requestTime: 2022-02-08T09:34:29.780Z
}
]
Currently Profile.findById(mobileNumber) works fine.
Any ideas what went wrong?
Will greatly appreciate your assistance.
Thanks in advance :)

Try this might work let me know if it doesn't
const user = await User.findById(req.user).populate('Profile');
console.log(user.requests);

try this:
User.findById(req.user).populate({
path: 'requests',
populate: {
path: 'profileId',
model: 'Profile'
}
})

For future readers having the same issue!
I've found a solution for this issue.
I had to change the profileId type to Number in my userSchema:
const userSchema = new mongoose.Schema({
firstName: { type: String },
lastName: { type: String },
requests: [
{
profileId: {
type: Number // and NOT use type: mongoose.Schema.Types.ObjectId,
ref: 'Profile',
},
requestTime: { type: Date, default: Date.now },
},
],
});
module.exports = mongoose.model('User', userSchema, 'users');
Now it works!

Related

type error regarding promise.all with the map [duplicate]

order.js
const mongoose = require('mongoose');
const orderSchema = mongoose.Schema({
orderItems: [{
type: mongoose.Schema.Types.ObjectId,
ref: 'OrderItem',
required:true
}],
shippingAddress1: {
type: String,
required: true,
},
shippingAddress2: {
type: String,
},
city: {
type: String,
required: true,
},
zip: {
type: String,
required: true,
},
country: {
type: String,
required: true,
},
phone: {
type: String,
required: true,
},
status: {
type: String,
required: true,
default: 'Pending',
},
totalPrice: {
type: Number,
},
user: {
type: mongoose.Schema.Types.ObjectId,
ref: 'User',
},
dateOrdered: {
type: Date,
default: Date.now,
},
})
orderSchema.virtual('id').get(function () {
return this._id.toHexString();
});
orderSchema.set('toJSON', {
virtuals: true,
});
exports.Order = mongoose.model('Order', orderSchema);
order-item.js
const mongoose = require('mongoose');
const orderItemSchema = mongoose.Schema({
quantity: {
type: Number,
required: true
},
product: {
type: mongoose.Schema.Types.ObjectId,
ref: 'Product'
}
})
exports.OrderItem = mongoose.model('OrderItem', orderItemSchema);
orders.js
router.post('/', async (req,res)=>{
const orderItemsIds = Promise.all(req.body.orderItems.map(async (orderItem) =>{
let newOrderItem = new OrderItem({
quantity: orderItem.quantity,
product: orderItem.product
})
newOrderItem = await newOrderItem.save();
return newOrderItem._id;
}))
const orderItemsIdsResolved = await orderItemsIds;
let order = new Order({
orderItems: orderItemsIdsResolved,
shippingAddress1: req.body.shippingAddress1,
shippingAddress2: req.body.shippingAddress2,
city: req.body.city,
zip: req.body.zip,
country: req.body.country,
phone: req.body.phone,
status: req.body.status,
totalPrice: totalPrice,
user: req.body.user,
})
order = await order.save();
if(!order)
return res.status(400).send('the order cannot be created!')
res.send(order);
})
const orderItemsIds = Promise.all(req.body.orderItems.map(async (orderItem) =>{
^
TypeError: Cannot read properties of undefined (reading 'map')
How to solve this error? could anyone please help me to find the error I can't find it I have tried in many ways but I failed to solve this.

How to group mongoose results by status, and populate a reference field?

I have a list of task records (see schema below). I am attempting to return records for a specific projectId, group my task records by status, and populate the responsible field. However, the responsible field is not populating. I have attached a code snippet below. Can anyone advise what I am doing incorrectly?
Code Snippet:
const test = await Task.aggregate([
{
$match: { project: { $eq: mongoose.Types.ObjectId(projectId) } },
},
{
$group: {
_id: "$status",
data: {
$push: {
_id: "$status",
name: "$name",
responsible: "$responsible",
endDate: "$endDate",
},
},
},
},
{
$sort: { status: 1 },
},
]);
console.log("test1:", test);
// Populate Aggregated Data:
const tasks = await User.populate(test, { path: "data.responsible" });
console.log("test2:", tasks);
TaskSchema:
const TaskSchema = new mongoose.Schema({
name: {
type: String,
trim: true,
required: true,
},
responsible: {
type: mongoose.Schema.Types.ObjectId,
ref: "User",
},
endDate: {
type: Date,
required: false,
},
status: {
type: String,
enum: ["new", "active", "inactive", "closed"],
required: true,
},
project: {
type: mongoose.Schema.Types.ObjectId,
ref: "Project",
},
});
Desired Results:
results = [
{
_id: “new”
data: [
{
endDate: "2022-09-16T04:00:00.000Z”,
name: "test1”,
responsible: {
email: “jane.doe#ymail.com”,
firstName: “Jane”,
lastName: “Doe”
},
{
endDate: "2022-09-16T04:00:00.000Z”,
name: "test2”,
responsible: {
email: “john.doe#ymail.com”,
firstName: “John”,
lastName: “Doe”
},
]
},

]

Mongoose populate denormalized data

I have a User model and a Book model. I want some data from my books to be denormalized on each User document, but still have the option to populate if needed. If I set ref: 'Book' on the books.$._id it gets populated inside the _id path which is unintended. I would like the population to overwrite the denormalized data.
How do I accomplish this?
in users.model.js:
const { Schema } = require('mongoose');
const UserSchema = new Schema({
name: String,
books: {
type: [
{
_id: Schema.Types.ObjectId,
title: String,
length: Number,
},
],
default: [],
},
});
Desired outcome
in users.controller.js:
app.get('/', async (req, res, next) => {
const users = await User.find({})
/*
users: [{
_id: ObjectId(),
name: 'Andrew',
books: [{
_id: ObjectId(),
title: 'Game of Thrones',
length: 298,
}, { ... }],
}, { ... }]
*/
});
app.get('/:id', async (req, res, next) => {
const book_id = req.params.id;
const user = await User.findById(book_id).populate({
path: 'books',
model: 'Book',
});
/*
user: {
_id: ObjectId(),
name: 'Andrew',
books: [{
_id: ObjectId(),
name: 'Game of Thrones',
length: 298,
author: 'Simone Dunow',
releasedOn: Date(),
price: 30,
...
}, { ... }],
}
*/
});
Schemas I've tried so far:
books: {
type: [
{
_id: Schema.Types.ObjectId,
title: String,
length: Number,
},
],
default: [],
ref: 'Book',
},
returns array of { _id: null }
books: {
type: [
{
_id: {
type: Schema.Types.ObjectId,
ref: 'Book',
},
title: String,
length: Number,
},
],
default: [],
},
books are populated inside of _id: { _id: { Book } }
books: {
type: [
{
type: {
_id: Schema.Types.ObjectId,
title: String,
length: Number,
},
ref: 'Book',
},
],
default: [],
},
throws exception: invalid type
const UserSchema = new Schema({
name: String,
books: [{
id: { type : Schema.Types.ObjectId, ref : 'Book'} //Whatever string you have used while modeling your schema
title: String,
length: Number,
}],
});
While using the schema you can populate as follows :
populate({ path: 'books.id' })
Output :
{
_id : // someid
name : "somename"
books : [
{
id : {//document referring to Books collection},
title : "sometitle",
length : //somelength
}, ...
]
}
To anybody that might be still looking to achieve a full replacement, full disclosure: It might be a bit hacky for some evangelists or even have a performance toll on high traffic apps, but if you really want to do it, you can tap into the toJSON method of the schema like the following:
UserSchema.method('toJSON', function () {
let obj = this.toObject();
obj.books = obj.books.map(
(book) => (Schema.Types.ObjectId.isValid(book.id)) ? book : book.id
);
return obj;
});
What's going on here is basically we're replacing the whole property with the populated result when the book.id has been populated otherwise we just return the original object by checking the validity of the book's id (when populated will be a full bloomed object rather than an id).

findByIdAndUpdate keeps being caught in .catch

So when I am executing my findByIdAndUpdate it doesn't execute my promise as expected and goes into my catch. I sent responses to postman and using res.json(req.user.id) and res.json(profileFields) . This is the response I get when I use
profileFields
{
"user": "5b3134a0e2543b06d130a5d7",
"handle": "wadeaston1",
"status": "Developer",
"skills": [
"HTML",
" CSS",
" Javascipt"
],
"social": {}
}
i'm at a loss here because all my fields are passing in the values as expected into user and $set. I don't understand why its going to my catch
Profile.findByIdAndUpdate(
{ user: req.user.id },
{ $set: profileFields },
{ new: true }
)
.then(profile => res.json(profile))
.catch(err => {
res.json("Timeout");
console.log("HI");
});
Here is my Profile Schema
const mongoose = require("mongoose");
const Schema = mongoose.Schema;
//Create Scheme
const ProfileSchema = new Schema({
user: {
//this will associate user by their ID
type: Schema.Types.ObjectId,
ref: "users"
},
handle: {
type: String,
required: true,
max: 40
},
company: {
type: String
},
website: {
type: String
},
location: {
type: String
},
status: {
type: String,
required: true
},
skills: {
//Array of strings
type: [String],
required: true
},
bio: {
type: String
},
githubusername: {
type: String
},
experience: [
{
title: {
type: String,
required: true
},
company: {
type: String,
required: true
},
location: {
type: String
},
from: {
type: Date,
required: true
},
to: {
type: Date,
required: true
},
current: {
type: Boolean,
default: false
},
description: {
type: String
}
}
],
education: [
{
school: {
type: String,
required: true
},
degree: {
type: String,
required: true
},
fieldofstudy: {
type: String,
required: true
},
from: {
type: Date,
required: true
},
to: {
type: Date,
required: true
},
current: {
type: Boolean,
default: false
},
description: {
type: String
}
}
],
social: {
youtube: {
type: String
},
twitter: {
type: String
},
facebook: {
type: String
},
linkedin: {
type: String
},
instagram: {
type: String
}
},
date: {
type: Date,
default: Date.now
}
});
module.exports = Profile = mongoose.model("profile", ProfileSchema);
findByIdAndUpdate is for finding the document to update by its _id value, but you need to find the document by its user field, so you should be using findOneAndUpdate instead:
Profile.findOneAndUpdate(
{ user: req.user.id },
{ $set: profileFields },
{ new: true }
)
.then(...
No need to manually cast req.user.id to an ObjectId as Mongoose will do that for you based on the way user is defined in your schema.
Based on the error you gave, it looks like you need to turn req.user.id from a string to an ObjectId:
Profile.findByIdAndUpdate(
{ user: new mongoose.Types.ObjectId(req.user.id) },
{ $set: profileFields },
{ new: true }
).then( /* rest of code */
This assumes you did a const mongoose = require('mongoose') or similar.

Populate() ref nested in object array

I am trying to populate() all the subscriptions in my User model with data from the Show model. I have tried .populate('subscriptions.show') but it does nothing to the results.
If I make subscriptions a plain array of Refs like so
subscriptions: [{type: Schema.Types.ObjectId, ref: 'Show'}]
doing a populate('subscriptions') works as intended
I have looked at every similar question I could find on Stackoverflow and what I could find on the docs. I can't see what I am doing wrong.
complete test file source that i am working with https://gist.github.com/anonymous/b7b6d6752aabdd1f9b59
Schema and Models
var userSchema = new Schema({
email: String,
displayName: String,
subscriptions: [{
show: {type: Schema.Types.ObjectId, ref: 'Show'},
favorite: {type: Boolean, default: false}
}]
});
var showSchema = new Schema({
title: String,
overview: String,
subscribers: [{type: Schema.Types.ObjectId, ref: 'User'}],
episodes: [{
title: String,
firstAired: Date
}]
});
var User = mongoose.model('User', userSchema);
var Show = mongoose.model('Show', showSchema);
Initial Data
var user = new User({
email: "test#test.com",
displayName: "bill"
});
user.save(function(err, user) {
var show = new Show({
title: "Some Show",
overview: "A show about some stuff."
});
show.save();
user.subscriptions.push(show);
user.save();
});
The Query
User.findOne({
displayName: 'bill'
})
.populate('subscriptions.show')
.exec(function(err, user) {
if (err) {
console.log(err);
}
console.log(user);
});
results in:
{
_id: 53a7a39d878a965c4de0b7f2,
email: 'test#test.com',
displayName: 'bill',
__v: 1,
subscriptions: [{
_id: 53a7a39d878a965c4de0b7f3,
favorite: false
}]
}
Some code perhaps, also some corrections to your approach. You kind of want a "manyToMany" type of join which you can make as follows:
var async = require("async"),
mongoose = require("mongoose"),
Schema = mongoose.Schema;
mongoose.connect('mongodb://localhost/user');
var userSchema = new Schema({
email: String,
displayName: String,
subscriptions: [{ type: Schema.Types.ObjectId, ref: 'UserShow' }]
});
userShows = new Schema({
show: { type: Schema.Types.ObjectId, Ref: 'Show' },
favorite: { type: Boolean, default: false }
});
var showSchema = new Schema({
title: String,
overview: String,
subscribers: [{ type: Schema.Types.ObjectId, ref: 'User' }],
episodes: [{
title: String,
firstAired: Date
}]
});
var User = mongoose.model('User', userSchema);
var Show = mongoose.model('Show', showSchema);
var UserShow = mongoose.model('UserShow', userShows);
var user = new User({
email: 'test#test.com',
displayName: 'bill'
});
user.save(function(err,user) {
var show = new Show({
title: "Some Show",
overview: "A show about some stuff."
});
show.subscribers.push( user._id );
show.save(function(err,show) {
var userShow = new UserShow({ show: show._id });
user.subscriptions.push( userShow._id );
userShow.save(function(err,userShow) {
user.save(function(err,user) {
console.log( "done" );
User.findOne({ displayName: "bill" })
.populate("subscriptions").exec(function(err,user) {
async.forEach(user.subscriptions,function(subscription,callback) {
Show.populate(
subscription,
{ path: "show" },
function(err,output) {
if (err) throw err;
callback();
});
},function(err) {
console.log( JSON.stringify( user, undefined, 4) );
});
});
});
});
});
});
That should show a populated response much like this:
{
"_id": "53a7b8e60462281231f2aa18",
"email": "test#test.com",
"displayName": "bill",
"__v": 1,
"subscriptions": [
{
"_id": "53a7b8e60462281231f2aa1a",
"show": {
"_id": "53a7b8e60462281231f2aa19",
"title": "Some Show",
"overview": "A show about some stuff.",
"__v": 0,
"episodes": [],
"subscribers": [
"53a7b8e60462281231f2aa18"
]
},
"__v": 0,
"favorite": false
}
]
}
Or without the "manyToMany" works also. Note here that there is no initial call to populate:
var async = require("async"),
mongoose = require("mongoose"),
Schema = mongoose.Schema;
mongoose.connect('mongodb://localhost/user');
var userSchema = new Schema({
email: String,
displayName: String,
subscriptions: [{
show: {type: Schema.Types.ObjectId, ref: 'UserShow' },
favorite: { type: Boolean, default: false }
}]
});
var showSchema = new Schema({
title: String,
overview: String,
subscribers: [{ type: Schema.Types.ObjectId, ref: 'User' }],
episodes: [{
title: String,
firstAired: Date
}]
});
var User = mongoose.model('User', userSchema);
var Show = mongoose.model('Show', showSchema);
var user = new User({
email: 'test#test.com',
displayName: 'bill'
});
user.save(function(err,user) {
var show = new Show({
title: "Some Show",
overview: "A show about some stuff."
});
show.subscribers.push( user._id );
show.save(function(err,show) {
user.subscriptions.push({ show: show._id });
user.save(function(err,user) {
console.log( "done" );
User.findOne({ displayName: "bill" }).exec(function(err,user) {
async.forEach(user.subscriptions,function(subscription,callback) {
Show.populate(
subscription,
{ path: "show" },
function(err,output) {
if (err) throw err;
callback();
});
},function(err) {
console.log( JSON.stringify( user, undefined, 4) );
});
});
});
});
});
check answer: https://stackoverflow.com/a/28180427/3327857
Car .find() .populate({
path: 'partIds',
model: 'Part',
populate: {
path: 'otherIds',
model: 'Other'
}
})

Categories