Mongoose Relationship Populate Doesn't Return results - node.js

var SecuritySchema = new Mongoose.Schema({
_bids: [{
type: Mongoose.Schema.Types.ObjectId,
ref: 'BuyOrder'
}],
_asks: [{
type: Mongoose.Schema.Types.ObjectId,
ref: 'SellOrder'
}]
});
var OrdersSchema = new Mongoose.Schema({
_security: {
type: Mongoose.Schema.Types.ObjectId,
ref: 'Security'
},
price: {
type: Number,
required: true
},
quantity: {
type: Number,
required: true
}
});
// declare seat covers here too
var models = {
Security: Mongoose.model('Security', SecuritySchema),
BuyOrder: Mongoose.model('BuyOrder', OrdersSchema),
SellOrder: Mongoose.model('SellOrder', OrdersSchema)
};
return models;
And than when I save a new BuyOrder for example:
// I put the 'id' of the security: order.__security = security._id on the client-side
var order = new models.BuyOrder(req.body.order);
order.save(function(err) {
if (err) return console.log(err);
});
And attempt to re-retrieve the associated security:
models.Security.findById(req.params.id).populate({
path: '_bids'
}).exec(function(err, security) {
// the '_bids' array is empty.
});
I think this is some sort of naming issue, but I'm not sure, I've seen examples here and on the moongoose website that use Number as the Id type: http://mongoosejs.com/docs/populate.html

