Populate() ref nested in object array - javascript

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'
}
})

Related

Mongo DB - MissingSchemaError

I am working with mongo DB and mongoose and I'm getting the following error when running the code below.
"MissingSchemaError: Schema hasn't been registered for model "Project".\nUse mongoose.model(name, schema)"
import {...}
const { school } = request.params;
const document = await Document.find({
}).populate('project').lean();
if (document.project.school != school)
throw HTTP.forbidden('ERROR.DOCUMENT_DOES_NOT_BELONG_TO_YOUR_SCHOOL');
My schemas look as follows
const Document = new Schema({
name: { type: String },
type: { type: String },
project: {
type: Schema.Types.ObjectId,
ref: 'Project'
}
}, {
timestamps: true
});
const Project = new Schema({
name: { type: String },
school: {
type: Schema.Types.ObjectId,
ref: 'School'
}
}, {
timestamps: true
});
const School = new Schema({
name: { type: String },
curriculum: [{
type: Schema.Types.ObjectId,
ref: 'Curriculum'
}],
}, {
timestamps: true
});
Does anyone know what I need to do to overcome this everything has been initialised by the time this section of code is getting called?
Assuming you have created model for each schema, try writing your refs using lowercase:
const Document = new Schema({
name: { type: String },
type: { type: String },
project: {
type: Schema.Types.ObjectId,
ref: 'project'
}
}, {
timestamps: true
});
const Project = new Schema({
name: { type: String },
school: {
type: Schema.Types.ObjectId,
ref: 'school'
}
}, {
timestamps: true
});
const School = new Schema({
name: { type: String },
curriculum: [{
type: Schema.Types.ObjectId,
ref: 'curriculum'
}],
}, {
timestamps: true
});

i want to check if current user is following other user

i want to check if current user is following other use lets say check if user A is following user B.
User Model:-
const UserSchema = new Schema({
email: {
required: true,
type: String,
unique: true,
lowercase: true,
validate: (value) => {
if (!validator.isEmail(value)) {
throw new Error('Invalid email address.');
}
}
},
fullName: {
required: true,
type: String,
},
username: {
required: true,
type: String,
unique: true,
lowercase: true,
minlength: 3,
},
password: {
type: String,
minlength: 8,
},
avatar: String,
bio: {
type: String,
default: null,
maxlength:300,
},
location: {
type: String,
default: 'Bangalore'
},
website: {
type: String,
default: null,
},
joindate: {
type: Date,
default: new Date()
},
isVerified:{
type:Boolean,
default:false,
}
})
const UserModel = mongoose.model('User', UserSchema);
module.exports = UserModel;
Followings Model:-
const FollowingsSchema = new Schema({
user: {
ref: 'User',
unique:true,
type: Schema.Types.ObjectId,
},
followings: [{
user: {
type: Schema.Types.ObjectId,
ref: 'User'
}
}]
})
const Followings = mongoose.model('Followings', FollowingsSchema);
module.exports = Followings;
Followers Model:-
const FollowersSchema = new Schema({
user: {
ref: 'User',
unique:true,
type: Schema.Types.ObjectId,
},
followers: [{
user: {
type: Schema.Types.ObjectId,
ref: 'User'
}
}]
})
const Followers = mongoose.model('Followers', FollowersSchema);
module.exports = Followers;
currently i was able to achieve this by iterating through each follower and check if user exist in that user followers list.
i want to achieve this with mongodb aggregation ,im new to mongob

Mongoose One-to-Many, How to get field value of category

I have 2 collection: Category and book
Book:
const mongoose = require('mongoose');
// eslint-disable-next-line camelcase
const mongoose_delete = require('mongoose-delete');
const { Schema } = mongoose;
const SchemaTypes = mongoose.Schema.Types;
const Book = new Schema({
name: { type: String },
price: { type: Number },
images: { type: Array },
country: { type: String },
author: { type: String },
publicationDate: { type: String },
description: { type: String },
category: { type: SchemaTypes.ObjectId, ref: 'Category' },
date: { type: Date, default: Date.now },
}, {
timestamps: true,
});
Book.plugin(mongoose_delete);
Book.plugin(mongoose_delete, { overrideMethods: 'all', deletedAt: true });
module.exports = mongoose.model('Book', Book);
Category:
const mongoose = require('mongoose');
// eslint-disable-next-line camelcase
const mongoose_delete = require('mongoose-delete');
const SchemaTypes = mongoose.Schema.Types;
const { Schema } = mongoose;
const Category = new Schema({
name: { type: String, required: true },
image: { type: String },
products: [{ type: SchemaTypes.ObjectId, ref: 'Book' }],
date: { type: Date, default: Date.now },
}, {
timestamps: true,
});
Category.plugin(mongoose_delete);
Category.plugin(mongoose_delete, { overrideMethods: 'all', deletedAt: true });
module.exports = mongoose.model('Category', Category);
and I select all the books received on the list:
{
_id: new ObjectId("623668ac5b1d392b37690cbc"),
name: 'The Godfather',
price: 110000,
country: 'U.S',
publicationDate: '1969',
category: new ObjectId("6238fdf64f60303756b60b20"),
author: 'Mario Puzo',
... : ...
}
And I want to display the category name on the product list, how do I do it?
**like: {{book.category.name}}
{{book.category.image}}
?**
Since you are using Mongoose, you can use populate() method. You can do it like this:
const books = await Books.find().populate('category')
try $lookup (aggregation)
Book.aggregate([{
$lookup: {
from: "Category",
localField: "category",
foreignField: "_id",
as: "category"
}
}]).exec(function(err, students) {
});

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

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!

