Mongoose populate not returning results - node.js

I am trying to use populate to return results that are ref to the Stamp model, under the users array of stamps but for some reason it does not return any results when I see in the database a list of stamp ids in the stamps array...
Here is my code:
var selectQuery = "_id name";
var populateQuery = [{path:'stamps', select: selectQuery, model: 'Stamp', }];
User.findOne({_id: userId}).populate(populateQuery).sort({date: -1}).skip(count).limit(100).exec(function(err, results) {
if(err) {
Here is the User Schema
var mongoose = require('mongoose'),
Schema = mongoose.Schema,
ObjectId = mongoose.Schema.Types.ObjectId,
var Stamp = require('../models/stamp.js');
var User = new Schema({
name: { type: String},
stamps: [{ type: ObjectId, ref: 'Stamp' }],

The "query" form of populate doesn't take an array as argument, but an object:
// `model` can be left out as Mongoose will look that up in the schema
var populateQuery = { path : 'stamps', select : selectQuery };

Related

How can I updateMany mongodb

I have three chatrooms and I would like to update the document with MongoDB only if the user matches with the members.user_id. I don't understand, it's updating to all the member's devices the same token. If someone has an idea?
It's my Node.js code :
const _id = req.params._id;
const token = req.body.token;
const user_id = req.body.user_id;
try{
const updateDevice = await ChatRoom.updateMany(
{"members.$[].user_id" : user_id},
{$set:{"members.$[].devices":token}})
res.send(updateDevice)
}catch(err){console.log(err)}
const mongoose = require('mongoose');
const chatRoom_schema = new mongoose.Schema({
name:{
type:Array,
name:String,
},
members:{
user_id:String,
name:String,
devices:String,
type:Array,
required:true
},
lastMessage:{
content:String,
createdAt:Date,
type:Array,
send_by:String,
readBy:Array
}
}, {
collection: "chatRoom"
})
module.exports = chatRoom = mongoose.model("ChatRoom", chatRoom_schema);
According to the schema there is a object of members not an array of object so to access the specific elements only dot(.) operator is used. If you want to access any element from the array or want to update the specific object value in array of object then $ is used.
Try this query to solve the problem
ChatRoom.updateMany(
{"members.user_id" : user_id},
{$set:{"members.devices":token}})
Let there be a record like
members=[
{
"user_id":"1",
"name":"DD",
"type":"ADMIN"
},
{
"user_id":"2",
"name":"HH",
"type":"CUSTOMER"
}
]
To update the type of user_id(2) from CUSTOMER to ADMIN then $ operator can be useful.
The query can be
ChatRoom.updateMany({"members.$.user_id" : 2},
{$set:{"members.$.type":"ADMIN"}})

MongoDB updating child document does not affect the original document and vise versa

I'm using mongoose and have these schema:
var mongoose = require('mongoose');
mongoose.connect(/*...*/)
const usersSchema = new Schema({
email: String,
name: String
})
const ordersSchema = new Schema({
user: usersSchema, // <--- ref to usersSchema
createdOn: Date,
// ...
})
const UsersModel = mongoose.model('Users', usersSchema );
const OrdersModel = mongoose.model('Orders', ordersSchema );
The problem is when I insert an entity into Users collection and put that entity reference into the Orders.user field, It seems that Mongo does clone that object in the Orders collection.
// insert user:
var savedUser = await(new UsersModel({
_id: mongoose.Types.ObjectId(),
email: 'AAA#example.com'
})).save();
// insert order with user reference:
var savedOrder = await(new OrdersModel({
_id: mongoose.Types.ObjectId(),
createdOn: new Date(),
user: savedUser // <--- ref to users
})).save();
Now I modify the user document:
// update original user:
var userDocToModify = await UsersModel.findById(savedUser._id);
userDocToModify.email = "BBB#example.com";
await userDocToModify.save();
Assertion will fail in the below line:
var orderDoc = await OrdersModel.findById(savedOrder._id);
assert(orderDoc.user.email == userDocToModify.email, 'email not changed in the orderDoc.user.email!');
Actually what you are doing here, is not referencing userSchema. You are embedding that schema into orders schema. If you want to reference user you should do as below:
const ordersSchema = new Schema({
user: {type: mongoose.Schema.Types.ObjectId, ref: 'Users'}
createdOn: Date,
// ...
})
In this case you just store the user id in parent document.
Now if you want to get orders and the user within it, you can either use mongoose population or $lookup in mongoose aggregation.

Create a new document using MEAN mongoose

i'm new to mongoose and mongodb, i'm trying to create a new document inside my collection.
I'm able to update existing one or get all the document from the collection.
I need to create a new document based on the collection (I have a single collection now). - createMoment function.
var mongoose = require('mongoose');
var db = mongoose.connect('mongodb://url and pass');
var usersSchema = require('./users_schema').usersSchema;
mongoose.model('usersM', usersSchema);
var usersAction;
mongoose.connection.once('open',function(){
var Users = this.model('usersM');
var query = Users.find();
query.where('userId');
query.exec(function(err, docs){
usersAction = docs;
console.log("docs: " + usersAction);
return usersAction;
});
});
exports.getData = function(){
return usersAction;
};
exports.createMoment = function(_fn, _ln, _conc,_lan,_lat,cb){
var Users = mongoose.model('usersM');
console.log("createMoment Called");
Users.insertOne({
'leadRank':1,
'adminApproval':true,
'userFname':_fn,
'userLname':_ln,
"conclusion":_conc,
'tentCoor':{'latitude' : _lat, 'longitude' : _lan}
}, null, function(err, result) {
assert.equal(err, null);
console.log("Inserted a document into the users collection.");
cb(result);
});
};
My schema:
I'm not passing some of the fields yet
var mongoose = require('mongoose');
var schema = mongoose.Schema;
var usersSchema = new schema({
campName: {type:String, required:true},
centerCoor: [{
latitude:{type:Number},
longitude:{type:Number}
}],
zoom: {type:String},
users: [{
leadRank: {type:Number},
adminApproval: Boolean,
userFname: {type:String, required:true},
userLname: {type:String, required:true},
conclusion: {type:String},
tentCoor: [{
latitude:{type:Number},
longitude:{type:Number}
}]
}]
}, {collection: 'camps'});
exports.usersSchema = usersSchema;

how to implement the function like left join of mysql in mongoose

I am going to implement the function like left join of mysql in mongoose.
the date is
var mongoose = require('mongoose')
, Schema = mongoose.Schema
var personSchema = Schema({
_id : Number,
name : String
});
var storySchema = Schema({
_creator : { type: Number, ref: 'Person' },
title : String
});
var personProfile = Schema({
userid : {type: Number, ref: 'Person'},
birthday: Date,
profilelink: String,
email: String
});
var Story = mongoose.model('Story', storySchema);
var Person = mongoose.model('Person', personSchema);
var personProfile = mongoose.model('Personprofile', personProfile );
I am going to display the Story model with the user profile.
We have to get the profile info with the _creator of story and the userid of personProfile
How can I get the info using mongoose query?
Thanks Nelis
What your are trying to do is not possible because there is no join statement on mongodb.
You can achieve this in two ways:
1 - By DBRefs: Changing your Schema to one that include all the user info and do not split them in two different schemas as you are doing, see denormalized. Then you can use the Population function to get all the persons data.
2 - By Manual references: The second solution is to make a second call to the database getting the personProfile data using the userid as a filter.
Example 1:
This way you can get all persons data without a second call to the database.
var personSchema = Schema({
_id : Number,
name : String,
birthday: Date,
profilelink: String,
email: String
});
var storySchema = Schema({
_creator : { type : Schema.Types.ObjectId, ref: 'Person' },
title : String
});
Story
.find()
.populate(['_creator'])
.exec(function(err, stories) {
//do your stuff here
}
Notice that I'm using the type Schema.Types.ObjectId and not the Number. This way, you can assign a new value to _creator passing either the _id or the person object and the mongoose will convert the object to its _id. For example, you can post something like
{
_creator : {
_id : 123123123123,
name : 'Foo',
birthday: '0000-00-00',
profilelink: 'http://foo.bar',
email: 'foo#bar.com'
},
title : 'Mr'
}
... and the mongoose will convert to
{
_creator : 123123123123,
title : 'Mr'
}
Example 2:
This way your data still normalized and you can get all the persons data with a second call.
Story
.find()
.exec(function(err, stories) {
var arrayLength = stories.length;
for (var i = 0; i < arrayLength; i++) {
var story = stories[i];
personProfile.findById(story._creator, function (err, person) {
story._creator = person;
}
};
// do your stuff here
}

How to set ObjectId as a data type in mongoose

Using node.js, mongodb on mongoHQ and mongoose. I'm setting a schema for Categories. I would like to use the document ObjectId as my categoryId.
var mongoose = require('mongoose');
var Schema = mongoose.Schema,
ObjectId = Schema.ObjectId;
var Schema_Category = new Schema({
categoryId : ObjectId,
title : String,
sortIndex : String
});
I then run
var Category = mongoose.model('Schema_Category');
var category = new Category();
category.title = "Bicycles";
category.sortIndex = "3";
category.save(function(err) {
if (err) { throw err; }
console.log('saved');
mongoose.disconnect();
});
Notice that I don't provide a value for categoryId. I assumed mongoose will use the schema to generate it but the document has the usual "_id" and not "categoryId". What am I doing wrong?
Unlike traditional RBDMs, mongoDB doesn't allow you to define any random field as the primary key, the _id field MUST exist for all standard documents.
For this reason, it doesn't make sense to create a separate uuid field.
In mongoose, the ObjectId type is used not to create a new uuid, rather it is mostly used to reference other documents.
Here is an example:
var mongoose = require('mongoose');
var Schema = mongoose.Schema,
ObjectId = Schema.ObjectId;
var Schema_Product = new Schema({
categoryId : ObjectId, // a product references a category _id with type ObjectId
title : String,
price : Number
});
As you can see, it wouldn't make much sense to populate categoryId with a ObjectId.
However, if you do want a nicely named uuid field, mongoose provides virtual properties that allow you to proxy (reference) a field.
Check it out:
var mongoose = require('mongoose');
var Schema = mongoose.Schema,
ObjectId = Schema.ObjectId;
var Schema_Category = new Schema({
title : String,
sortIndex : String
});
Schema_Category.virtual('categoryId').get(function() {
return this._id;
});
So now, whenever you call category.categoryId, mongoose just returns the _id instead.
You can also create a "set" method so that you can set virtual properties, check out this link
for more info
I was looking for a different answer for the question title, so maybe other people will be too.
To set type as an ObjectId (so you may reference author as the author of book, for example), you may do like:
const Book = mongoose.model('Book', {
author: {
type: mongoose.Schema.Types.ObjectId, // here you set the author ID
// from the Author colection,
// so you can reference it
required: true
},
title: {
type: String,
required: true
}
});
My solution on using ObjectId
// usermodel.js
const mongoose = require('mongoose')
const Schema = mongoose.Schema
const ObjectId = Schema.Types.ObjectId
let UserSchema = new Schema({
username: {
type: String
},
events: [{
type: ObjectId,
ref: 'Event' // Reference to some EventSchema
}]
})
UserSchema.set('autoIndex', true)
module.exports = mongoose.model('User', UserSchema)
Using mongoose's populate method
// controller.js
const mongoose = require('mongoose')
const User = require('./usermodel.js')
let query = User.findOne({ name: "Person" })
query.exec((err, user) => {
if (err) {
console.log(err)
}
user.events = events
// user.events is now an array of events
})
The solution provided by #dex worked for me. But I want to add something else that also worked for me: Use
let UserSchema = new Schema({
username: {
type: String
},
events: [{
type: ObjectId,
ref: 'Event' // Reference to some EventSchema
}]
})
if what you want to create is an Array reference. But if what you want is an Object reference, which is what I think you might be looking for anyway, remove the brackets from the value prop, like this:
let UserSchema = new Schema({
username: {
type: String
},
events: {
type: ObjectId,
ref: 'Event' // Reference to some EventSchema
}
})
Look at the 2 snippets well. In the second case, the value prop of key events does not have brackets over the object def.
You can directly define the ObjectId
var Schema = new mongoose.Schema({
categoryId : mongoose.Schema.Types.ObjectId,
title : String,
sortIndex : String
})
Note: You need to import the mongoose module
Another possible way is to transform your _id to something you like.
Here's an example with a Page-Document that I implemented for a project:
interface PageAttrs {
label: string
// ...
}
const pageSchema = new mongoose.Schema<PageDoc>(
{
label: {
type: String,
required: true
}
// ...
},
{
toJSON: {
transform(doc, ret) {
// modify ret directly
ret.id = ret._id
delete ret._id
}
}
}
)
pageSchema.statics.build = (attrs: PageAttrs) => {
return new Page({
label: attrs.label,
// ...
})
}
const Page = mongoose.model<PageDoc, PageModel>('Page', pageSchema)
Now you can directly access the property 'id', e.g. in a unit test like so:
it('implements optimistic concurrency', async () => {
const page = Page.build({
label: 'Root Page'
// ...
})
await page.save()
const firstInstance = await Page.findById(page.id)
const secondInstance = await Page.findById(page.id)
firstInstance!.set({ label: 'Main Page' })
secondInstance!.set({ label: 'Home Page' })
await firstInstance!.save()
try {
await secondInstance!.save()
} catch (err) {
console.error('Error:', err)
return
}
throw new Error('Should not reach this point')
})

Resources