記錄Mongodb的populate
Mongodb是文檔型數據庫,不存在像關系型數據庫一樣的外鍵設置,但通過Mongodb中的populate函數可以模擬關系型數據庫的外鍵連接后的查詢功能。
文檔型數據庫存儲自由,不像關系型數據庫在插入數據等時需要嚴格按照表的結構,而文檔型數據庫即可隨意的增添屬性列等。但文檔型數據庫在處理表間關聯時較麻煩,而population就是Mongoose封裝的用於在展示查詢結果的視圖時填充被關聯的表的相關字段(前提是該字段已設置了關聯其他表,且滿足類型等條件,下面會講)。
使用學生表,課程表,選課表記錄對Mongodb中populate函數的理解
//引入mongoose模塊,用於在nodejs端操作Mongodb數據庫
var mongoose = require('mongoose');
//設置連接,其中mongdb是使用Mongodb數據庫需要的協議,在IP:port/后填入的是數據庫名稱
mongoose.connect('mongodb://localhost:27017/test');
var db = mongoose.connection;
//監聽數據庫的連接狀態
db.on('open',function() {
console.log('數據庫連接成功');
});
db.on('error',function(){
console.log('數據庫連接失敗');
});
// 引入Schema來定義數據庫中表的結構
var Schema = mongoose.Schema;
// 設置學生表的表結構
var studentSchema = new Scheam({
sno:String,
sname:String
},{
// Mongodb中有自帶的_v和_id字段,設置versionKey為false將不增添_v字段
versionKey:false
});
// 使用mongoose.model實例出表數據的構造函數,后續的表的元祖將根據此構造函數實例對象
var Student = mongoose.model('Student',studentSchema);
// 定義課程表的結構
var courseSchema = new Scheam({
cno:String,
cname:String,
},{
versionKey:false
});
// 定義課程表的的構造函數
var Course = mongoose.model('Course', courseSchema);
// 定義選課表的表的數據結構
var scSchema = new Scheam({
sno:{type:Scheam.Types.ObjectId,ref:'Student'},
con:{type:Scheam.Types.ObjectId,ref:'Course'},
},{
versionKey:false
});
var SC = mongoose.model('SC', scSchema);
在這里需要注意的就是在設置表間關聯時,關聯字段的類型(type)和引用(ref)需要在定義表的結構(定義某張表的Schema時就聲明好)
在Mongodb中使用類似外鍵的功能需要將要關聯的表的類型設置為Scheam.Types.ObjectId類型。例如在SC表中的sno和cno屬性是使用Student表中的sno屬性列,Course表中的cno屬性列。想要做到表間關聯需要設置表字段的類型為S
chema.Types.ObjectId類型,這點與關系型數據庫相類似,關系型數據庫中表間關聯需要存放的別的表的主鍵字段,根據主鍵可以唯一的查詢出數據庫中的數據。而Mongodb中_id字段是數據庫自有的,且每個元祖的_id不同,相當於主鍵功能,可用於唯一的查詢數據,故表間關聯需要設置關聯字段的類型為type。
ref設置了關聯字段中引用的是哪張表,注意設置的是被引用表的mongoose.model中的第一個參數,設置了這個才能使用populate函數查詢出關聯的依賴項
在nodejs中操作Mongodb的增刪改查
簡單例子,文件目錄結構如下:

var express = require('express');
var app = express();
// 引用
var StudentTable = require('./database').StudentTable;
var CourseTable = require('./database').CourseTable;
var SCTable = require('./database').SCTable;
var student = new StudentTable({
sname:'黃貓',
sage:30,
sgender:1
});
student.save();
var course = new CourseTable({
cname:'大數據',
cteacher:'嘿嘿'
});
course.save();
StudentTable.find({sname:'白貓'},function (err, students) {
if (!err) {
CourseTable.find({cname:'Html5'},function (err,courses) {
var sc = new SCTable({
sno:students[0]._id,
cno:courses[0]._id
});
sc.save()
});
}
});
通過修改上述student,course內容存入多條數據,在可視化工具Robo 3T中可以查看到如下:
1.StudentTable中
2.CourseTable中

// 填充sc(選課表)設置第一條選課記錄為Student表的第一個學生選取了Course表的第一個課程,注意這里填充的都是id
StudentTable.find({sname:'白貓'},function (err, students) {
if (!err) {
CourseTable.find({cname:'Html5'},function (err,courses) {
var sc = new SCTable({
sno:students[0]._id,
cno:courses[0]._id
});
sc.save()
});
}
});
3.SCTable中

4.查詢出表間關聯字段
SCTable.find()
// 填充sc表的sno,cno字段,執行populate的前提是這兩個字段在設置表的結構(Schema)時已經設置好類型和引用的表名
.populate('sno cno','-_id')
.exec(function (err, data) {
if (!err) {
// 這里輸出全部的數據
console.log(data);
for (var i = 0; i < data.length; i++) {
console.log(data[i].sno.sname);
console.log(data[i].sno.sage);
console.log(data[i].cno.cname);
console.log(data[i].cno.cteacher);
}
}
});
終端輸出如下:

關於populate函數參數
Query.populate(path, [select], [model], [match], [options]) 語法
para1:(path)必填參數,指定要填充的表的字段,注意指定的是前面查詢的表的字段,多個字段間使用空格隔開
para2:(select)可選參數,不寫時默認填充的是被關聯表的所有字段,可填入String或Object類型
String類型時‘sname -_id’ ‘-’表示不填入該字段
Object類型時{sname:1, _id:0} 1表示填充,0表示不填充
后續幾個參數基本用不到
