mongoose 系列之五 populate

做用

Mongoose 的 populate() 能夠連表查詢,即在另外的集合中引用其文檔。html

Populate() 能夠自動替換 document 中的指定字段,替換內容從其餘 collection 中獲取。數據庫

refs

建立 Model 的時候,可給該 Model 中關聯存儲其它集合 _id 的字段設置 ref 選項。ref 選項告訴 Mongoose 在使用 populate() 填充的時候使用哪一個 Modelapi

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數組

ObjectIdNumberString 以及 Buffer 均可以做爲 refs 使用。 可是最好仍是使用 ObjectIdmongoose

在建立文檔時,保存 refs 字段與保存普通屬性同樣,把 _id 的值賦給它就行了。ui

let author = new Author({
  name: 'dora',
  age: 18,
  story: story._id  // 直接賦值 story 的 _id
});

await author.save();

populate(path, select)

填充 document

let author = await Author.findOne({ name: 'dora' }).populate('story');

author.story   // {...} 從 Story 表中查到的文檔

被填充的 story 字段已經不是原來的 _id,而是被指定的 document 代替。這個 document 由另外一條 query 從數據庫返回。prototype

refs 數組返回存儲對應 _iddocument 數組。code

沒有關聯的 documenthtm

若是沒有關聯的文檔,則返回值爲 null,即 author.storynull;若是字段是數組,則返回 [] 空數組即 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

populate 多個字段

let author = await Author.findOne({ name: 'dora' }).populate('story').populate('friends');

若是對同一字段 populate() 兩次,只有最後一次生效。

populate({ objParam })

objParam:

  • path:須要 populate 的字段。
  • populate:多級填充。
  • select:從 populate 的文檔中選擇返回的字段。
  • model:用於 populate 的關聯 model。若是沒有指定,populate 將根據 schema 中定義的 ref 字段中的名稱查找 model。可指定跨數據庫的 model
  • matchpopulate 連表查詢的條件,符合條件的會用文檔替換 _id,不符合條件的會用 null 替換 _id
  • optionspopulate 查詢的選項。

    • 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 
})

經過 refPath 動態引用填充的 Model

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 中也能夠定義單獨的 blogPostproduct 字段,分別存儲 _id 和對應的 ref 選項。 可是,這樣是不利於業務擴展的,好比在後續的業務中增長了歌曲或電影的用戶評論,則須要在 schema 中添加更多相關字段。並且每一個字段都須要一個 populate() 查詢。而使用 refPath 意味着,不管 commentSchema 能夠指向多少個 Model,聯合查詢的時候只須要一個 populate() 便可。

相關文章
相關標籤/搜索