mongoose find with $regex not return expected result if value stored with special character

I am trying to build a search function that searches on username or full name ... it works fine but if username like this "example.name" or "example_name" it did not return in result if I searched like this "examplename" or "example name" how to solve this problem to return a matched characters even that special characters
User Schema
const mongoose = require("mongoose");
const bcrypt = require("bcrypt-node");
const uniqueValidator = require("mongoose-unique-validator");
const schemaTypes = mongoose.Schema.Types;
const userSchema = new mongoose.Schema(
{
firstName: { type: String, required: true, trim: true },
lastName: { type: String, trim: true },
fullName: {type: String, trim: true},
username: { type: String, trim: true, unique: true, lowercase: true },
email: {
type: String,
unique: true,
trim: true,
lowercase: true,
required: true
},
password: { type: String, select: false },
gender: { type: String, default: "male" },
birthDate: Number,
location: { type: [Number], index: '2d' }, // [<longitude>, <latitude>]
bio: {type: String, default: ""},
profilePhoto: {type: String, default: "default-user-profile.jpg"},
private: { type: Boolean, default: false },
favouriteUsers: [{ type: schemaTypes.ObjectId, ref: "users" }],
blockedUsers: [{ type: schemaTypes.ObjectId, ref: "users" }],
tempPassword: String,
tempToken: String,
socialId: String,
favouriteRequests: [{ type: schemaTypes.ObjectId, ref: "users" }],
requests: [{ type: schemaTypes.ObjectId, ref: "users" }],
followers: [{ type: schemaTypes.ObjectId, ref: "users" }],
following: [{ type: schemaTypes.ObjectId, ref: "users" }],
playerId: {type: [String], default: []}
},
{ timestamps: true }
);
userSchema.plugin(uniqueValidator, { message: "This {VALUE} is used" });
userSchema.index({ "$**": "text" });
userSchema.pre("save", function(next) {
const user = this;
if (!user.isModified("password")) return next();
if (user.password) {
bcrypt.hash(user.password, null, null, function(err, hashedPassword) {
if (err) {
return;
}
user.password = hashedPassword;
next();
});
}
});
userSchema.pre("save", function(next) {
const user = this;
if (!user.isModified("firstName") && !user.isModified("lastName")) return
next();
if (user.firstName || user.lastName) {
user.fullName = user.firstName + " " + user.lastName;
next();
}
});
userSchema.methods.comparePassword = function(password) {
return bcrypt.compareSync(password, this.password);
};
module.exports = mongoose.model("users", userSchema);
Code
function search(keyword){
return new Promise((resolve,reject) => {
const User = require('../models/users');
let str = keyword.replace(/[`~!#$%^&*()|+\=?;:'",<>\{\}\[\]\\\/]/gi, "");
let key = new RegExp(str, "ig");
User.find({
$or: [
{ fullName: { $regex: key } },
{ username: { $regex: key } },
{ firstName: { $regex: key } },
{ lastName: { $regex: key } },
{ email: { $regex: key } }
]
}).exec(async (err, users) => {
if(err) return reject(err);
resolve(users);
})
})
}
Example 1
if keyword entered "ahmed ibrahim" or "ahmed.ibrahim" result is okay as expected
result for example 1
[
{
"_id": "5b40d19ae4fc082ca8f2ff3b",
"profilePhoto": "http://localhost/public/male-image.jpg",
"firstName": "Ahmed",
"lastName": "Ibrahim",
"username": "ahmed.ibrahim65356",
"email": "example#gmail.com",
"fullName": "Ahmed Ibrahim"
}
]
Exmaple 2 the problem
if the keyword is "ahmedibrahim"
I expected the previous result put returns nothing an empty array [] hot to solve this or any suggestion match characters even contain special characters
It won't work basically because you are searching for "ahmedibrahim" and it cannot relate with any of the fields in your document.
So for this specific scenario, what I would do is create a new field say "concatFullName" which should save as firstname+lastname, then use that field contactFullName in your find ie.
contactFullName : { $regex: key }
Try below sample for aggregate,
db.getCollection('test').aggregate({
$project: {
docs: "$$ROOT",
concatname: {
$concat: [
'$firstName',
'$lastName',
]
}
}
}, {
$match: {
concatname: {
$regex: "^ahmedIbrahim$",
$options: "i"
},
}
}, {
$group: {
_id: "$_id",
docs: {
$push: "$docs"
}
}
});

Categories