Is there a way to instruct model to populate ALWAYS a certain field?
Something like, to have "field" populated in any find query:
{field: Schema.ObjectId, ref: 'Ref', populate: true}
?
With Mongoose 4.0, you can use Query Hooks in order to autopopulate whatever you want.
Below example is from introduction document by Valeri Karpov.
Definition of Schemas:
var personSchema = new mongoose.Schema({
name: String
});
var bandSchema = new mongoose.Schema({
name: String,
lead: { type: mongoose.Schema.Types.ObjectId, ref: 'person' }
});
var Person = mongoose.model('person', personSchema, 'people');
var Band = mongoose.model('band', bandSchema, 'bands');
var axl = new Person({ name: 'Axl Rose' });
var gnr = new Band({ name: "Guns N' Roses", lead: axl._id });
Query Hook to autopopulate:
var autoPopulateLead = function(next) {
this.populate('lead');
next();
};
bandSchema.
pre('findOne', autoPopulateLead).
pre('find', autoPopulateLead);
var Band = mongoose.model('band', bandSchema, 'bands');
this plugin is a solution to your question:
https://www.npmjs.com/package/mongoose-autopopulate
I use query hook to autopopulate, but it not works with create() and save() for modified fields.
This is my code:
var autoPopulate = function(next) {
this.populate('updated_by','name').populate('created_by','name');
next();
};
ProjectSchema.pre('findOne', autoPopulate);
ProjectSchema.pre('find', autoPopulate);
If I update Project only created_by is populated
If I create new Project both created_by and updated_by are not populated.
find and findOne works with no problems.
What should I do to always populate both: created_by and updated_by?
Related
long time Java programmer here trying to figure out Node.js
I am trying to cascade a delete to child objects using the pre middleware, but no child delete is happening, while the parent deletes without a hitch. Leaving my database full of sad orphans. When I started logging to the console I see that the reference to 'this' is empty. For most cases this seems to be a problem of using ==> to create the function, but I am not doing so:
GameSession(parent):
var mongoose = require('mongoose');
var TimeLineEvent = require('../models/timelineevent');
//Define a schema
var Schema = mongoose.Schema;
var GameSessionSchema = new Schema({
name: {type: String, required: true},
gameMasterId : {type: Schema.Types.ObjectId, ref: 'GameMaster', required: true},
});
GameSessionSchema.pre('findOneAndDelete', function(next) {
console.log('GameSessionSchema.pre findOneAndDelete this ='+this);
console.log('GameSessionSchema.pre findOneAndDelete id ='+this._id);
TimeLineEvent.deleteMany({gameSessionId: this._id}).exec();
next();
});
//Export function to create "SomeModel" model class
module.exports = mongoose.model('GameSessionModel', GameSessionSchema );
TimeLineEvent(child):
//Require Mongoose
var mongoose = require('mongoose');
//Define a schema
var Schema = mongoose.Schema;
var TimeLineEventSchema = new Schema({
name: {type: String, required: true},
gameSessionId: {type: Schema.Types.ObjectId, ref: 'GameSession', required: true},
time: {type: Number, required: true},
nextAction: {type: Number}
});
module.exports = mongoose.model('TimeLineEventModel', TimeLineEventSchema );
This is what the console shows when the pre method is called:
GameSessionSchema.pre findOneAndDelete this =[object Object]
GameSessionSchema.pre findOneAndDelete id =undefined
Can you see what I am missing? Thanks!
From the documentation: "In query middleware functions, this refers to the query."
If you use console.log(this) you will be able to view the full Query object. You might find this.model references what you need.
I have the below code in my application
const citySchema = new Schema({
cityName: {
type: String,
required: true,
},
citizen:[{
type: Schema.Types.ObjectId,
ref: "Citizen",
}],
});
module.exports = mongoose.model("City", citySchema);
const citizenSchema = new Schema({
citizenName: {
type: String,
required: true,
},
city:{
type: Schema.Types.ObjectId,
ref: "City",
},
});
module.exports = mongoose.model("Citizen", citizenSchema);
router.post('/', (req, res) => {
// req.body.cityName
// req.body.citizenName
})
In my POST request , I receive both city name(new city) and citizen name(new citizen) that are not present . But I want both these schemas to be updated properly .
City should contain Citizen references
Citizen should contain City reference
How can I do that ? Kindly help
Rather than doing that, I think you'll be much better to apply referencing via pre-hook middleware in your data model.
The code should be like this:
const citySchema = new Schema({
cityName: {
type: String,
required: true,
},
citizen:[{
type: Schema.Types.ObjectId,
ref: "Citizen",
}],
});
// Query middleware to populate the 'citizen' attribute every time the 'find' function is called.
citySchema.pre(/^find/, function (next) {
this.populate('citizen');
next();
});
module.exports = mongoose.model("City", citySchema);
const citizenSchema = new Schema({
citizenName: {
type: String,
required: true,
},
city:{
type: Schema.Types.ObjectId,
ref: "City",
},
});
citizenSchema.pre(/^find/, function (next) {
this.populate('city');
next();
});
module.exports = mongoose.model("Citizen", citizenSchema);
If you want to select just the ID and not the 'full data', you could do it like this for example:
citizenSchema.pre(/^find/, function (next) {
this.populate({
path: 'city',
select: '_id',
});
next();
});
Explanation:
By doing this, every time you call Mongoose's functions like findByIdAndUpdate, find, findOne, the referenced data will appear in the city and citizen attribute. This is actually more efficient rather than updating each time there is a new data.
The populate method is used to populate the attributes with data from another data model.
The object that I inserted into the populate method is used to get the 'name' of the model (in the path), and to select what kind of data to take from the referenced model. In this case, I only want to take the _id attribute.
I have the two following models. In the user model I want to use an array of Requests and in Request Model I want to use User as an attribute(without the password). How can I do it?
var userSchema = new Schema({
cartaoCidadao: {
type: String,
required: true,
index: {
unique: true,
},
match: /[0-9]{8}/,
},
password: { type: String, required: true },
role: { type: String },
estado: { type: String, enum: ["Infetado", "Suspeito"] },
});
var requestSchema = new Schema({
encaminhado: { type: String },
pessoaRisco: { type: String },
trabalhoRisco: { type: String },
estadoPedido: { type: String },
resultado: { type: String },
});
You can use the schema you defined as a type itself:
var userSchema = new Schema({
// ...
requests: {
type: [requestSchema] // this property type is: array of requests
}
// ...
});
If both models are stored in database and you probably want to go for their association. You can reference one model from another. (see the answer of Muhammad Lahin)
Then you query your parent model and associate the children models with it (https://mongoosejs.com/docs/populate.html)
And here is an example of how you can exclude some fields during the population:
https://mongoosejs.com/docs/populate.html#query-conditions
It will be something like:
User.
find(/* some query */).
populate({
path: 'requests',
select: 'fieldToSelect1 fieldToSelect2' // You can control which fields to include
}).
exec();
you can do something like this
var userSchema = new Schema({
requests: [
{
type: Schema.Types.ObjectId,
ref: "Request",
}
]
});
var requestSchema = new Schema({
user: {
type: Schema.Types.ObjectId,
ref: "User"
}
})
you can set the type of the user in the request model like this
type: schema.Types.ObjectID,
ref: 'users'
which users id schema of users and when you want to send the whole user data in the response, use populate and in populate you can omit the password too
now if you see the mondodb database in the request database the whole user object is not saved and only the user_id is saved so you dont have access to the user. you have to send another request to get the user data from the gived user_id which is not good. the solution is you set the schema as I said and when you are sending a response which have the data of the request use populate to add the userdata to the response
res.json({request: requests.find(//options to find a specified request).populate('users','name')})
first argument is the model which is users and the second argument is the wanted field of the users model for example this will return the name of the user. you can add more arguments to add all of them except the password
const mongoose = require('mongoose')
const productSchema = new mongoose.Schema({
user: {
type: mongoose.Types.ObjectId,
ref: 'User'
}
});
I Set manually an uid to each item in my collections...I Want To Know It's Possible that I use uid for populate?
I Dont Want Use from '_id' because Have Many collections In My DB And should change many things...somthing like this :
var mongoose = require('mongoose')
, Schema = mongoose.Schema
var PersonSchema = new Schema({
name : String
, age : Number
, stories : [{ type: String, ref: 'Story' }]
});
var StorySchema = new Schema({
_creator : { type: String, ref: 'Person' }
, title : String
, fans : [{ type: String, ref: 'Person' }]
});
var Story = mongoose.model('Story', StorySchema);
var Person = mongoose.model('Person', PersonSchema);
No, it is not possible to have a mongodb document without '_id' , if you want to avoid duplication of ids, you can put uid in '_id' before saving the document.
If you keep uid seperately, '_id' will get autopopulated as ObjectId.
Here is a clear example of how to do it (https://mongoosejs.com/docs/populate.html):
Story.
find(...).
populate({
path: 'fans',
select: 'name -_id' // <---------------'-_id' exclude _id field.
}).
exec();
I have a normal MongoDB with 2 Collections:
A post with an Array of group pointer.
var Post = mongoose.model('Post', new mongoose.Schema({
groups: {type: [{type: mongoose.Schema.Types.ObjectId, ref: 'Group'}]},
}));
var Group = mongoose.model('Group', new mongoose.Schema({
}));
Now I want to query all post which have a group given by a id in there groups array?
I tried the following:
return Post.find({ groups: req.body.groupId} },function(err,post){
console.log(post);
res.send(post);
});
I also tried to query first the group object und passing this instead of the groupId? Does anybody have an idea?
I think you have error in your Post schema, it should look like this:
var Post = mongoose.model('Post', new mongoose.Schema({
groups: [{type: mongoose.Schema.Types.ObjectId, ref: 'Group'}],
}));
then your query should work correctly.
In addition to the correction by Yelizaveta, you left off mention of .populate(aaa) as in
return Post.find({ groups: req.body.groupId} },function(err,post){
console.log(post);
res.send(post);
}).populate('groups');
where you list the fields of type: Reference ... without this .populate() these fields will just list the ObjectId values, not the underlying document in the referenced other collection : group