I have two collections user and comments.In user there is fields :
const user = mongoose.Schema({
_id :("In the form of ObjectID"),
//ObjectId("5a19086e0664e832842f2c24")
user_name :{type: String},
password : {type: String}
});
and comments collection:
const comments = mongoose.Schema({
user_id :{type: String},
//"5a19086e0664e832842f2c24" this is user's _id
comment : {type: String}
});
Now I want to know that how to populate this 2 collection with the user's _id which is in string type in comment collection.
Thankyou in advance
I think you already can use String to populate an ObjectId like this :
const user = mongoose.Schema({
user_name : {type: String},
password : {type: String}
}); // users._id will natively be an ObjectId
const comments = mongoose.Schema({
user_id : {type: String, ref:'users'}, // user_id has to be referenced with your "users" model
comment : {type: String}
});
Comment.find({}).populate('user_id').exec();
Hope it helps.
At first you need to update your schema as it requires the reference of the collection you want to populate:
const user = mongoose.Schema({
user_name: { type: String },
password: { type: String }
});
const comments = mongoose.Schema({
user_id: { type: Schema.Types.ObjectId, ref: 'User' },
comment: { type: String }
});
Note: I removed the _id field as it will be added automatically. Also take note that _id is an ObjectId not just a string (even though you can consider it as a string).
Then you can use the populate() method:
Comments.find({}).populate('user_id').exec()
Related
I have this Schema
var users = new Schema({
name: {type: String, required: true},
email: {type: String, required: true},
password: {type: String, required: true},
following: [{type: Schema.ObjectId, ref: "users"}]
});
users.index({name: 'text'});
I want to use Mongoose to find users who they have "john" in their name and they are exists in following array of user of _id = x
in other way if it was SQL the query would be (it just example to illustrate the relations)
SELECT * FROM users where _id = x AND users.following.name LIKE '%john%'
I think that if following array was embedded in the user collections it would be easy to make.
How can i handle that in mongoose ?
I found the answer here http://mongoosejs.com/docs/populate.html
I used populate with match
.findById("x", {following: 1}).populate({ path: 'following',match: {$text: {$search: "john"}}})
In my application I store comments. Previously my model for that looked like this:
var CommentsSchema = new Schema({
username: {type: String},
display_name: {type: String},
facebook_username: {type: String},
text_content: {type: String},
photo_content_url: {type: String},
hashtags: {type: [String]},
device_id: {type: String},
comment_date: {type: Date, default: Date.now},
friends_only: {type: Boolean, default: false}
}
Each comment - besides storing its details - had also details about the author, e.g. username, facebook_username, device_id from which the comment was added and display_name. There was also a bool flag friends_only based on which I was deciding whether that comment should be visible only to user's facebook friends or to everyone.
Construction of the node.js/mongoose query for fetching all comments looked like this:
commentsRoutes.post('/friends', function (req, res) {
var friends = req.body.friends;
var publicComments = req.body.publicComments;
var hashtagsInput = req.body.hashtags;
var startDate = req.body.startDate;
var endDate = req.body.endDate;
var query= {};
query.$and = [];
// and condition on start date
if(startDate != undefined) {
var startDate = new Date(req.param('startDate'));
var endDate = new Date(req.param('endDate'));
query.$and.push({"comment_date":{$gte: startDate}});
query.$and.push({"comment_date":{$lte: endDate}});
}
// and condition on hastags
if (hashtagsInput != undefined) {
var hashtags = hashtagsInput.split(",");
query.$and.push({"hashtags":{$in: hashtags}});
}
// creating a OR condition for facebook friends and public flag
var friend_query = {};
friend_query.$or = [];
if (friends != undefined) {
var friendsSplit = friends.split(",");
friend_query.$or.push({"facebook_username":{$in: friendsSplit}});
}
if (publicComments != undefined && publicComments === "true") {
friend_query.$or.push({friends_only: false});
}
//Merging facebook friend condition with other condition with AND operator.
query.$and.push(friend_query);
var finalQuery = Comment.find(query)
With the code above user could fetch content posted by his friends (that was set either to public or private) and all other public content (from everyone else).
I've decided to change all of that and split the data into two models. After changing it I have:
var CommentsSchema = new Schema({
user_id: {type: String, required: true, ref: 'users' },
text_content: {type: String},
photo_content_url: {type: String},
hashtags: {type: [String]},
comment_date: {type: Date, default: Date.now},
friends_only: {type: Boolean, default: false},
device_id: {type: String}
}
and
var UsersSchema = new Schema({
username: {type: String},
facebook_username: {type: String},
display_name: {type: String}
}
Now, when I want to keep the old functionality, I need to modify the code responsible for creating the query.
I could merge two queries with async, or the other way is to use mongoose .populate option. I decided to go with the second choice, so now I need to move the code responsible for creating or query to the match part of populate function:
...
var finalQuery = Comment.find(query)
finalQuery.populate({path: 'user_id',
select: 'facebook_username display_name username',
match: {
}});
I don't know how to do it. Can you help me with that?
First, i suggest you that go with a populate query, if you feel that populate won't give you a data that you need that you can run two queries and merge those results.
for populate, i found the solution from the official doc of mongoose. you can do like this.
var mongoose = require('mongoose')
, Schema = mongoose.Schema
var personSchema = Schema({
_id : Number,
name : String,
age : Number,
stories : [{ type: Schema.Types.ObjectId, ref: 'Story' }]
});
var storySchema = Schema({
_creator : { type: Number, ref: 'Person' },
title : String,
fans : [{ type: Number, ref: 'Person' }]
});
var Story = mongoose.model('Story', storySchema);
var Person = mongoose.model('Person', personSchema);
Story
.findOne({ title: 'Once upon a timex.' })
.populate('_creator')
.exec(function (err, story) {
if (err) return handleError(err);
console.log('The creator is %s', story._creator.name);
// prints "The creator is Aaron"
});
here is doc link: http://mongoosejs.com/docs/populate.html
So that if the original fields are modified, the copied field changes too.
Pseudo-code example :
userSchema = {
firstName: {type: String},
lastName: {type: String},
displayName: firstName + ' ' + lastName
}
Is something like this possible ?
EDIT: I need to make request based on that field, so I can't just concat the fields when i retrieve them.
You can use the hooks http://mongoosejs.com/docs/middleware.html
userSchema = {
firstName: {type: String},
lastName: {type: String},
displayName: {type: String}
}
userSchema.pre('save', function(next) {
this.displayName = this.username+' '+this.lastName;
next();
});
Hello I have this problem when checking if a subdocument exist before pushing a new subdocument.
var UserSchema = new Schema({
name : String,
app_key : String,
app_secret : String,
tasks : [{type: Schema.ObjectId, ref: 'Task'}] // assuming you name your model Task
});
var TaskSchema = new Schema({
name : String,
lastPerformed : Date,
folder : String,
user : {type: Schema.ObjectId, ref: 'User'} // assuming you name your model User
});
With this, your query for all users, including arrays of their tasks might be:
User.findOne({...}).populate('tasks').run(function(err, user) {
var subdoc = user.tasks.id(mytask.id);
if(subdoc){
//not exist
//push
}
});
This is the error:
TypeError: Object has no method 'id'
You are getting that error because there is no 'id' field defined for the 'tasks' subdocument. You might have meant 'user.tasks._id', which will return the ObjectId that MongoDB adds to its documents by default.
Maybe a second set of eyes can see what is wrong with my schema
var UserSchema = new Schema({
name:
{
first : {type: String}
, last : {type : String}
}
, password: {type: String}
, username: {type: String}
, role: RoleSchema
, created_at : {type : Date, default : Date.now}
, modified_at : {type : Date, default : Date.now}
})
var RoleSchema = {
type: [String]
, study_type: [String]
}
mongoose.model('User', UserSchema)
The Error:
TypeError: Invalid value for schema path `role`
The embedded Schema (Roles) needs to be above the UserSchema
In addition to the Roles schema having to be imported before the UserSchema.
In the newer versions of mongoose the following sort of syntax was also needed for to get beyond the 'TypeError: Invalid value for schema Array path:
var SomeSchema = new mongoose.Schema();
SomeSchema.add({
key1: {
type: String,
required: true
},
key2: {
type: String,
required: true
},
key3: {
type: String,
required: true
}
});
SomeSchema.get(function(val){
// Remove the _id from the Violations
delete val._id;
return val;
});
And the parent:
var ParentSchema = new mongoose.Schema({
parentKey: String,
someArray: [SomeSchema]
})
module.exports = mongoose.model('Parent', ParentSchema)
This happened when switching from mongoose 3.x to 4.x