mongoose populate as an instance object - node.js

When using mongoose's populate, you can get the document back but it is not an object instance where your schema methods are defined.
var tweetSchema = new mongoose.Schema({
owner: { type: Schema.Types.ObjectId, ref: 'User' },
message: { type: String },
});
var userSchema = new mongoose.Schema({
email: { type: String, unique: true, lowercase: true },
});
userSchema.methods.test = function(){
return 'test'
}
Tweet.findOne({}).populate('user').exec(function(err,tweet){
console.log(tweet.user.test);
});
This will result in an error 'Cannot read property 'userTwit' of undefined' as tweet.user is just a hash and not a document instance. Is there a way to make population return the document instance???
What would be a nice way to make something like this possible?

Unless you use lean(), the populated fields are doc instances.
Looks like your User field in tweetSchema is called owner, not user, so this should work:
Tweet.findOne({}).populate('owner').exec(function(err, tweet){
console.log(tweet.owner.test());
});

Related

MongoDB populate returning null

I am trying to populate my user schema with items but for some reason it does not populate anything in to the user schema. Could someone please take a look. I have 1 user and 1 item belonging to that user within my database but nothing is populating and I keep seeing null.
User Schema
var mongoose = require('mongoose')
var userSchema = mongoose.Schema({
name: {
type: String,
required: true
},
discordID: {
type: String,
required: true
},
discordImage: {
type: String,
required: true
},
items: [{
type: mongoose.Schema.Types.ObjectId,
ref: 'Item'
}]
})
const User = module.exports = mongoose.model('User', userSchema)
Item Schema
var mongoose = require("mongoose")
var itemSchema = mongoose.Schema({
name: {
type: String,
required: true
},
purchasedPrice: {
type: Number,
required: true
},
purchasedDate: {
type: String,
required: true
},
author: {
type: mongoose.Schema.Types.ObjectId,
required: true,
ref: "User"
}
})
const Item = module.exports = mongoose.model("Item", itemSchema)
Populate Code
app.get("/inventory", async (req, res) => {
try {
await req.user.populate({
path: 'items'
}).execPopulate()
console.log(req.user)
} catch (error) {
console.log(error)
}
res.status(200).render("inventory.ejs", { currentUser: req.user })
})
Objects in the DB:
Item:
User:
Used virtual method on user schema to create association
userSchema.virtual("items", {
ref: "Item",
localField: "_id",
foreignField: "author"
})
Worked fine with original code
I keep seeing null.
and
no its just empty
hints there are no items added to your user. You need to have some ids you can populate.
All populate does is convert an ObjectID into a document. There is no magic that will sync itemSchema.author with userSchema.items.
Hence, it's not enough to add the author to the item. You also need to add the item to the author.
So for example, you could add an item like this:
const item = new Item({author: user._id});
await item.save();
req.user.items.push( item );
await req.user.save();
Now when you log req.user, are there any items there?
Once you see objectIds, then you can go back and add that .populate('items') into the mix and I promise you it'll work.

How to dynamically populate array of objects in mongoose?

I am trying to dynamically populate array of objects in mongoose. On my user model I want an array that contains all posts that user made. The problem is that I have multiple types of posts.
Different post models:
const ImagePost = mongoose.model('ImagePost', new Schema({ url: String }))
const TextPost = mongoose.model('TextPost', new Schema({ text: String }))
My user model looks like this:
const userSchema = new Schema({
userName: {
type: String,
required: true
},
posts: [{
postId: {
type: Schema.Types.ObjectId,
required: true,
refPath: "postModel"
},
postModel: {
type: String,
required: true,
enum: ['ImagePost', 'TextPost']
}
}]
})
const User = mongoose.model('User', userSchema)
How can I get the user from my database and automatically populate the posts the user made?
The whey I think it should work is this but for some reason it doesn't do anything:
User.findById('5d302c7caf1b8906ccb611b6').populate('posts.postId')
Changing your refPath from postModel to posts.postModel may solve your problem.

Mongoose populate ObjectID from multiple possible collections

