Mongoose 的 populate()
能夠連表查詢,即在另外的集合中引用其文檔。html
Populate()
能夠自動替換 document
中的指定字段,替換內容從其餘 collection
中獲取。數據庫
建立 Model
的時候,可給該 Model
中關聯存儲其它集合 _id
的字段設置 ref
選項。ref
選項告訴 Mongoose 在使用 populate()
填充的時候使用哪一個 Model
。api
const authorSchema = new Schema({ name: String, age: Number, story: { type: Schema.Types.ObjectId, ref: 'Story' } friends: [{ type: Schema.Types.ObjectId, ref: 'User' }] }); let Author = mongoose.model('Author', authorSchema);
上例中 Author
model 的 friends
字段設爲 ObjectId
數組。 ref
選項告訴 Mongoose 在填充的時候使用 User
model。全部儲存在 friends
中的 _id
都必須是 User
model 中 document
的 _id
。數組
ObjectId
、Number
、String
以及 Buffer
均可以做爲 refs
使用。 可是最好仍是使用 ObjectId
。mongoose
在建立文檔時,保存 refs
字段與保存普通屬性同樣,把 _id
的值賦給它就行了。ui
let author = new Author({ name: 'dora', age: 18, story: story._id // 直接賦值 story 的 _id }); await author.save();
let author = await Author.findOne({ name: 'dora' }).populate('story'); author.story // {...} 從 Story 表中查到的文檔
被填充的 story
字段已經不是原來的 _id
,而是被指定的 document
代替。這個 document
由另外一條 query 從數據庫返回。prototype
refs
數組返回存儲對應 _id
的 document
數組。code
沒有關聯的 documenthtm
若是沒有關聯的文檔,則返回值爲 null
,即 author.story
爲 null
;若是字段是數組,則返回 []
空數組即 author.friends
爲 []
。blog
let author = await Author.findOne({ name: 'dora' }).populate('friends'); author.friends // []
若是隻須要填充 document
中一部分字段,可給 populate()
傳入第二個參數,參數形式即 返回字段字符串,同 Query.prototype.select()
。
let author = await Author.findOne({ name: 'dora' }).populate('story', 'title -_id'); author.story // {title: ...} 只返回 title 字段 author.story.content // null 其他字段爲 null
let author = await Author.findOne({ name: 'dora' }).populate('story').populate('friends');
若是對同一字段 populate()
兩次,只有最後一次生效。
objParam
:
path
:須要 populate
的字段。populate
:多級填充。select
:從 populate
的文檔中選擇返回的字段。model
:用於 populate
的關聯 model
。若是沒有指定,populate
將根據 schema
中定義的 ref
字段中的名稱查找 model
。可指定跨數據庫的 model
。match
:populate
連表查詢的條件,符合條件的會用文檔替換 _id
,不符合條件的會用 null
替換 _id
。options
:populate
查詢的選項。
sort
:排序。limit
:限制數量。// 查詢 friends 的 friends Author.findOne({ name: 'dora' }). populate({ path: 'friends', populate: { path: 'friends' } });
跨數據庫不能直接經過 schema
中的 ref
選項填充,可是能夠經過 objParam
中的 model
選項顯式指定一個跨數據庫的 model
。
let eventSchema = new Schema({ name: String, conversation: ObjectId // 注意,這裏沒有指定 ref! }); let conversationSchema = new Schema({ numMessages: Number }); let db1 = mongoose.createConnection('localhost:27000/db1'); let db2 = mongoose.createConnection('localhost:27001/db2'); // 不一樣數據庫的 Model let Event = db1.model('Event', eventSchema); let Conversation = db2.model('Conversation', conversationSchema); // 顯示指定 model let doc = await Event.find().populate({ path: 'conversation', model: Conversation })
Mongoose 還能夠針對同一個存儲 _id
的字段從多個不一樣的集合中查詢填充。
//用於存儲評論的 schema。用戶能夠評論博客文章或做品。 const commentSchema = new Schema({ body: { type: String, required: true }, on: { type: Schema.Types.ObjectId, required: true, refPath: 'onModel' }, onModel: { type: String, required: true, enum: ['BlogPost', 'Product'] } }); const Product = mongoose.model('Product', new Schema({ name: String })); const BlogPost = mongoose.model('BlogPost', new Schema({ title: String })); const Comment = mongoose.model('Comment', commentSchema);
refPath
選項比 ref
更復雜。 若是 ref
只是一個字符串,Mongoose 將查詢相同的 model
以查找填充的子文檔。 而使用 refPath
,能夠配置用於每一個不一樣文檔的 model
。
const book = await Product.create({ name: '笑場' }); const blog = await BlogPost.create({ title: '笑場中的經典語錄,句句犀利,直戳人心' }); // 分別指定了不一樣評論來源的兩個評論數據 const commentOnBook = await Comment.create({ body: 'Bravo', on: book._id, onModel: 'Product' }); const commentOnBlog = await Comment.create({ body: '不曾開言我先笑場。笑場完了聽我訴一訴衷腸。', on: blog._id, onModel: 'BlogPost' });
const comments = await Comment.find().populate('on'); comments[0].on.name; // "笑場" comments[1].on.title; // "笑場中的經典語錄..."
固然在 commentSchema
中也能夠定義單獨的 blogPost
和 product
字段,分別存儲 _id
和對應的 ref
選項。 可是,這樣是不利於業務擴展的,好比在後續的業務中增長了歌曲或電影的用戶評論,則須要在 schema
中添加更多相關字段。並且每一個字段都須要一個 populate()
查詢。而使用 refPath
意味着,不管 commentSchema
能夠指向多少個 Model
,聯合查詢的時候只須要一個 populate()
便可。