I have this "User" model where I store friends, this friends have some option
// Prepare schema
var schema = new db.Schema({
friends: [{
active : Boolean,
user : { type: db.Schema.ObjectId, ref: 'user' },
options : [{ type: db.Schema.ObjectId, ref: 'friend_option' }],
}],
image: { type: db.Schema.ObjectId, ref: 'file' },
password: {
type: String,
select: false
},
email: String,
name: String,
udid: [{
device: String,
udid: String
}],
created_on: Date
});
here I get one user with his friends
var friendQuery = User.find( {_id:req.params.id});
friendQuery.select('friends');
friendQuery.populate({ path: 'friends._id', model: 'user', select:'_id name' });
friendQuery.exec(
function(err, user) {
if (err)
res.send(err);
res.send(user);
});
my question is, why the result have a different name from my model? why friends->_id->_id instead go friends->user->_id?
friends": [
{
"_id": {
"_id": "58025664c154929d3207b232",
"name": "User 1"
},
"options": [
],
"active": false
},
{
"_id": {
"_id": "580257e1e3cd4fc7326fa97b",
"name": "User 2"
},
"options": [
],
"active": false
}
]
another option, this solution is good for an hypothetical big app?
The way You are using this in incorrect just make a simple change in your query
var friendQuery = User.find( {_id:req.params.id});
friendQuery.select('friends');
friendQuery.populate({ path: 'friends.user', model: 'user', select:'_id name' });
friendQuery.exec(
function(err, user) {
if (err)
res.send(err);
res.send(user);
});
as you are taking reference of your user in friends array simply replace _id from query to user that will definitely work for you
Related
I am getting some data in an array of object like this :
{
"success": true,
"result": {
"docs": [
{
"_id": "60a602901a74f62935a4898f",
"user": "607030ba3c82e235443db610",
"weekNum": 19,
"__v": 0,
"createdAt": "2021-05-20T06:32:48.742Z",
"data": [
{
"activity": "6063f898232d3f2acca5d2ae",
"_id": "60a6063668f27715b0f08753",
"project": "60702d1f3c82e235443db5ff",
"task": "60702d3d3c82e235443db601",
"workingDate": "2021-05-10T18:30:00.000Z",
"dayVal": 1,
"description": ""
}
],
"managersComment": "leleleleelelel",
"status": "Submitted",
"updatedAt": "2021-05-20T06:48:22.163Z"
}
],
"paginator": {
"itemCount": 1,
"offset": 0,
"perPage": 10000,
"pageCount": 1,
"currentPage": 1,
"slNo": 1,
"hasPrevPage": false,
"hasNextPage": false,
"prev": null,
"next": null
}
}
}
my schema for this collection in like this:
const timesheetSchema = new Schema({
managersComment: {
type: String
},
weekNum: {
type: Number
},
data:[{
project: {
type: Schema.ObjectId,
ref: projectModel
},
task: {
type: Schema.ObjectId,
ref: taskModel
},
activity: {
type: Schema.ObjectId,
default: null,
ref: activityModel
},
workingDate: {
type: Date
},
dayVal: {
type: Number
},
description: {
type: String
},
}],
user: { type: ObjectId, ref: userModel },
status: {
type: String,
enum: ['Saved', 'Submitted', 'Approved', 'Rejected', 'Reset']
},
}, { timestamps: true });
timesheetSchema.plugin(mongoosePaginate);
const timesheetModel = mongoose.model('timesheet', timesheetSchema);
my code for getting data is something like this:
try {
console.log('populateRequired --------------------------------------------------')
const populateArray = [
{ path: "task", select: "taskName" },
{ path: "project", select: "projectName" },
{ path: "activity", select: "title" },
];
const query = {
user: req.params.userId,
status: req.query.status,
};
const paginationParams = {
populate: populateArray,
customLabels: customLabels,
limit: req.query.limit,
};
console.log("USER QUERY ", query);
const userTimesheet = await getTimesheetDataByUserId(
query,
paginationParams
);
console.log(userTimesheet);
res.send({ success: true, result: userTimesheet });
} catch (err) {
console.log(err);
next(err);
}
But as shown in return data above i am not getting populate applied in data array. Please help not sure what to do.
According to the data you posted, I think that the issue is that you're not creating virtual fields to populate with your references. Your fields project, task and activity in each array element, or user, are meant to be ids referring to the corresponding models. But those ids alone will not implement the population, they are only the pointers that the population will need in order to be executed. To make that a little bit more clear, I would change those names to userId: { type: ObjectId, ref: userModel }.
After that, you will need to create the virtual fields:
timesheetSchema.virtual("user", {
ref: "userModel",
localField: "userId",
foreignField: "_id",
justOne: true,
});
Finally, if you want to have the virtual field timesheet.user populated each time you query your collection, you will have to add some middleware to your schema. For me, the most reasonable way to make this work is:
timesheetSchema.pre("find", function (next) {
this.populate("user");
next();
});
Just to have a complete solution: I think this will solve your problem for the timesheet.user field. But I don't think it will work in your data array. In fact, I'm not 100% sure the way you're defining it is really going to work: creating a timesheet with an array of imputations doesn't make too much sense to me. A more coherent approach would be creating a collection of all the imputations that looked like this:
const dataSchema = new Schema({
projectId: {
type: Schema.ObjectId,
ref: projectModel
},
taskId: {
type: Schema.ObjectId,
ref: taskModel
},
activityId: {
type: Schema.ObjectId,
default: null,
ref: activityModel
},
userId: {
type: ObjectId,
ref: userModel
},
workingDate: {
type: Date
},
dayVal: {
type: Number
},
description: {
type: String
},
});
With virtual fields like:
dataSchema.virtual("project", {
ref: projectModel,
localField: "projectId",
foreignField: "_id",
justOne: true
});
And so on. I would populate each field just like I showed you with the user example. Then, for the timesheet schema I would only reference userId, and populate data like this:
const timesheetSchema = new Schema({
managersComment: {
type: String
},
weekNum: {
type: Number
},
userId: {
type: ObjectId,
ref: dataModel
},
status: {
type: String,
enum: ['Saved', 'Submitted', 'Approved', 'Rejected', 'Reset']
},
}, { timestamps: true });
timesheetSchema.virtual("data", {
ref: dataModel,
localField: "userId",
foreignField: "userId"
});
timesheetSchema.virtual("user", {
ref: userModel,
localField: "userId",
foreignField: "_id",
justOne: true
});
This way you would have a collection with all the imputations for all the users, and you would be able to query and filter that collection for each userId, projectId or anything you would need. Having an array inside your timesheet collection would make this quite more difficult.
One simple solution I found on another SO post is like this:
const result = await timesheetModel.findOne(query).populate({
path: 'data.project data.activity data.task'
});
I want different values from the nested schema. How can I populate them so that every field is showing me its nested data and not the object ID?
I'm using MongoDB and node/express.
This is my postDB where the post schema is defined:
const mongoose = require('mongoose');
var postSchema = new mongoose.Schema({
title: {
type:String,
required:true
},
body: {
type:String,
required:true
},
comments:[{
type: mongoose.Schema.Types.ObjectId,
ref: "comment"
}],
category:{
type:String,
required:true
},
creator: {
type: mongoose.Schema.Types.ObjectId,
ref: "user"
}
},{timestamps : true}
)
module.exports = mongoose.model('postData', postSchema);
This is my commentDB which is referenced from the postDB:
const mongoose = require('mongoose');
// Using the Schema constructor, create a new CommentSchema object
// This is similar to a Sequelize model
var CommentSchema = new mongoose.Schema({
// `body` is of type String
creator: {
type: mongoose.Schema.Types.ObjectId,
ref: "user"
},
body: String
},{timestamps : true});
var Comment = mongoose.model("comment", CommentSchema);
module.exports = Comment;
This is how I'm trying to populate:
router.get('/READ', (req,res)=>{
posts.find({}, function (err, post) {
if (err) {
console.log(err);
}else{
res.json({post})
}
}
)
.populate([{path:'creator'}, {path:'comments'}])
})
However the results i get from this does not populate every object ID.
For example:
{
"comments": [
{
"_id": "5f8d91d8f8550044f0f755c8",
"creator": "5f84e5b1d893ac42dcc9cb78",
"body": "This looks cool",
"createdAt": "2020-10-19T13:17:12.323Z",
"updatedAt": "2020-10-19T13:17:12.323Z",
"__v": 0
},
{
"_id": "5f8d92e82ecfbe34b8f6375b",
"creater": "5f84e5b1d893ac42dcc9cb78",
"body": "hello",
"createdAt": "2020-10-19T13:21:44.463Z",
"updatedAt": "2020-10-19T13:21:44.463Z",
"__v": 0
},
],
"_id": "5f887cef6fd7d34548a592ea",
"title": "A DESCRIPTIVE PARAGRAPH EXAMPLE",
"body": "\"The room in which I found myself was very large and lofty. The windows were ",
"category": "Finance",
"creator": {
"joined": "2020-10-15T12:14:23.888Z",
"posts": [
"5f887cef6fd7d34548a592ea",
"5f887e0d6fd7d34548a592ec",
"5f887e266fd7d34548a592ed",
"5f887e586fd7d34548a592ee",
"5f89bfccc2bebd40b07b044a",
"5f89c36e906bbb27b84af897",
"5f89c7614199d52b141ff249",
"5f89c7ea4199d52b141ff24a",
"5f8c5ab175ef762ed89eddba",
"5f8c5be2d7fac046f0021d9f"
],
"_id": "5f88481d00ed460960da90f8",
"username": "kenwaysharma",
"email": "kenwaysharma#gmail.com",
"password": "$2b$10$p3qjmdSKWIF9qAagZoqbjuG34cjOgXTe5XYER0aowwviIS65COVlu",
"__v": 0
},
"__v": 0,
"updatedAt": "2020-10-20T05:42:56.320Z"
}
Here is the userDB:
username: {
type: String,
required: [true, "Username is required!"],
unique: true,
lowercase: true,
},
email:{
type: String,
required: [true, "Email is required!"],
unique: true,
lowercase: true,
validate: [isEmail, "Please enter a valid email!"]
},
password:{
type: String,
required: [true, "Password is required!"],
minlength: [6, "Enter atleast 6 characters!"],
},
comments:[{
type: mongoose.Schema.Types.ObjectId,
ref: "comment"
}],
posts:[{
type: mongoose.Schema.Types.ObjectId,
ref: "postData"
}],
},{timestamps : true});
GET users:
router.get('/USERS', (req,res)=>{
User.find({}, function (err, user) {
if (err) {
console.log(err);
}else{
res.send(user)
}
}
).populate('comments') .populate('posts')
})
How do I get the creator data inside of comments instead of just its object ID?
Update:
I also tried selecting the creator inside comments like
.populate('comments', 'creator')
but it still gives me the creator object ID in a string.
Update 2:
I have added the code for userDB to which the commentDB and postDB references.
Also added the GET users just to see how it works in postman.
Try chaining multiple populate methods and using the exec method to pass your callback.
posts.find({})
.populate({
path: 'comments',
populate: {
path: 'creator',
model: 'user'
}
})
.populate('creator')
.exec(function (err, post) {
if (err) {
console.log(err);
}else{
res.json({post})
}
});
i'm trying to save a group with references to users and ranks. users and ranks is arrays of id's. However when i make a post request it seem to say that Group validation failed. What am i doing wrong with my references?
router.post('/:users/:game/:ranks/:age/:quantity/:usercount', function(req, res, next) {
var params = req.params;
var ranks = params.ranks.split(',');
var users = params.users.split(',');
var group = new Group({
users: users,
game: params.game,
ranks: ranks,
quantity: params.quantity,
age_above: params.age,
userCount: params.usercount
});
group.save(function(err, object) {
if (err) {
res.send(err);
} else {
User.findByIdAndUpdate(params.id, {$set:{group:object.id}}, {new: true}, function(err, doc){
if(err){
res.send(err);
} else {
res.send(doc);
}
}).populate('group');
}
});
});
User schema
var userSchema = new Schema({
_id: {
type: SchemaTypes.Long,
required: true,
unique: true,
index: {unique: true}
},
name: String,
birthday: Date,
country: String,
image: String,
group: { type: Schema.Types.ObjectId, ref: 'Group'},
games: [{
game: { type: Schema.Types.ObjectId, ref: 'Game'},
rank: { type: Schema.Types.ObjectId, ref: 'Ladder'},
userName: String
}],
friends: [{ type: Schema.Types.ObjectId, ref: 'User' }],
created_at: Date,
updated_at: Date
});
Error message
{
"message": "Group validation failed",
"name": "ValidationError",
"errors": {
"users": {
"message": "Cast to Array failed for value \"[ '10210697939629739' ]\" at path \"users\"",
"name": "CastError",
"kind": "Array",
"value": [
"10210697939629739"
],
"path": "users",
"reason": {
"message": "Cast to ObjectId failed for value \"10210697939629739\" at path \"users\"",
"name": "CastError",
"kind": "ObjectId",
"value": "10210697939629739",
"path": "users"
}
}
}
}
I have a conversation schema which will contain two users. I want to flag messages to be deleted from one user so the other recipient still able to read the message.
Schema
// Messages Schema
var messagesSchema = new Schema({
from: {
type: Schema.Types.ObjectId,
required: true,
ref: 'User',
},
content: {
type: String,
required: true,
trim: true
},
deleted_by: [{
type: Schema.Types.ObjectId,
ref: 'User'
}],
read_by: [{
type: Schema.Types.ObjectId,
ref: 'User'
}],
}, {
timestamps: true
});
// Conversation Schema
var conversationsSchema = new Schema({
recipients: [{
type: Schema.Types.ObjectId,
required: true,
ref: 'User',
index: true
}],
messages: [messagesSchema],
}, {
timestamps: true
});
Now when I create a conversation between two users it will look like the following
[
{
"_id": "57bb6fed3d001f054e809175",
"updatedAt": "2016-08-22T21:37:30.631Z",
"createdAt": "2016-08-22T21:34:37.381Z",
"__v": 2,
"messages": [
{
"updatedAt": "2016-08-22T21:34:37.380Z",
"createdAt": "2016-08-22T21:34:37.380Z",
"from": "57b7448668d04d3035774b9a",
"content": "Hello are you there?",
"_id": "57bb6fed3d001f054e809176",
"read_by": [],
"deleted_by": []
},
{
"updatedAt": "2016-08-22T21:34:58.060Z",
"createdAt": "2016-08-22T21:34:58.060Z",
"from": "57b7448668d04d3035774b9a",
"content": "I miss you",
"_id": "57bb70023d001f054e809177",
"read_by": [],
"deleted_by": []
},
{
"updatedAt": "2016-08-22T21:37:30.631Z",
"createdAt": "2016-08-22T21:37:30.631Z",
"from": "57b7816b68d04d3035774b9b",
"content": "Hey... Me too",
"_id": "57bb709a3d001f054e809178",
"read_by": [],
"deleted_by": []
}
],
"recipients": [
"57b7448668d04d3035774b9a",
"57b7816b68d04d3035774b9b"
]
}
]
Now when one of the users want to delete the conversation from his side I want to add the user id to the deleted_by array inside each message.
I am trying to do something like this
Conversation.findOneAndUpdate({
_id: conversation_id
}, {
$push: {
'messages.deleted_by': req.loggedInUser._id
}
}, function(err, data) {
if(err) return next(err);
res.json(data);
})
Error Returned
TypeError: Cannot read property '$isMongooseDocumentArray' of undefined
I tried to add the $ sign and still getting the same error.
Try this:
Conversation.findOne({
_id: conversation_id
}, function(err, docs) {
if(err) return next(err);
if(docs)
{
docs.messages.forEach(function(msg,index,array)
{
msg.deleted_by.push(req.loggedInUser._id);
});
docs.save();
}
});
Read this for better understanding of forEach function.
I hope this helps.
You can push multiple items just by adding the parameters in the $push field
Example: $push:{<field1>:<value1>,<field2>:<value2>}
Conversation.findOneAndUpdate({
_id: conversation_id},
{$push: {messages.deleted_by: req.loggedInUser._id,read_by: //your_fields }
}, function(err, data) {
if(err) return next(err);
res.json(data);
})
I have these Mongoose schemes:
// User schema
exports.User = new Schema({
name: {
type: String,
required: true
},
home: [{
type: mongoose.Schema.Types.ObjectId,
ref: 'Post'
}]
});
// Post schema
exports.Post = new Schema({
likes: [{
type: Schema.Types.ObjectId,
ref: 'User'
}],
author: {
id: {
type: Schema.Types.ObjectId,
ref: 'User',
required: true
},
name: {
type: String,
required: true
},
shortId: String, // this is User id
},
created: {
type: Date,
default: Date.now,
required: true
}
});
// THE DATA IN THE DATABASE
// User
{"name" : "Mio Nome",
"home" : [
ObjectId("533af14b994c3647c5c97338")
]}
// Post
{ "author" : {
"id" : ObjectId("533af14b994c3647c5c97338"),
"name" : "AutoreDelPost"
},
"likes" : [
ObjectId("533af14b994c3647c5c97337"),
ObjectId("533af14b994c3647c5c97339"),
ObjectId("533af14b994c3647c5c97340")
]
}
And i want to get from users the posts in home field and count how many likehave one user
With this code i can show all posts in home whit populate, but i can't count likes.
req.db.User.find({
_id: req.user._id //req.user is my test user
}, {
home: 1
})
.limit(200)
.populate('home')
.exec(function (err) {
if (!err) {
return res.json(200)
}
return res.json(500, err)
});
// output
[
{
"_id": "533af0ae994c3647c5c97337",
"name" : "Mio Nome"
"home": [
{
"_id": "533b004e6bcb9105d535597e",
"author": {
"id": "533af14b994c3647c5c97338",
"name": "AutoreDelPost"
},
"likes": [] // i can't see like and i can't count they
}
]
I tryed to use aggregate, to count etc but i can't see the posts getting populated but their _id
req.db.User.aggregate({
$match: {
_id: req.user._id
}
}, {
$project: {
home: 1
}
}, {
$unwind: "$home"
}).exec(function (err, home) {
if (!err) {
return res.json(200, home)
}
return res.json(500, err)
});
// output
[
{
"_id": "533af0ae994c3647c5c97337",
"home": "533b004e6bcb9105d535597e"
},
{
"_id": "533af0ae994c3647c5c97337",
"home": "533b004e6bcb9105d5355980"
},
{
"_id": "533af0ae994c3647c5c97337",
"home": "533b004f6bcb9105d5355982"
},
{
"_id": "533af0ae994c3647c5c97337",
"home": "533b004f6bcb9105d5355984"
},
{
"_id": "533af0ae994c3647c5c97337",
"home": "533b00506bcb9105d5355986"
}
]
QUESTION: I want to get from users the posts in home field and count how many like a user has
Perhaps you can store your data more denormalized and add a counter field which is incremented on each new "like". See http://cookbook.mongodb.org/patterns/votes/. Something like:
update = {'$push': {'voters': user_id}, '$inc': {vote_count: 1}}