I want to populate an object into a virtual field with mongoose as a JSON object, but it always returns an array with a single item.
Here is my scheme code (part with virtual field):
Order.virtual('client', {
type: 'ObjectId',
ref: 'User',
localField: 'clientId',
foreignField: '_id'
});
Here is how I do population:
Order.findOneAndUpdate({ internalStatus: 2}, { internalStatus: 3 })
.lean()
.populate({ path: 'client', select: 'email' })
.exec(function (err, order) {
//...
});
Here is what I receive in returned JSON:
{ _id: 5ad903d90443fe13b8c9061a,
client: [ { _id: 5b3755fe69635d1e942d00a8, email: 'user#user.com' } ] }
This is what I want to achieve:
{ _id: 5ad903d90443fe13b8c9061a,
client: { _id: 5b3755fe69635d1e942d00a8, email: 'user#user.com' } }
Thank you for any help or suggestions!
You have to add "justOne : true" to your virtual field config :
Order.virtual('client', {
type: 'ObjectId',
ref: 'User',
localField: 'clientId',
foreignField: '_id',
justOne : true
});
In mongoose mongoose#5.0.17 I am seeing they return as JSON_OBJECT but when I upgraded to mongoose#5.3.0 , it started retuning as JSON_ARRAY
Related
I have collections user, news, and user-news."user-news" populated to "user" and "news".
Can I sort and search document of "user-news" by "user.name" or "news.title" ?
const usersSchema = new Schema({ name: String })
const newsSchema = new Schema({ title: String }),
UsersNewsModel.find(queryObj)
.populate('user', 'name')
.populate('news', 'title')
.sort({ 'user.name': -1 })
.exec((findError, records) => {...}
Mongoose populate doesn't support sorting from populated field. But you can do something like this with aggregate.
UsersNewsModel.aggregate([
{
$lookup: {
from : 'user',
localField : 'user',
foreignField: '_id',
as : 'user'
}
},
{
$unwind: {
path: $user
}
},
{
$sort: {
'user.name': -1
}
}
])
I try to play with populate but without success ...
It's possible to do this?
I have 2 shema :
- User
import mongoose, { Schema } from 'mongoose'
const userSchema = new Schema({
email: { type: String, unique: true },
password: String,
passwordResetToken: String,
passwordResetExpires: Date,
products: [{
productId: { type: Schema.Types.ObjectId, ref: 'Product' },
dateAdd: { type: Date, default: Date.now }
}]
}, { timestamps: true })
const User = mongoose.model('User', userSchema)
export default User
And Product :
import mongoose, { Schema } from 'mongoose'
const productSchema = new Schema({
domain: String,
originUrl: { type: String },
price: Number,
img: String,
userFollow: [{ type: Schema.Types.ObjectId, ref: 'User' }]
})
const Product = mongoose.model('Product', productSchema)
export default Product
So I want to retrieve all the info for each of my prodcutId
I try this way (and many others without success):
User.findOne({ _id: userId }).populate({
path: 'products.productId',
populate: { path: 'products.productId', model: 'Products' }
}).exec(function (err, products) {
if (err) {
console.log('errors :' + err)
}
console.log('Product => ' + util.inspect(products))
})
Populate has no effect, same result with just the findOne()
I think User.findOne({ _id: userId }).populate('products.productId') should work.
Try using aggregate function of MongoDB and $lookup.
Users.aggregate([
{
"$match":
{
_id: user.id
}
},
{
"$lookup":
{
from: "Product",
localField: "products",
foreignField: "_id",
as: "products"
}
}
])
.exec()
.then((result) => {
//your result
})
.catch((err) => {
// if any error
});
I am trying to group a mongoose query and populate a few sub-documents in mongoose using $lookup but my result document arrays are returning empty.
Can anyone tell me why?
var mongoose = require('mongoose'),
Schema = mongoose.Schema;
var answerSchema = new Schema({
_user : { type: Schema.Types.ObjectId, ref: 'User', required: true },
_poll : { type: Schema.Types.ObjectId, ref: 'Poll', required: true },
_question : [{ type: Schema.Types.ObjectId, ref: 'Question' }],
answer : { type : String },
},
{ timestamps: { createdAt: 'created_at', updatedAt: 'updated_at' }
});
mongoose.model('Answer', answerSchema);
Here's my code:
module.exports = {
index: function(req, res, next){
Poll.findOne({_id: req.params.poll_id}, function(err, poll){
if(err) return next(err);
console.log(poll);
Answer.aggregate([
{$unwind: "$_question"},
{$match: {_poll: poll._id}},
{$group: {
_id: '$_question',
}},
{
$lookup : {
from : 'polls',
localField : '_poll',
foreignField: '_id',
as: 'poll'
},
},
{
$lookup : {
from : 'questions',
localField : '_question',
foreignField: '_id',
as: 'questions'
},
},
{
$lookup : {
from : 'users',
localField : '_user',
foreignField: '_id',
as: 'user'
},
},
], function (err, result) {
res.status(200).json(result);
});
});
},
The new subdocuments are returned empty for some reason.
Please note that each answer contains the reference to ONE poll, ONE user and MANY questions.
[
{
_id: "57ce8927ea5a4f090774215d",
poll: [ ],
questions: [ ],
user: [ ]
}
]
Can anyone spot my mistake?
Should I be using $project instead? I heard $lookup is the new and better way. I am using mongo 3.2.0 and mongoose 4.5.8.
Mongdb aggregate query is a pipeline operation. So the result of subsequent queries is passed on to the next query. For more info on Mongodb aggregate refer this. The mistake you have done is that when you used the $group query only _id is being passed on to the next $lookup query. You can fix this by using the following query.
Answer.aggregate([
{$unwind: "$_question"},
{$match: {_poll: poll._id}},
{$group: {
_id: '$_question',
_poll: { $first: '$_poll'},
_user: { $first: '$_user'},
_question : { $first: "$_question "}
}},
{
$lookup : {
from : 'polls',
localField : '_poll',
foreignField: '_id',
as: 'poll'
},
},
{
$lookup : {
from : 'questions',
localField : '_question',
foreignField: '_id',
as: 'questions'
},
},
{
$lookup : {
from : 'users',
localField : '_user',
foreignField: '_id',
as: 'user'
},
},
], function (err, result) {
res.status(200).json(result);
});
I have a query like this:
galleryModel.find({_id: galleryId})
.populate({
model: 'User',
path: 'objectId',
select: 'firstName lastName'
})
End response for objectId will be like this:
objectId: {
...
}
How can I change it to user in response without changing real path?
You can do this by virtual populate, introduced in mongoose version 4.5 . For that you need to define a virtual field in mongoose schema.
var GallerySchema = new mongoose.Schema({
name: String,
objectId: {
type: mongoose.Schema.Types.ObjectId
},
});
GallerySchema.virtual('user', {
ref: 'User',
localField: 'objectId',
foreignField: '_id'
});
Ans when you run find query, just populate it with user.
Gallry.find({_id: galleryId}).populate('user','firstName lastName').exec(function(error, gallery) {
console.log(error);
console.log(gallery);;
});
Above code is not tested in program, there may be typos, You can get more details about mongoose virtual populate on below link
http://mongoosejs.com/docs/populate.html
I have a Mongoose model which have paths which need to be populated :
var adsSchema = new Schema({
price: { type: Number, min: 0, required: true },
author: { type: ObjectId, ref: 'Users', required: true },
title: { type: String, required: true },
date: { type: Date, default: Date.now },
offers: [{
price: { type: Number, required: true },
date: { type: Date, default: Date.now },
offerBy: { type: ObjectId, ref: 'Users', required: true }
}],
category: { type: ObjectId },
images: [{
type: ObjectId,
ref: 'Images'
}],
videos: [String]
});
In some GET request, I need to populated numerous fields as I was saying, in particular offers with a sorting by 'price' by ascending.
As the documentation of Mongoose is showing there (http://mongoosejs.com/docs/api.html#query_Query-populate), you can sort by a subpath.
My problem is that it isn't the case.
There is my code which is doing it :
Ads.findOne({ _id: adId })
.populate({ path: 'author', select: 'firstName lastName' })
.populate('images')
.populate({ path: 'offers', options: { sort: { 'price': -1 } })
.populate({ path: 'category', select: 'name' })
.exec(function (err, ad) {
if (err)
deferred.reject({ status: 500, message: 'Database error.' });
else if (!ad)
deferred.reject({ status: 500, message: 'The ad doesn\'t exist!.' });
else {
deferred.resolve(ad.toJSON());
}
})
I have read as much as possible questions/answers giving here or in the Mongoose mailing-list :
https://stackoverflow.com/a/19450541/6247732
I know it's not possible to sort documents results from subdocument result, OK. But I just want to sort that subdocument only, and only this one. Some responses here seems to say it's possible :
https://stackoverflow.com/a/31757600/6247732
https://stackoverflow.com/a/16353424/6247732
I have 2 questions around it :
Is it possible (as the Mongoose documentation is written) to sort a subdocument during the population ?
Is it linked to the fact I didn't just put a ObjectId linked to an other Schema ?
Thanks for your answer ;)
Personally, I do not use 'populate' as possible as I can. I had experienced many trouble in the past. So, I do not know how to sort while populating.
Instead of using populate, you can use $lookup method on aggregation and you can easily sort any field. Its almost same way with 'population'.
Ads.aggregate([
{
$lookup: {
from: 'Users',
localField: 'author',
foreignField: '_id',
as: 'authorName'
}
},
{
$lookup: {
from: 'Images',
localField: 'images',
foreignField: '_id',
as: 'image'
}
},
{
$lookup: {
from: 'categories',
localField: 'category',
foreignField: '_id',
as: 'category'
}
},
{
$project: {
'authorName.firstName': 1,
'image.imagePath': 1,
'category.categoryName': 1,
'_id': 0
}
},
{
$sort : { 'authorName.firstName' : -1}
}
]).exec(function(err, adss) {
});
I did not check all fields properly. Please implement this way to your model and hope this can give you some idea. Good luck
I found the solution, and it was the response of my second question.
When you are writing a subdocument in your schema like in my question, you are not creating a "relation" between one Model and an other one. Which means that the populate method doesn't work at all.
You have to make a reference to an ObjectId and the model associated to be able to use populate, and so the options which offers you Mongoose. This is an example :
var adsSchema = new Schema({
price: { type: Number, min: 0, required: true },
author: { type: ObjectId, ref: 'Users', required: true },
offers: [{
type: ObjectId,
ref: 'Offers'
}],
videos: [String]
});
Now, the .populate method is working, because Mongoose (and so MongoDB) can perform the subquery on an other Collection and sort it. It wasn't the case before because it was in the same Model, so Mongoose doesn't perform a subquery on the same Collection (as I seems to understand it).