Mongoose returns null array when populate - node.js

I'm trying to create a webapp with mongoose and nodejs. This is the first time i'm using mongoose and nodejs so i'm not quit good at it.
I have a person model:
var PERSON = new mongoose.Schema({
name: {type: String, required: true},
zipcode: {type: String, required: true},
city: {type: String, required: true},
street: {type: String, required: true},
address: {type: Number, required: true},
email: {type: String, required: true},
type: {type: String, enum: ['lid', 'passant'], required: true},
ships: {
type: [{
name: {type: String, required: true},
length: {type: Number, required: true},
type: {type: String, required: true},
picture: {type: Buffer, required: false}
}],
required: false
},
user: {
type: {
userName: {type: String, required: true},
password: {type: String, required: true},
passwordKey: {type: String, required: true},
roles: {type: [{type: mongoose.Schema.Types.ObjectId, ref: 'role'}], required: true}
},
required: false
}
},
{
collection: 'PERSON'
});
And the rol model:
var ROLE = new mongoose.Schema({
name: {type: String, required: true}
},
{
collection: 'ROLE'
});
When i'm trying to run a find query on the person model, I want to populate the user.roles array. But as a result it remains empty.
/ GET a specific person by id or type/
server.get('/api/person/:id', function (req, res, next) {
if (helper.isValidObjectID(req.params.id)) {
person.findById(req.params.id).populate("user.roles").exec(function (err, result) {
if (err) {
res.status(404).json(err);
} else {
res.status(200).json(result);
}
}
);
} else {
next();
}
}, function (req, res, next) {
person.find({type: req.params.id}).populate('user.roles').exec(function (err, result) {
if (err) {
res.status(404).json(err);
} else {
res.status(200).json(result);
}
});
});
and as a result i get:
{
"_id":"553f79d4f1481c0c14b42d59",
"name":"Wilco Boogert",
"zipcode":"4305RH",
"city":"Ouwerkerk",
"street":"baalpapenweg",
"address":2,
"email":"wilcoboogert17#gmail.com",
"type":"lid",
"user":{
"userName":"wboogert",
"password":"sha1$168fd599$1$502b965cb083ebdfcafb17e655455ef63779e1a1",
"passwordkey":"kfjvlksdfm",
"roles":[
]
},
"__v":0,
"ships":[
{
"name":"titanic",
"length":269,
"type":"Olympic-klasse",
"_id":"553f79d4f1481c0c14b42d5a"
}
]
}
The orginal object in the database is:
{
"_id": {
"$oid": "553f79d4f1481c0c14b42d59"
},
"name": "Wilco Boogert",
"zipcode": "4305RH",
"city": "Ouwerkerk",
"street": "baalpapenweg",
"address": 2,
"email": "wilcoboogert17#gmail.com",
"type": "lid",
"user": {
"roles": [
{
"$oid": "5522996f0fff331ed03cae6c"
}
],
"passwordkey": "kfjvlksdfm",
"password": "sha1$168fd599$1$502b965cb083ebdfcafb17e655455ef63779e1a1",
"userName": "wboogert"
},
"ships": [
{
"name": "titanic",
"length": 269,
"type": "Olympic-klasse",
"picture": "<Binary Data>",
"_id": {
"$oid": "553f79d4f1481c0c14b42d5a"
}
}
],
"__v": 0
}
so as you an see the roles is empty. And I search the web but i cannot find what the problem is. Can annyone help me?

You should replace this line :
roles: {type: [{type: mongoose.Schema.Types.ObjectId, ref: 'role'}], required: true}
with this one :
roles: {type: [{type: mongoose.Schema.Types.ObjectId, ref: 'ROLE'}], required: true}
In fact, the reference must be the exact same name as it's defined :
collection: 'ROLE'
It's case sensitive.

Related

Mongoose: Populate an array of related values