The ref field should use the singular model name
Also, just do:
models.Security.findById(req.params.id).populate('_bids').exec(...
My main suspicion given your snippet at the moment is your req.body.order has _security as a string instead of an array containing a string.
Also, you don't need an id property. Mongodb itself will automatically do the _id as a real BSON ObjectId, and mongoose will add id as a string representation of the same value, so don't worry about that.

While I don't understand your schema (and the circular nature of it?), this code works:
var order = new models.BuyOrder({ price: 100, quantity: 5});
order.save(function(err, orderDoc) {
var security = new models.Security();
security._bids.push(orderDoc);
security.save(function(err, doc) {
models.Security.findById({ _id: doc._id })
.populate("_bids").exec(function(err, security) {
console.log(security);
});
});
});
It:
creates a BuyOrder
saves it
creates a Security
adds to the array of _bids the new orderDoc's _id
saves it
searches for the match and populates
Note that there's not an automatic method for adding the document to the array of _bids, so I've done that manually.
Results:
{ _id: 5224e73af7c90a2017000002,
__v: 0,
_asks: [],
_bids: [ { price: 100,
quantity: 5,
_id: 5224e72ef7c90a2017000001, __v: 0 } ] }

Related

How to populate sub document of another model in mongoose?

I have two mongodb model as following.
const CompanySchema = new Schema(
{
sections: [{
name: { type: String },
budgets: [{ // indicates from CalcSchema
index: { type: Number },
title: { type: String },
values: [Number],
sum: { type: Number, default: 0 },
}],
}]
},
{ timestamps: true }
);
const CalcSchema = new Schema({
budget: {
type: Schema.Types.ObjectId, // I want to populate this field. this indicates budget document in Company model
ref: "Company.sections.budgets" //it's possible in mongoose?
},
expense: {
type: Number,
default: 0
}
});
budget field indicate one of budgets field in CompanySchema.
So I want to populate when get Calc data.
But I don't how to populate embedded document.
I tried set ref value to ref: "Company.sections.budgets". but it's not working.
Please anyone help.
Finally, I found answer myself.
There is useful plugin for it.
https://github.com/QuantumGlitch/mongoose-sub-references-populate#readme
And I learned that my schema structure was wrong. It's anti-pattern in mongodb.

Populating array of collection which contains references to another collections returns empty array

I have two models Vote and Link,I am trying to populate the votes array in link model,The votes array contains id's that references to the collection Vote,which only contains two fields link and User which also refs to same link model mentioned below and a user model respectively
link Schema:-
const linkSchema = new mongoose.Schema(
{
description: {
type: String,
trim: true,
},
url: {
type: String,
trim: true,
},
postedBy: {
type: mongoose.Types.ObjectId,
ref: "User",
},
votes: [{ type: mongoose.Types.ObjectId, ref: "Vote" }],
},
{
timestamps: true,
}
);
linkSchema.index({ description: "text" });
linkSchema.index({ createdAt: -1 });
module.exports = mongoose.model("Link", linkSchema);
Vote schema:-
const mongoose = require("mongoose");
const voteSchema = new mongoose.Schema({
link: { type: mongoose.Types.ObjectId, ref: "Link" },
user: { type: mongoose.Types.ObjectId, ref: "User" },
});
module.exports = mongoose.model("Vote", voteSchema);
but when i try to get the votes of a link,it always return an empty array ,My function:-
const votes = async ({ id }) => {
const linkData = await Link.findById(id).populate("votes").exec();
console.log(linkData);
};
Output Data:-
{
votes: [], //empty always
_id: 5ecb21059a157117c03d4fac,
url: 'https://www.apollographql.com/docs/react/',
description: 'The best GraphQL client for React',
postedBy: 5ec92a58bf38c32b38400705,
createdAt: 2020-05-25T01:36:05.892Z,
updatedAt: 2020-05-25T01:37:52.266Z,
__v: 0
}
Instead of populate(), you can use aggregate() to get your desired output. This should probably work in your case:
Link.aggregate([
{
$match: {
_id: { $in: [mongoose.Types.ObjectId(id)] // as suggested by the questioner
}
},
{
$lookup: {
from: "vote", // collection to join
localField: "votes", // field from the input documents (filtered after _id is matched)
foreignField: "link", // field to compare with, from other collection
as: "linkData" // output array name
}
}
])
Let me know in the comments.

Mongoose populate a field without ObjectId?

I have a schema:
var Schema = mongoose.Schema;
var TicketSchema = new Schema({
externalId: String,
name: String,
items: [{
externalId: String,
price: Number,
quantity: {
type: Number,
default: 1
},
comment: {
type: String,
default: '',
trim: true
},
entity: {
type: String,
ref: 'Entity'
}
}],
tableId: String
});
mongoose.model('Ticket', TicketSchema);
And I want to populate entity field with an unique field other than ObjectId.
How can I achieve that?
Though late answer. Please check Populate Virtuals for Mongoose 4.5.0
Click the link below
http://mongoosejs.com/docs/populate.html
And scroll down or search for Populate Virtuals you will see it does exactly what you want.
I found Views as one useful approach, though not sure it is the most efficient! For example, in movielens database, I wanted to refer 'movieId' in ratings collection to 'movieId' in the movies collection using 'movieId' as foreign key.
db.createView('rating-movie-view','ratings',[{$lookup:{from:"movies",localField:"movieId",foreignField:"movieId",as:"ratings_movie"}},{ $project:{'userId':1,'movieId':1,'rating':1,'timestamp':1,'ratings_movie.title':1,'ratings_movie.genres':1 } }])
New view "rating-movie-view" thus created has the required fields 'title and 'genres'.
db["rating-movie-view"].findOne()
{
"_id" : ObjectId("598747c28198f78eef1de7a3"),
"userId" : 1,
"movieId" : 1129,
"rating" : 2,
"timestamp" : 1260759185,
"ratings_movie" : [
{
"title" : "Escape from New York (1981)",
"genres" : "Action|Adventure|Sci-Fi|Thriller"
}
]
}
Hope this useful!
Those who are not familiar with movielens data here are the schema
var MovieSchema = new mongoose.Schema({
movieId: Number,
title: String,
genres: String,
});
var RatingSchema = new mongoose.Schema({
userid: Number,
movieId:Number,
rating: Number,
timestamp:Number,
});
//View schema
var RatingViewSchema = new mongoose.Schema({
userid: Number,
movieId:Number,
rating: Number,
timestamp:Number,
rating_movie:{title:String,genres:String}
});
const blogs = this.blogModel
.find(find)
.populate('blogCategory', 'name -_id');
Note -_id will exclude the object _id
I'm not sure if I understood your question correctly.
In Mongoose model, in case we do not specify a primary key, it automatically adds in an extra field called ObjectId and assigns a unique value for each object.
In case we need to specify our own key, we can do it by specifying the key property.
For example:
mongoose.model('Todo', {
todoID: {
type: String,
key: true
},
text: {
type: 'text'
},
done: {
type: Boolean,
default: false,
},
date: {
type: Date,
},
items: [{
entity: {
type: String,
ref: 'Entity'
}
}]
});
I hope, this is what you meant.
If you are asking about fetching objects based on Items -> entity's property,
Todo.find({'items.entity.type':required_type}, function(err, foundTodos){
// ---
});
Thanks,
Use crypto to hash something unique like the objectId , and then save it to your entities.
Var hash = crypto.createHmac('sha256', ticket.objectId).digest('hex');
Ticket.entities= hash;

Mongoose Populate not working

Hello i have this Schema(called schema.js):
var mongoose = require('mongoose'),
Schema = mongoose.Schema;
var RoomSchema = new Schema({
name: { type: String, required: true, index: { unique: true } },
people: { type: Number, required: true },
childrens: {type: Number, required: true},
total: {type: Number, required: true}
});
var Room = mongoose.model('Room', RoomSchema);
var AvSchema = new Schema({
roomId: {type: Schema.Types.ObjectId, ref: 'Room'},
people: { type: Number, required: true },
childrens: {type: Number, required: true},
total: {type: Number, required: true}
});
var Av = mongoose.model('Av', AvSchema);
module.exports = {
Room: Room,
Av: Av
};
in my Route file :
module.exports = function(app) {
var model = require('../models/Schema');
app.get('/api/rooms', function(req, res) {
model.Room.find(function(err, rooms) {
if (err)
res.send(err);
res.json(rooms);
});
});
app.get('/api/av', function(req, res) {
model.Av.find().populate('roomId').exec(function(err, av) {
if (err)
res.send(err);
res.json(av);
});
});
};
A pic of the db :
GET /api/rooms - response:
[{
"_id": "5444d0dd9a31437167eea816",
"name": "Single",
"people": 1,
"childrens": 1,
"total": 4
}, {
"_id": "5444d1009a31437167eea817",
"name": "Double",
"people": 2,
"childrens": 2,
"total": 10
}]
When i call api/rooms looks fine but when i call api/av i got an empty array [] .... Any idea what i do wrong? I should mention that i have inserted records in av collection for both roomsID
Thank you in advance.
By default, Mongoose pluralizes the model name to come up with the name of the collection, so Mongoose is looking in the avs collection instead of av.
You can explicitly set the collection name by passing that as the third parameter to model:
var Av = mongoose.model('Av', AvSchema, 'av');
I had the same issue but none of the answers worked for me.
I wanted to populate a document after it was queried.
This didn't work:
// IIFE for async/await
( async() => {
var user = await User.findOne( { _id } );
await user.populate( 'comments' ); // Doesn't work
} );
The Mongoose Documentation explains that when calling .populate() without a callback it won't be executed. Instead you need to use .populate().execPopulate():
// IIFE for async/await
( async() => {
var user = await User.findOne( { _id } );
await user.populate( 'comments' ).execPopulate(); // Works as expected
} );
Similar to CodyBugstein's answer, I'm posting why it wasn't working in my case, even though it's not the same case as OP's.
I was trying to populate the "pro" field of my schema in a .post('save') hook, as so:
mySchema.post('save', function(doc, next) {
console.log(doc.pro); // Expected to log ObjectID
doc.populate("pro"); // Populate field
console.log(doc.pro); // Expected to log actual pro document
}
However, the 2nd console.log was also logging the ObjectID instead of the doc.
After struggling with this for a solid hour and trying different approaches, I found out that all I had to do was use promises and call execPopulate() so that it returned a fully-fledged promise. I used async/await but you could use .then too:
mySchema.post('save', async function(doc, next) {
console.log(doc.pro); // Expected to log ObjectID
await doc.populate("pro").execPopulate(); // Populate field
console.log(doc.pro); // Expected to log actual pro document
}
This way, the 2nd console.log did log the entire pro doc as expected :)
Since this is the most popular result for the query
mongoose populate not working
I'll include the reason it wasn't working for me, even though it's not a direct answer to this already solved question, in the hopes it will help someone
The problem for me was that I had specified fields in select({..} but not the field I was trying to populate.
Don't forget to add the ref property to the schema for the property you are trying to populate. E.g.
// ...
const orderSchema = new mongoose.Schema({
userId: {
type: Types.ObjectId,
required: true
},
reservationId: {
type: Types.ObjectId,
required: true,
ref: 'Reservation' // <-- don't forget the ref
}
}, {
timestamps: true
})
// ...
See Mongoose Populate
Also check that your schema doesn't have depopulate in toJSON or toObject option set to true. (facepalm myself)
See all schema options
For me, it is was due to incorrect data. The Ids which I want to populate got deleted from the main table.
So when I do populate it didn't filled the populated data because the Ids were not in the table.
add this in your model
const productSchema = mongoose.Schema(
{
name: {
type: String,
required: [true, "product name must be provide"],
minlength: [3, "Name length minimum is 3"],
maxlength: [50, "Name length maximum is 50"],
trim: true,
},
price: {
type: Number,
required: [true, "price must be provide for product"],
default: 0,
},
description: {
type: String,
required: [true, "product description is required"],
trim: true,
},
{
timestamps: true,
toJSON: {
virtuals: true,
},
toObject: {
virtuals: true,
},
}
);
Example:
productSchema.virtual("reviews", {
ref: "Review",
localField: "_id",
foreignField: "product",
justOne: false,
});
How to use:
products = await productModel.find({}).populate("reviews");
For me, the issue was that I did not require the model to be populated at the beginning of the file.

Mongoose Populate - array

can someone please help me with population of this schema? I need to populate array of Staff by their userId.
var PlaceSchema = new Schema ({
name: { type: String, required: true, trim: true },
permalink: { type: String },
country: { type: String, required: true },
...long story :D...
staff: [staffSchema],
admins: [adminSchema],
masterPlace:{ type: Boolean },
images: []
});
var staffSchema = new Schema ({
userId: { type: Schema.Types.ObjectId, ref: 'Account' },
role: { type: Number }
});
var adminSchema = new Schema ({
userId: { type: Schema.Types.ObjectId, ref: 'Account'}
})
var Places = mongoose.model('Places', PlaceSchema);
I tried to use this query, but without success.
Places.findOne({'_id' : placeId}).populate('staff.userId').exec(function(err, doc){
console.log(doc);
});
Polpulation is intended as a method for "pulling in" information from the related models in the collection. So rather than specifying a related field "directly", instead reference the related fields so the document appears to have all of those sub-documents embedded in the response:
Places.findOne({'_id' : placeId}).populate('staff','_id')
.exec(function(err, doc){
console.log(doc);
});
The second argument just returns the field that you want. So it "filters" the response.
There is more information on populate in the documentation.

Resources