初學(xué)Node.js
接觸到MongoDB
數(shù)據(jù)庫抱婉,閱讀資料中推薦的都是Mongoose
模塊矩桂,可以更加方便的對(duì)數(shù)據(jù)庫進(jìn)行操作,便開始接觸Mongoose
证九。在學(xué)習(xí)時(shí)碰到許多基礎(chǔ)問題彭谁,查閱了許多資料理來理解,此篇文章就是以自己的理解允扇,記錄下入門的基礎(chǔ)知識(shí)缠局,以及歸納下常用的函數(shù)以及格式,方便今后查閱考润。
初學(xué)筆記難免有許多不足與錯(cuò)誤歡迎指出狭园。
環(huán)境搭建
安裝MongoDB
如果需要在本地測試,需要先安裝MongoDB數(shù)據(jù)庫糊治。
在官網(wǎng)下載最新安裝包唱矛。安裝成功后,在命令行下進(jìn)入MongoDB\bin
目錄
mongod --dbpath D:\MongoDB\data
啟動(dòng)數(shù)據(jù)庫井辜。
http://localhost:27017/
在瀏覽器中訪問這地址绎谦,訪問正常則數(shù)據(jù)庫啟動(dòng)成功。
更詳細(xì)的說明可以查看《MongoDB的下載粥脚、安裝與部署》
Node.js中安裝模塊
npm install mongoose
在已經(jīng)安裝過Node.js的電腦上窃肠,進(jìn)入工作目錄輸入以上命令即可安裝Mongoose模塊。
可視化工具
Robomongo
是一款不錯(cuò)的可視化工具刷允,Windows冤留、MacOS、Linux平臺(tái)都有树灶,界面也簡潔纤怒。雖然有收費(fèi),普通操作免費(fèi)版足夠用天通。
官網(wǎng)下載
引入與連接
引入模塊
在需要使用的js文件中引入模塊泊窘。
var mongoose = require('mongoose');
連接數(shù)據(jù)庫
var db = mongoose.connect('mongodb://localhost/mongodb');
URL以mongodb://
+ [用戶名:密碼@]
+數(shù)據(jù)庫地址[:端口]
+ 數(shù)據(jù)庫名
。(默認(rèn)端口27017)
需要對(duì)連接狀況進(jìn)行判斷土砂,可以用以下代碼:
db.connection.on("error", function (error) {
console.log("數(shù)據(jù)庫連接失斨菁取:" + error);
});
db.connection.on("open", function () {
console.log("數(shù)據(jù)庫連接成功");
})
db.connection.on('disconnected', function () {
console.log('數(shù)據(jù)庫連接斷開');
})
基本概念
最常接觸到的有三個(gè)概念Schema
、Model
萝映、Entity
吴叶。按自己理解,
Schema
是定義數(shù)據(jù)庫的結(jié)構(gòu)序臂。類似創(chuàng)建表時(shí)的數(shù)據(jù)定義蚌卤,但比創(chuàng)建數(shù)據(jù)庫可以做更多的定義实束,只是沒辦法通過Schema對(duì)數(shù)據(jù)庫進(jìn)行更改。
Model
是將Schema定義的結(jié)構(gòu)賦予表名逊彭。但可用此名對(duì)數(shù)據(jù)庫進(jìn)行增刪查改咸灿。
Entity
是將Model與具體的數(shù)據(jù)綁定,可以對(duì)具體數(shù)據(jù)自身進(jìn)行操作侮叮,例如保存數(shù)據(jù)避矢。
Schema
Schema用來定義數(shù)據(jù)庫文檔結(jié)構(gòu),數(shù)據(jù)庫有什么字段囊榜、字段是什么類型审胸、默認(rèn)值、主鍵之類的信息卸勺。除了定義結(jié)構(gòu)外砂沛,還能定義文檔的實(shí)例方法,靜態(tài)模型方法曙求,復(fù)合索引碍庵,中間件等。詳情查看mongoose官方文檔悟狱。
在引入Mongoose模塊var mongoose = require('mongoose')
的js文件中進(jìn)行操作静浴。
var blogSchema = new mongoose.Schema({
title: String,
comments: [{ body: String, date: Date }],
date: { type: Date, default: Date.now },
hidden: Boolean,
meta: {
votes: Number,
favs: Number
}
})
這樣即定義了一個(gè)名為blogSchema
的Schema。
如需再添加數(shù)據(jù)芽淡,用add
方法马绝。
blogSchema.add( { author: String, body: String} );
資料中介紹,Shema
不僅定義了文檔的結(jié)構(gòu)和屬性挣菲,還可以定義文檔的插件富稻、實(shí)例方法、靜態(tài)方法白胀、復(fù)合索引文檔生命周期鉤子椭赋,具體還需查看官方文檔。
Schema.Type
Schema.Type
是Mongoose內(nèi)部定義的數(shù)據(jù)類型或杠∧恼基本類型有:String
、Number
向抢、Date
认境、Boolean
、Array
挟鸠、Buffer
叉信、Mixed
、ObjectId
艘希。
Mixed
混合數(shù)據(jù)類型硼身,可以直接定義{}
來使用硅急,以下兩種形式等價(jià)。
new Schema({mixed: {Schema.Types.Mixed} });
new Schema({mixed: {} });
ObjectId
儲(chǔ)存在數(shù)據(jù)庫中的每個(gè)數(shù)據(jù)都會(huì)有默認(rèn)的主鍵_id
佳遂,默認(rèn)存儲(chǔ)的是ObjectId
营袜。
ObjectId
是一個(gè)12字節(jié)的BSON
類型字符串。按照字節(jié)順序依次代表:
4字節(jié):UNIX時(shí)間戳
3字節(jié):表示運(yùn)行MongoDB的機(jī)器
2字節(jié):表示生成此_id的進(jìn)程
3字節(jié):由一個(gè)隨機(jī)數(shù)開始的計(jì)數(shù)器生成的值
Model
var blogModel = mongoose.model('Blog', blogSchema);
將名為blogSchema
的Schema與Blog
名字綁定丑罪,即是存入數(shù)據(jù)庫的名字荚板,但存入數(shù)據(jù)庫中的名字是Blogs
,會(huì)自動(dòng)添加一個(gè)s
吩屹。
這里將Model命名為blogModel
啸驯,需要對(duì)Blog
表操作的話,都需要使用變量名blogModel
祟峦。
Entity
可以綁定具體數(shù)據(jù)對(duì)Model實(shí)例化。
var blogEntity = new blogModel({
title: "Mongoose",
author: "L",
body: "Documents are instances of out model. Creating them and saving to the database is easy",
comments: [{ body: "It's very cool! Thanks a lot!", date: "2014.07.28" }],
hidden: false,
meta: {
votes: 100,
favs: 99
}
})
這里將名為blogModel
的Model實(shí)例化徙鱼。之后我們可以用blogEntity
名對(duì)數(shù)據(jù)進(jìn)行保存并執(zhí)行回調(diào)宅楞。
blogEntity.save(function(err, docs){
if(err) console.log(err);
console.log('保存成功:' + docs);
})
在平常使用SQL語句操作數(shù)據(jù)庫時(shí),取得數(shù)據(jù)后先組織成SQL語句袱吆,然后放入執(zhí)行語句中執(zhí)行厌衙。這里理解也是類似,取得數(shù)據(jù)先進(jìn)行實(shí)例化绞绒,這一步類似于組織成SQL語句婶希,然后再做具體操作例如上面的Save
操作。但由于Node.js是異步操作蓬衡,所以返回的數(shù)據(jù)利用回調(diào)函數(shù)來進(jìn)行操作喻杈。
知道了以上概念后就可以對(duì)數(shù)據(jù)進(jìn)行操作了,下面將列出一些常用的資料狰晚,并附上相應(yīng)的例子筒饰。
增查改刪(CRUD)
所有的參數(shù)都是以JSON對(duì)象
形式傳入。
增(C)
var doc = ({
title: "Mongoose",
author: "L",
body: "Documents are instances of out model. Creating them and saving to the database is easy",
comments: [{ body: "It's very cool! Thanks a lot!", date: "2014.07.28" }],
hidden: false,
meta: {
votes: 100,
favs: 99
}
};
blogModel.create(doc, function(err, docs){
if(err) console.log(err);
console.log('保存成功:' + docs);
});
Model#save([options], [options.safe], [options.validateBeforeSave], [fn])
var blogEntity = new blogModel({
title: "Mongoose",
author: "L",
body: "Documents are instances of out model. Creating them and saving to the database is easy",
comments: [{ body: "It's very cool! Thanks a lot!", date: "2014.07.28" }],
hidden: false,
meta: {
votes: 100,
favs: 99
}
});
blogEntity.save(function(err, docs){
if(err) console.log(err);
console.log('保存成功:' + docs);
});
多條數(shù)據(jù)插入壁晒,將多條數(shù)據(jù)一次性插入瓷们,相對(duì)于循環(huán)使用create
保存會(huì)更加快。
blogModel.insertMany([
{title: "mongoose1", author: "L"},
{title: "mongoose2", author: "L"}
], function(err, docs){
if(err) console.log(err);
console.log('保存成功:' + docs);
});
查(R)
conditions
:查詢條件秒咐;projection
:控制返回的字段谬晕;options
:控制選項(xiàng);callback
:回調(diào)函數(shù)携取。
blogModel.find({title: "Mongoose", meta.votes: 100}, {title: 1, author: 1, body: 1}, function(err, docs){
if(err) console.log(err);
console.log('查詢結(jié)果:' + docs);
})
查詢“title”標(biāo)題為“Mongoose”攒钳,并且“meta”中“votes”字段值為“100”的記錄,返回僅返回“title”歹茶、“author”夕玩、“body”三個(gè)字段的數(shù)據(jù)你弦。
Model.findOne([conditions], [projection], [options], [callback])
conditions
:查詢條件;projection
:控制返回的字段燎孟;options
:控制選項(xiàng)禽作;callback
:回調(diào)函數(shù)。
只返回第一個(gè)查詢記錄揩页。
id
:指定_id
的值旷偿;projection
:控制返回的字段;options
:控制選項(xiàng)爆侣;callback
:回調(diào)函數(shù)萍程。
改(U)
conditions
:查詢條件;doc
:需要修改的數(shù)據(jù)兔仰,不能修改主鍵(_id
)茫负;options
:控制選項(xiàng);callback
:回調(diào)函數(shù)乎赴,返回的是受影響的行數(shù)忍法。
options
有以下選項(xiàng):
safe (boolean): 默認(rèn)為true。安全模式榕吼。
upsert (boolean): 默認(rèn)為false饿序。如果不存在則創(chuàng)建新記錄。
multi (boolean): 默認(rèn)為false羹蚣。是否更新多個(gè)查詢記錄原探。
runValidators: 如果值為true,執(zhí)行Validation驗(yàn)證顽素。
setDefaultsOnInsert: 如果upsert選項(xiàng)為true咽弦,在新建時(shí)插入文檔定義的默認(rèn)值。
strict (boolean): 以strict模式進(jìn)行更新戈抄。
overwrite (boolean): 默認(rèn)為false离唬。禁用update-only模式,允許覆蓋記錄划鸽。
blogModel.update({title: "Mongoose"}, {author: "L"}, {multi: true}, function(err, docs){
if(err) console.log(err);
console.log('更改成功:' + docs);
})
以上代碼先查詢“title”為“Mongoose”的數(shù)據(jù)输莺,然后將它的“author”修改為“L”,“multi”為true允許更新多條查詢記錄裸诽。
一次更新多條
一次更新一條
Model.findByIdAndUpdate(id, [update], [options], [callback])
id
:指定_id
的值嫂用;update
:需要修改的數(shù)據(jù);options
控制選項(xiàng)丈冬;callback
回調(diào)函數(shù)嘱函。
options
有以下選項(xiàng):
new: bool - 默認(rèn)為false。返回修改后的數(shù)據(jù)埂蕊。
upsert: bool - 默認(rèn)為false往弓。如果不存在則創(chuàng)建記錄疏唾。
runValidators: 如果值為true,執(zhí)行Validation驗(yàn)證函似。
setDefaultsOnInsert: 如果upsert選項(xiàng)為true槐脏,在新建時(shí)插入文檔定義的默認(rèn)值。
sort: 如果有多個(gè)查詢條件撇寞,按順序進(jìn)行查詢更新顿天。
select: 設(shè)置數(shù)據(jù)的返回。
Model.findOneAndUpdate([conditions], [update], [options], [callback])
conditions
:查詢條件蔑担;update
:需要修改的數(shù)據(jù)牌废;options
控制選項(xiàng);callback
回調(diào)函數(shù)啤握。
options
有以下選項(xiàng):
new: bool - 默認(rèn)為false鸟缕。返回修改后的數(shù)據(jù)。
upsert: bool - 默認(rèn)為false排抬。如果不存在則創(chuàng)建記錄叁扫。
fields: {Object|String} - 選擇字段。類似.select(fields).findOneAndUpdate()畜埋。
maxTimeMS: 查詢用時(shí)上限。
sort: 如果有多個(gè)查詢條件畴蒲,按順序進(jìn)行查詢更新悠鞍。
runValidators: 如果值為true,執(zhí)行Validation驗(yàn)證模燥。
setDefaultsOnInsert: 如果upsert選項(xiàng)為true咖祭,在新建時(shí)插入文檔定義的默認(rèn)值。
passRawResult: 如果為真蔫骂,將原始結(jié)果作為回調(diào)函數(shù)第三個(gè)參數(shù)么翰。
刪(D)
blogModel.remove({author: "L"}, function(err, docs){
if(err) console.log(err);
console.log('刪除成功:' + docs);
})
刪除“author”值為“L”的記錄。
id
:指定_id
的值辽旋;update
:需要修改的數(shù)據(jù)浩嫌;options
控制選項(xiàng);callback
回調(diào)函數(shù)补胚。
options
有以下選項(xiàng):
sort: 如果有多個(gè)查詢條件码耐,按順序進(jìn)行查詢更新。
select: 設(shè)置數(shù)據(jù)的返回溶其。
conditions
:查詢條件骚腥;update
:需要修改的數(shù)據(jù);options
控制選項(xiàng)瓶逃;callback
回調(diào)函數(shù)束铭。
options
有以下選項(xiàng):
sort: 如果有多個(gè)查詢條件廓块,按順序進(jìn)行查詢更新。
maxTimeMS: 查詢用時(shí)上限契沫。
select: 設(shè)置數(shù)據(jù)的返回带猴。
復(fù)雜條件查詢
在之前的查詢說明中僅演示了確定值的查詢,如果遇到更加復(fù)雜的情況就需要使用其他一些方法埠褪。
詳細(xì)的文檔可以在這兒查找 mongodb查詢符浓利。
執(zhí)行查詢,回調(diào)函數(shù)钞速。
使用find()
贷掖、$where
之類查詢返回的是Mongoose自己封裝的Query對(duì)象,使用find()
可以在函數(shù)最后接上回調(diào)來獲取查詢到的數(shù)據(jù)渴语。
使用鏈?zhǔn)秸Z句時(shí)苹威,可以在之后接.exec()
執(zhí)行查詢,并指定回調(diào)函數(shù)驾凶。
blogModel.find({title: "Mongoose", meta.votes: 100}, {title: 1, author: 1, body: 1}).exec(function(err, docs){
if(err) console.log(err);
console.log('查詢結(jié)果:' + docs);
})
配合各種查詢符可以方便的實(shí)現(xiàn)復(fù)雜的查詢牙甫。
比如我需要查詢“title”中以“Mongoose”開頭,并且“meta”中“votes”的值小余100调违。并且按“meta”中“votes”的值升序排序窟哺。
blogModel.and([
{ title: { $regex: "Mongoose.+","$options":"i"}},
{ meta.votes: { $lt: 100}}
).sort({ meta.votes: 1}
).exec(function(err, docs){
if(err) console.log(err);
console.log('查詢結(jié)果:' + docs);
});
比較查詢運(yùn)算符
$equals 等于 / $gt 大于 / $gte 大于等于 / $lt 小余 / $lte 小余等于 / $ne 不等于 / $in 在數(shù)組中 / $nin 不在數(shù)組中
blogModel.find({meta.votes: {$lt: 100}});
查詢“meta”中的“votes”字段值小余100的數(shù)據(jù)。
blogModel.find({title: {$in: ['Mongoose', 'Mongodb', 'Nodejs']}});
查詢“title”為“Mongoose”或“Mongodb”或“Nodejs”其中之一的數(shù)據(jù)技肩。
邏輯查詢運(yùn)算符
blogModel.find({ $and: [
{meta.votes: {$gte: 50}},
{meta.votes: {$lt: 100}}
]});
查詢“meta”中的“votes”字段值大于等于50到小余100的數(shù)據(jù)且轨。
blogModel.find({ $nor: [
{meta.votes: 50},
{meta.votes: 100}
]});
查詢“meta”中的“votes”字段值不等于50和不等于100的數(shù)據(jù)。
以上例子也可以寫成這樣形式虚婿,比較清晰旋奢,其他類同
blogModel.and([
{meta.votes: {$gte: 50}},
{meta.votes: {$lt: 100}}
]);
blogModel.nor([
{meta.votes: 50},
{meta.votes: 100}
]);
元素查詢運(yùn)算符
$exists 查詢的字段值是否存在
blogModel.find({ title: {$exists: true}});
blogModel.where('title').exists(true);
查詢存在“title”字段的數(shù)據(jù)然痊。
評(píng)估查詢運(yùn)算符
$mod 與數(shù)據(jù)進(jìn)行取模運(yùn)算篩選
blogModel.find({ meta.votes: {$mod: [4, 0]}});
blogModel.where('meta.votes').$mod(4, 0);
查找“meta”中的“votes”字段值與4取模后至朗,值為0的數(shù)據(jù)。
$regex 使用正則表達(dá)式查詢數(shù)據(jù)
blogModel.find({ title: { $regex: "Mongoose.+","$options":"i"}});
搜索以“Mongoose”開頭的“title”字段剧浸,“options”中的“i”代表不區(qū)分大小寫锹引。
$options
參數(shù)與其余用法可以查看mongodb文檔中 $regex 一節(jié)。
$where 支持js表達(dá)式查詢
blogModel.find({ $where: 'this.comments.length === 10 || this.name.length === 5' });
blogModel.$where(function() { return this.comments.length === 10 || this.name.length === 5; });
數(shù)組查詢運(yùn)算符
Query#all([path], val) 查詢數(shù)組的本身及超集
blogModel.find( tags: ['nodejs', 'mongoose']);
查詢“tags”的字段值同時(shí)包含有['nodejs', 'mongoose']的數(shù)據(jù)唆香。只要值中包含此數(shù)組即返回?cái)?shù)據(jù)粤蝎,若是只包含數(shù)組中的一個(gè)則不返回此數(shù)據(jù)。
Query#elemMatch(path, criteria) 查詢數(shù)組的交集
blogModel.find( $elemMatch: { tags: 'mongoose', author: 'L'});
查詢“tags”為“mongoose”或是“author”為“L”的數(shù)據(jù)袋马。
Query#size([path], val) 查詢指定大小的數(shù)組
blogModel.find( tags: { $size: 2});
blogModel.where('tags').size(2);
查詢“tags”數(shù)組中包含兩個(gè)元素的數(shù)據(jù)初澎。
其他常用的運(yùn)算符
Query#limit(val) 限制查詢返回的數(shù)量
blogModel.find( tags: 'mongoose').limit(5);
查詢“tags”為“mongoose”的數(shù)據(jù),只返回前5個(gè)查詢結(jié)果。
Query#skip(val) 跳過前N個(gè)查詢結(jié)果
blogModel.find( tags: 'mongoose').skip(10).limit(5);
查詢“tags”為“mongoose”的數(shù)據(jù)碑宴,跳過前10個(gè)查詢結(jié)果软啼,返回從第11個(gè)開始的五個(gè)查詢結(jié)果。
做分頁時(shí)常用到這兩個(gè)延柠,但數(shù)據(jù)量過大時(shí)就會(huì)有性能問題祸挪。
Query#sort(arg) 對(duì)結(jié)果按某個(gè)指定字段進(jìn)行排序
1
、asc
為升序贞间,-1
贿条、desc
為降序≡鋈龋可以對(duì)一個(gè)字段進(jìn)行排序整以,也可以是多個(gè)。
blogModel.find( tags: 'mongoose').skip(10).limit(5).sort("{ meta.votes: 1}");
查詢“tags”為“mongoose”的數(shù)據(jù)峻仇,跳過前10個(gè)查詢結(jié)果公黑,返回從第11個(gè)開始的五個(gè)查詢結(jié)果。之后按“votes”進(jìn)行升序排序摄咆。
Query#count([criteria], [callback]) 計(jì)數(shù)
blogModel.count({ title: 'mongoose'}, function(err, docs){});
統(tǒng)計(jì)“title”為“mongoose”數(shù)據(jù)的數(shù)量
Query#select(arg) 選擇指定字段
在查詢中可以選擇指定的查詢字段凡蚜,或者排除指定的字段。+
為包含吭从,-
為排除朝蜘。
blogModel.select('title body');
只包含“title”、“body”字段涩金。
blogModel.select('-title -body');
排除“title”芹务、“body”字段。