I'm fairly new to Mongo and Mongoose, so if this question is rather silly then feel free to point me in the right direction.
I have a API with models for users and stories that they published. When I display the users, I want the stories to show as well.
The relation is drawn rather simply:
const userSchema = mongoose.Schema({
username: {
type: String,
unique: true,
},
password: String,
firstName: {
type: String,
required: false,
},
lastName: {
type: String,
required: false,
},
birthday: {
type: Date,
},
bio: {
type: String,
required: false,
},
country: {
type: mongoose.Schema.Types.ObjectId,
ref: 'Country',
},
stories: [{
type: mongoose.Schema.Types.ObjectId,
ref: 'Story',
}]
})
With a stories scheme declared as such:
const storySchema = new mongoose.Schema({
name: {
type: String,
max: 255,
},
url: {
type: String,
unique: true,
},
description: {
type: String,
required: false,
},
length: {
type: Number,
default: 0,
},
createdAt: Date,
author: {
type: mongoose.Schema.Types.ObjectId,
ref: 'User',
},
language: {
type: mongoose.Schema.Types.ObjectId,
ref: 'Country'
},
type: {
type: mongoose.Schema.Types.ObjectId,
ref: 'StoryType'
},
genre: {
type: mongoose.Schema.Types.ObjectId,
ref: 'Genre'
},
})
I print the values using the simple find method (the action for now is only meant to show all of them.
app.get('/list-users', async (req, res) => {
const users = await User.find().populate('country').populate('stories').exec();
res.json(users);
});
Unfortunately, although the results show all the values properly, the stories array does not get populated at all.
[
{
"_id": "6127ce5b0576979256fd3e08",
"username": "test",
"password": "123",
"firstName": "tst",
"lastName": "tst",
"birthday": "1995-11-02T00:00:00.000Z",
"country": {
"_id": "6127c020f95eb5abf72f713a",
"name": "Poland",
"languange": "Polish",
"__v": 0
},
"stories": [],
"__v": 0
}
]
The docs have a clear indicator that such schemas will return an empty array, and I am aware I could just run a find by User and populate with that but I'm wondering what is the cleanest solution to apply in a situation like this.
Any help would be of great value.

Mongoose retrieves null of my document inner array

I have a collection that one of it's fields is an array of strings. but when I query with find(), it returns null while in the MongoDB Compass I can see it.
here is my document:
{
"_id":"5d4894f23f86a41b84303795",
"position":1,
"garmentTitle":"My first Garment",
"garmentURL":"www.firstgarment.com",
"garmentPictureURL":"/images/first.png",
"brand":"5d49e60e4eface2a00ac58d7",
"garmentMaker":"5d49e8854eface2a00ac58d9",
"garmentPurpose":"5d49e8754eface2a00ac58d8",
"gender":"5d37546f2f8c280adc60b3fe",
"garmentCategory":"5d3a4c7f447a7a3afc71a746",
"fabricComposition":null,
"garmentSizes":["5d4d211f0fe9591facb9a268"] // <<== This is mentioned array
}
Here is my defined schema :
var mongoose = require('mongoose');
var gardataSchema = new mongoose.Schema({
position: {
type: Number,
unique: true,
required: true
},
garmentTitle: {
type: String,
unique: true,
required: true
},
garmentURL: {
type:String,
required:false
},
garmentPictureURL: {
type: String,
required:false,
},
brand:{
type: mongoose.Schema.Types.ObjectId,
ref: 'BR',
required: false
},
garmentMaker: {
type: mongoose.Schema.Types.ObjectId,
ref: 'GM',
required: false
},
garmentPurpose: {
type: mongoose.Schema.Types.ObjectId,
ref: 'GP',
required: false
},
garmentSizes: [{
type: mongoose.Schema.Types.ObjectId,
ref: 'GS',
required: false
}],
gender: {
type: mongoose.Schema.Types.ObjectId,
ref: 'GN',
required: false
},
garmentCategory: {
type: mongoose.Schema.Types.ObjectId,
ref: 'GC',
required: false
},
fabricComposition: {
type: mongoose.Schema.Types.ObjectId,
ref: 'FC',
required: false
},
});
var collectionName = 'garmentData';
mongoose.model('GD', gardataSchema, collectionName);
And here is my controller:
module.exports.getOneGarmentDataByID = function (req, res) {
GD.find({_id:req.params.id})
.exec()
.then(result => {
res.status(200).json(result);
})
.catch(err => {
console.log(err);
res.status(500).json({error:err});
});
}
Requested route:
http://localhost:3000/gar/getonegarmentdatabyid/5d4894f23f86a41b84303795
And final result is :
[
{
"garmentSizes": null, <<== returns null!!!
"_id": "5d4894f23f86a41b84303795",
"position": 1,
"garmentTitle": "My first Garment",
"garmentURL": "www.firstgarment.com",
"garmentPictureURL": "/images/first.png",
"brand": "5d49e60e4eface2a00ac58d7",
"garmentMaker": "5d49e8854eface2a00ac58d9",
"garmentPurpose": "5d49e8754eface2a00ac58d8",
"gender": "5d37546f2f8c280adc60b3fe",
"garmentCategory": "5d3a4c7f447a7a3afc71a746",
"__v": 0,
"fabricComposition": null
}
]
As you can see it returns null instead of array object.
Do you have any idea?
Schema type is wrong. Should be:
garmentSizes: {
type: [
{
type: mongoose.Schema.Types.ObjectId,
ref: 'GS'
}
],
required: false
}

Node.js with mongoose delete one array element

I am trying to delete one array element when I click delete button on jade view page.
When clicked, it's going to send selected instructor objected as req.body.
At sever side, it will find courses that contain the instructor objectId.
Any idea for me?
Thank you for reading it.
here is my code:
var id = req.body._id;
clist.find({ instructors: { $in: [id] } }).exec(function (err, result) {
result.forEach(function (obj) {
clist.update(
{ _id: new mongoose.Types.ObjectId(obj._id)},
{ $pull: { instructors : [new mongoose.Types.ObjectId(id)] } }
);
console.log(new mongoose.Types.ObjectId(obj._id) + ' was deleted');
});
});
Schema Clist and ilist:
var instructorlist = mongoose.Schema({
name: { type: String, required: true },
age: { type: Number, required: true },
gender: { type: String, required: true },
DOB: { type: Date, required: true, default: Date.now },
email: { type: String, required: true },
phone: { type: Number, required: true },
address: { type: String, required: true },
dateofstart: { type: Date, required: true},
courses: [{
type: mongoose.Schema.Types.ObjectId,
ref: "clist"
}]
});
var courselist = mongoose.Schema({
coursename: { type: String, required: true },
coursenumber: { type: String, required: true },
coursecredit: { type: Number, required: true },
courseroom: { type: String, required: false },
courseregisteddate: {type: Date, default: Date.now},
students: [{
type: mongoose.Schema.Types.ObjectId,
ref: "slist"
}],
instructors: [{
type: mongoose.Schema.Types.ObjectId,
ref: "ilist"
}]
});
one example for mongodb :
{
"_id": {
"$oid": "591a7a3b391a1842e8a69e23"
},
"coursename": "JDKD",
"coursenumber": "COMP4483",
"coursecredit": 4,
"courseroom": "sdaf",
"instructors": [
{
"$oid": "591a374422a3a13d38c0bbe5"
}
],
"students": [],
"courseregisteddate": {
"$date": "2017-05-16T04:04:11.848Z"
},
"__v": 0
}
When I add instructor objectID in Course.
var newcourse = new clist({
'coursename': req.body.coursename, 'coursenumber': req.body.coursenumber, 'coursecredit': req.body.coursecredit
, 'courseroom': req.body.room, 'instructors': instructors._id
});
Use same operation to find and update multiple
clist.update(
{ instructors: { $in: [id] }},
{ $pull: { instructors : { _id : new mongoose.Types.ObjectId(id) } } }, //or{ $pull: { instructors: mongoose.Types.ObjectId(id) } }
{
multi:true
},
function(error, success){
if(error){
console.log("error",error)
}
console.log("success",success)
});

Mongodb, search for text and geolocation in different collections

I have 3 collections: business, sevice, employee.
I need search by service (without fulltext), by employee and by the geolocation of each business, and should show only business.
var BusinessSchema = new Schema({
business_id: {
type: String,
required: true,
unique:true
},
name: {
type: String,
required: true
},
email: {
type: String,
},
description:{
type: String
},
location:{
country:{
type:String,
},
city:{
type:String
},
coord:[Number]
}
services:[{
type: Schema.Types.ObjectId,
ref: 'Service'
}]
},
{
timestamps: true
});
var ServiceSchema = new Schema({
business:{
type: Schema.Types.ObjectId,
ref: 'Business'
},
category:{
type: Schema.Types.ObjectId,
ref: 'Category',
index:true
},
name: {
type: String,
required: true,
index:true
},
employee: [{
type: Schema.Types.ObjectId,
ref: 'User'
}],
{
timestamps: true
});
var UserSchema = new Schema({
birthday:Date,
first_name: {
type: String,
required: true
},
last_name: {
type: String,
required: true
},
email: {
type: String,
unique: true,
match: [/^\w+([\.-]?\w+)*#\w+([\.-]?\w+)*(\.\w{2,3})+$/, 'Please fill a valid email address'],
required: true
},
password:{
type: String
},
{
timestamps: true
});
What changes should i make collections to optimize the query?
Service.distinct('business',filter_services)
.exec(function (err, business) {
if (err) {
return cb({ status: 400, message: err }, null);
} else {
if(business.length > 0){
var filter_business = [{is_active:true},{is_approved:true}]
filter_business.push({_id:{$in:business}})
filter_business.push({location.coord:input_coord}})
filter_business = {$and:filter_business}
Business.find(filter_business)
.select('name services')
.exec(function (err,result){
if(err){
return cb({ status: 400, message: err }, null);
}
else{
if(result.length > 0){
var total = result.length;
}
return cb(null, result);
}
})
}
// si no hay business en el primer query, se retorna [].
else{
return cb(null, business);
}
}
});
Could geo filter by text and at the same time to get closer to a point?
For now, i am not using the Employee collection, but, if i would search by business name, employee name and service name simultaneously, what changes should make.
Your business model should be
var BusinessSchema = new Schema({
business_id: {
type: String,
required: true,
unique:true
},
name: {
type: String,
required: true
},
email: {
type: String,
},
description:{
type: String
},
address:{
country:{
type:String,
},
city:{
type:String
}
},
location : {
type: [Number],
index: '2dsphere'
},
services:[{
type: Schema.Types.ObjectId,
ref: 'Service'
}],
employees:[{
type: Schema.Types.ObjectId,
ref: 'User'
}]
},
{
timestamps: true
});
The change in schema is you have to create index on location, and for find business based on employee have to add employees id in business schema.Then you can you geo near query of mongodb.

Mongoose query reference

I have the below schema structure for my application:
var User = new Schema({
name: {type: String, required: true},
screen_name: {type: String, required: true, index:{unique:true}},
email: {type: String, required: true, unique:true},
created_at: {type: Date, required: true, default: Date}
});
var Line = new Schema({
user: {type: Schema.ObjectId, ref: 'User'},
text: String,
entered_at: {type: Date, required: true, default: Date}
});
var Story = new Schema ({
sid: {type: String, unique: true, required: true},
maxlines: {type: Number, default: 10}, // Max number of lines per user
title: {type: String, default: 'Select here to set a title'},
lines: [Line],
created_at: {type: Date, required: true, default: Date}
});
var Story = db.model('Story', Story);
var User = db.model('User', User);
When i query a Story using the sid i want it to output the lines in that story but also the users screen_name.
Currently i'm using the below to query the story:
app.get('/api/1/story/view/:id', function(req, res){
Story.findOne({ sid: req.params.id }, function(err, story){
if (err) {
res.json(err);
}
else if(story == null) {
res.json(err);
}
else{
res.send(story);
}
});
});
Which just brings back the result like this:
{
"__v": 1,
"_id": "5117878e381d7fd004000002",
"sid": "DIY0Ls5NwR",
"created_at": "2013-02-10T11:42:06.000Z",
"lines": [
{
"user": "511782cab249ff5819000002",
"text": "TESTING",
"_id": "51178795381d7fd004000003",
"entered_at": "2013-02-10T11:42:13.000Z"
}
],
"title": "Select here to set a title",
"maxlines": 10
}
There is a user set-up with that _id but i'm not entirely sure how to output the story and for it to include the users screen_name or even the entire user information along with the output
{
"__v": 0,
"screen_name": "Tam",
"email": "test#test.com",
"name": "Tam",
"_id": "511782cab249ff5819000002",
"created_at": "2013-02-10T11:21:46.000Z"
}
I haven't tested it, but you should be able to use populate to get the full user doc for each lines element like this:
Story.findOne({sid: req.params.id})
.populate('lines.user')
.exec(function(err, story){ ...

Resources