mongoose的关联查询 :populate
mongoose关联查询从3.2版本开始支持
基本用法如下:
var studentSchema = new Schema({ name:String, age:String, school:{ type:Schema.Types.ObjectId, ref:'school' } }); var schoolSchema = new Schema({ name:String, students:[ { type:Schema.Types.ObjectId, ref:"student" } ] }) var Student = mongoose.model('student',studentSchema); var School = mongoose.model("school",schoolSchema); School.findOne({name:"xxx"}).populate("students","name age").exec(function(err,school){ console.log(school); console.log("============") }) //populatte中第二个参数,只返回关联表中的字段 Student.findOne({name:"xxx"}).populate("school","name").exec(function(err,student){ console.log(student); console.log("===============") })
总结点:
1、schame中的ref值需要对应mongoose.model中的第一个参数,即数据库中的集合名称,否者查询失败
2、populate(arg1,arg2)
第一个参数对应集合中的存续关联数据的属性,若对应错误,查询成功,但关联集合只有_id返回。
第二个参数用于过滤查询关联集合中的属性,多个属性用空格隔开,若缺失,返回关联集合的所有参数,可以传"-_id"去除返回值中的_id属性
注:官方说明:In Mongoose >= 4.0, you can manually populate a field as well.
//官方例子
Story. findOne({ title: /casino royale/i }). populate('author', 'name'). // only return the Persons name exec(function (err, story) { if (err) return handleError(err); console.log('The author is %s', story.author.name); // prints "The author is Ian Fleming" console.log('The authors age is %s', story.author.age); // prints "The authors age is null' });
3、多个关联集合
Story. find(...). populate('fans'). populate('author'). exec();
关于多个关联集合,若同时populate多个相同的集合,则只有最后一个产生作用
School.findOne({name:"xxxx"})
.populate("students","name")
.populate("students","age")
.exec(function(err,school){
console.log(school);
console.log("============")
})
//返回结果中只有age,没有name
//也可以写为:populate({ path: 'students', select: 'name age' })
4、关联条件查询
Story. find(...). populate({ path: 'fans',//关联的结合 match: { age: { $gte: 21 }},//条件 select: 'name -_id',//去掉_id属性,选择name options: { limit: 5 }//分页 }). exec();
5、多级查询
var userSchema = new Schema({ name: String, friends: [{ type: ObjectId, ref: 'User' }] }); User. findOne({ name: 'Val' }). populate({ path: 'friends',//查询我的朋友 // Get friends of friends - populate the 'friends' array for every friend populate: { path: 'friends' }//查询我朋友的朋友列表 });
6、跨数据库查询
var eventSchema = new Schema({ name: String, // The id of the corresponding conversation // Notice there's no ref here! conversation: ObjectId }); var conversationSchema = new Schema({ numMessages: Number }); var db1 = mongoose.createConnection('localhost:27000/db1'); var db2 = mongoose.createConnection('localhost:27001/db2'); var Event = db1.model('Event', eventSchema); var Conversation = db2.model('Conversation', conversationSchema); //给populate的conversation指定一个model,这样就能通过model跨数据库查询 Event. find(). populate({ path: 'conversation', model: Conversation }). exec(function(error, docs) { /* ... */ });
7、动态参考:假如有个用户的schema,有个关联的字段为group,group中能来自多个集合的参考,如:group可以是一个足球队、一个篮球队。。。
var userSchema = new Schema({ name: String, connections: [{ kind: String, item: { type: ObjectId, refPath: 'connections.kind' } //connections.kind就是表示此中的关联是kind字段对应的组织 }] }); var organizationSchema = new Schema({ name: String, kind: String }); var User = mongoose.model('User', userSchema); var Organization = mongoose.model('Organization', organizationSchema); // 有一个组织{ _id: '01'), name: "Beyond", kind: 'Band' } //有两个用户 // {_id: '02'),name: '黄家驹', // connections: [ // { kind: 'User', item: '03') }, // { kind: 'Organization', item:'01') } // ] // }, // { // _id: '03', // name: '叶世荣', // connections: [] // } User. findOne({ name: '黄家驹' }). populate('connections.item').//关联中的item exec(function(error, doc) { // doc.connections[0].item is a User doc // doc.connections[1].item is an Organization doc }); //总结: // refPath告诉mongoose,populate指向connetions.item,
// 而connetions.item又指向connetions.kind,kind最终存的是对应的model,
// kind的值可以不一样,则populate中的指向就会根据kind的值不同而改变,类似动态参数一样
8、虚拟填充:版本>4.5
不是根据_id的关联查询,如下面的例子是关联band名字的关联查询
var PersonSchema = new Schema({ name: String, band: String }); var BandSchema = new Schema({ name: String });
//给BandSchme设置一个虚拟关联字段:members; BandSchema.virtual('members', { ref: 'Person', // 虚拟字段的model为Person localField: 'name', // 查找到Person.band的值和Band.name的值相等的项 foreignField: 'band', // // justOne用于指定,返回的members是单个数据还是一个数组集合,justOne默认为false justOne: false }); var Person = mongoose.model('Person', PersonSchema); var Band = mongoose.model('Band', BandSchema); /** * 假如有两个band:"beyond", "唐朝乐队" * 有4个Person: 黄家驹(黄家驹.band = beyond) 黄家强(黄家强.band="beyond")
* 丁武(丁武.band = "唐朝乐队") 陈磊(陈磊.band="唐朝乐队") */ Band.find({}).populate('members').exec(function(error, bands) {
//这里返回的bands含有members字段,里面的值为Person的实例
});
以上基本上是官方文档的所有说明了,需要注意版本的不同。