I have a mongoose model that looks something like this
var LogSchema = new Schema({
item: {
type: ObjectId,
ref: 'article',
index:true,
},
});
But 'item' could be referenced from multiple collections. Is it possible to do something like this?
var LogSchema = new Schema({
item: {
type: ObjectId,
ref: ['article','image'],
index:true,
},
});
The idea being that 'item' could be a document from the 'article' collection OR the 'image' collection.
Is this possible or do i need to manually populate?
Question is old, but maybe someone else still looks for similar issues :)
I found in Mongoose Github issues this:
mongoose 4.x supports using refPath instead of ref:
var schema = new Schema({
name:String,
others: [{ value: {type:mongoose.Types.ObjectId, refPath: 'others.kind' } }, kind: String }]
})
In #CadeEmbery case it would be:
var logSchema = new Schema({
item: {type: mongoose.Types.ObjectId, refPath: 'kind' } },
kind: String
})
But I did't try it yet...
First of all some basics
The ref option says mongoose which collection to get data for when you use populate().
The ref option is not mandatory, when you do not set it up, populate() require you to give dynamically a ref to him using the model option.
#example
populate({ path: 'conversation', model: Conversation }).
Here you say to mongoose that the collection behind the ObjectId is Conversation.
It is not possible to gives populate or Schema an array of refs.
Some others Stackoverflow people asked about it.
Soluce 1: Populate both (Manual)
Try to populate one, if you have no data, populate the second.
Soluce 2: Change your schema
Create two link, and set one of them.
var LogSchema = new Schema({
itemLink1: {
type: ObjectId,
ref: 'image',
index: true,
},
itemLink2: {
type: ObjectId,
ref: 'article',
index: true,
},
});
LogSchema.find({})
.populate('itemLink1')
.populate('itemLink2')
.exec()
Dynamic References via refPath
Mongoose can also populate from multiple collections based on the value of a property in the document. Let's say you're building a schema for storing comments. A user may comment on either a blog post or a product.
body: { type: String, required: true },
on: {
type: Schema.Types.ObjectId,
required: true,
// Instead of a hardcoded model name in `ref`, `refPath` means Mongoose
// will look at the `onModel` property to find the right model.
refPath: 'onModel'
},
onModel: {
type: String,
required: true,
enum: ['BlogPost', 'Product']
}
});
const Product = mongoose.model('Product', new Schema({ name: String }));
const BlogPost = mongoose.model('BlogPost', new Schema({ title: String }));
const Comment = mongoose.model('Comment', commentSchema);

Search by ObjectId in mongoose schema nodejs

I am using mongoose with nodejs . I have item schema and user schema given below.
var userSchema = new Schema({
email: { type: String, required: true, unique: true },
password: { type: String, required: true },
items : [{ type: Schema.Types.ObjectId, ref: 'Item' }]
});
var itemSchema = new Schema({
name: { type: String, required: true },
_owner : { type: Schema.Types.ObjectId, ref: 'User' }
});
I am trying to find item by user id(_owner in item schema).
I have tried to find directly
var uid = req.decoded._id;
var item = Item.findOne({"_owner": uid});
console.log(item.name); // undefined
By searching similar I found that id needs to be in ObjectId object so I tried
var uid = new mongoose.Types.ObjectId(req.decoded._id);
var item = Item.findOne({"_owner": uid});
console.log(item.name); // undefined
In both the cases item.name is undefined .Note that I have rechecked the value of req.decoded._id(by printing) with the db so it not undefined and present in db.
Is there anything I am doing wrong?
Model.findOne is an async call. It doesn't return the doc, it passes it to a callback function that you need to provide as a second parameter.
var uid = req.decoded._id;
var item = Item.findOne({"_owner": uid}, function(err, item) {
console.log(item.name);
});

MongoDb.js / Mongoose find() translate a String in ObjectId

I have two schemas utilizing Mongoose
Schema 1
var schema = mongoose.Schema({
name: {
type: String,
required: true
}
});
return mongoose.model('User', schema);
Schema 2
var schema = mongoose.Schema({
name: {
type: String,
required: true
},
user: {
type: mongoose.Schema.ObjectId,
ref: 'User',
required: true
}
});
return mongoose.model('Page', schema);
My Data in Page Collection
_id
551b0cdf63dc96e9c39de0f8
551b0d1563dc96e9c39de0f9
551b0d2d63dc96e9c39de0fa
551b0d4363dc96e9c39de0fb
551daae0f4cb312c62dcbc1e
name
Cazaquistao
Russia
China
Australia
El Salvador
user
5515c7aaaf6d59fea26d7185
5515c7aaaf6d59fea26d7185
5515c7aaaf6d59fea26d7185
5515c7aaaf6d59fea26d7185
5515c7c9af6d59fea26d7186
when i search in schema 2 by user, I dont find any results.
The mongoose translate the mongoose converts the query erroneously.
Query
var test = Page.find().select("_id").where({"user" : "5515c7aaaf6d59fea26d7185"}).exec()
.then(function (t) {
console.log("hi", t);
});
Query Translated erroneously
db.pages.find({ user: ObjectId("5515c7aaaf6d59fea26d7185") })
no results
Query as it should be
db.pages.find({ user: "5515c7aaaf6d59fea26d7185" })=
4 results
Any suggestions of what to do to work around this?
If user is a string in your page docs, then your schema needs to reflect that or Mongoose will try and cast it to the type in your schema (ObjectId in this case).
So make user a string in the schema:
var schema = mongoose.Schema({
name: {
type: String,
required: true
},
user: {
type: String,
ref: 'User',
required: true
}
});
I solved passing a objectId rather than a String

Resources