Mongodb
配置選項(xiàng)
--quiet # 安靜輸出
--port arg # 指定服務(wù)端口號(hào),默認(rèn)端口27017
--bind_ip arg # 綁定服務(wù)IP,若綁定127.0.0.1,則只能本機(jī)訪問,不指定默認(rèn)本地所有IP
--logpath arg # 指定MongoDB日志文件,注意是指定文件不是目錄
--logappend # 使用追加的方式寫日志
--pidfilepath arg # PID File 的完整路徑,如果沒有設(shè)置,則沒有PID文件
--keyFile arg # 集群的私鑰的完整路徑,只對于Replica Set 架構(gòu)有效
--unixSocketPrefix arg # UNIX域套接字替代目錄,(默認(rèn)為 /tmp)
--fork # 以守護(hù)進(jìn)程的方式運(yùn)行MongoDB,創(chuàng)建服務(wù)器進(jìn)程
--auth # 啟用驗(yàn)證
--cpu # 定期顯示CPU的CPU利用率和iowait
--dbpath arg # 指定數(shù)據(jù)庫路徑
--diaglog arg # diaglog選項(xiàng) 0=off 1=W 2=R 3=both 7=W+some reads
--directoryperdb # 設(shè)置每個(gè)數(shù)據(jù)庫將被保存在一個(gè)單獨(dú)的目錄
--journal # 啟用日志選項(xiàng),MongoDB的數(shù)據(jù)操作將會(huì)寫入到j(luò)ournal文件夾的文件里
--journalOptions arg # 啟用日志診斷選項(xiàng)
--ipv6 # 啟用IPv6選項(xiàng)
--jsonp # 允許JSONP形式通過HTTP訪問(有安全影響)
--maxConns arg # 最大同時(shí)連接數(shù) 默認(rèn)2000
--noauth # 不啟用驗(yàn)證
--nohttpinterface # 關(guān)閉http接口,默認(rèn)關(guān)閉27018端口訪問
--noprealloc # 禁用數(shù)據(jù)文件預(yù)分配(往往影響性能)
--noscripting # 禁用腳本引擎
--notablescan # 不允許表掃描
--nounixsocket # 禁用Unix套接字監(jiān)聽
--nssize arg (=16) # 設(shè)置信數(shù)據(jù)庫.ns文件大小(MB)
--objcheck # 在收到客戶數(shù)據(jù),檢查的有效性,
--profile arg # 檔案參數(shù) 0=off 1=slow, 2=all
--quota # 限制每個(gè)數(shù)據(jù)庫的文件數(shù),設(shè)置默認(rèn)為8
--quotaFiles arg # number of files allower per db, requires --quota
--rest # 開啟簡單的rest API
--repair # 修復(fù)所有數(shù)據(jù)庫run repair on all dbs
--repairpath arg # 修復(fù)庫生成的文件的目錄,默認(rèn)為目錄名稱dbpath
--slowms arg (=100) # value of slow for profile and console log
--smallfiles # 使用較小的默認(rèn)文件
--syncdelay arg (=60) # 數(shù)據(jù)寫入磁盤的時(shí)間秒數(shù)(0=never,不推薦)
--sysinfo # 打印一些診斷系統(tǒng)信息
--upgrade # 如果需要升級(jí)數(shù)據(jù)庫
通常在mongod.conf中
dbpath = /data/db
logpath = /data/mongodb/mongodb.log
logappend = true
port = 27017
fork = true
auth = true
配置文件
設(shè)置了配置文件后啟動(dòng)時(shí)以自定義的配置文件啟動(dòng):mongod --config mongodb.conf
錯(cuò)誤提示
若提示DeprecationWarning: Mongoose: mpromise則在連接數(shù)據(jù)庫前加一句
mongoose.Promise = global.Promise;
shell命令
mongodb監(jiān)控工具
- 控制臺(tái)輸入mongostat可以實(shí)時(shí)輸出系統(tǒng)狀態(tài)
- 還可以通過mongodb的官方監(jiān)控工具M(jìn)MS來監(jiān)控(收費(fèi))
對數(shù)據(jù)庫的操作
- db.serverStatus()顯示數(shù)據(jù)庫服務(wù)器運(yùn)行狀態(tài)。
- 切換/創(chuàng)建數(shù)據(jù)庫:use yourDB; 當(dāng)創(chuàng)建一個(gè)集合(table)的時(shí)候會(huì)自動(dòng)創(chuàng)建當(dāng)前數(shù)據(jù)庫
- 查詢所有數(shù)據(jù)庫:show dbs;
- 查看當(dāng)前數(shù)據(jù)庫下所有的表:show collections
- 刪除當(dāng)前使用數(shù)據(jù)庫:db.dropDatabase();
- 從指定主機(jī)上克隆數(shù)據(jù)庫:db.cloneDatabase(“127.0.0.1”);
將指定機(jī)器上的數(shù)據(jù)庫的數(shù)據(jù)克隆到當(dāng)前數(shù)據(jù)庫 - 從指定的機(jī)器上復(fù)制指定數(shù)據(jù)庫數(shù)據(jù)到某個(gè)數(shù)據(jù)庫:db.copyDatabase("mydb", "temp", "127.0.0.1");
將本機(jī)的mydb的數(shù)據(jù)復(fù)制到temp數(shù)據(jù)庫中 - 修復(fù)當(dāng)前數(shù)據(jù)庫:db.repairDatabase();
- 查看當(dāng)前使用的數(shù)據(jù)庫:db.getName();
db; db和getName方法是一樣的效果,都可以查詢當(dāng)前使用的數(shù)據(jù)庫 - 顯示當(dāng)前db狀態(tài):db.stats();
- 當(dāng)前db版本:db.version();
- 查看當(dāng)前db的鏈接機(jī)器地址:db.getMongo();
對表的操作:foo是數(shù)據(jù)庫內(nèi)的一張表
db.foo.find(...).count()
db.foo.find(...).limit(n) 根據(jù)條件查找數(shù)據(jù)并返回指定記錄數(shù)
db.foo.find(...).skip(n)
db.foo.find(...).sort(...) 查找排序
db.foo.findOne([query]) 根據(jù)條件查詢只查詢一條數(shù)據(jù)
db.foo.getDB() get DB object associated with collection 返回表所屬的庫
db.foo.getIndexes() 顯示表的所有索引
db.foo.group( { key : ..., initial: ..., reduce : ...[, cond: ...] } ) 根據(jù)條件分組
db.foo.mapReduce( mapFunction , reduceFunction , <optional params> )
db.foo.remove(query) 根據(jù)條件刪除數(shù)據(jù)
db.foo.renameCollection( newName ) renames the collection 重命名表
db.foo.save(obj) 保存數(shù)據(jù)
db.foo.stats() 查看表的狀態(tài)
db.foo.storageSize() - includes free space allocated to this collection 查詢分配到表空間大小
db.foo.totalIndexSize() - size in bytes of all the indexes 查詢所有索引的大小
db.foo.totalSize() - storage allocated for all data and indexes 查詢表的總大小
db.foo.update(query, object[, upsert_bool]) 根據(jù)條件更新數(shù)據(jù)
db.foo.validate() - SLOW 驗(yàn)證表的詳細(xì)信息
db.foo.getShardVersion() - only for use with sharding
用戶相關(guān)
- 添加一個(gè)用戶:每個(gè)數(shù)據(jù)庫獨(dú)立設(shè)置自己的用戶,數(shù)據(jù)庫之間相互獨(dú)立
- db.createUser({user:"admin",pwd:"pass",roles:["readWrite","dbAdmin"]})
- userSource <database> (可選)代替pwd字段,指向具有相同的用戶定義的另一個(gè)數(shù)據(jù)庫。pwd或那個(gè)數(shù)據(jù)庫的userSource然后被用作該用戶的憑據(jù)。userSource字段和pwd字段是相互排斥的,一個(gè)文檔不能同時(shí)包含兩者。
- roles:指定用戶的角色,可以用一個(gè)空數(shù)組給新用戶設(shè)定空角色;在roles字段,可以指定內(nèi)置角色和用戶定義的角色。role里的角色可以選:
Built-In Roles(內(nèi)置角色):
1. 數(shù)據(jù)庫用戶角色:read、readWrite;
2. 數(shù)據(jù)庫管理角色:dbAdmin、dbOwner、userAdmin;
3. 集群管理角色:clusterAdmin、clusterManager、clusterMonitor、hostManager;
4. 備份恢復(fù)角色:backup、restore;
5. 所有數(shù)據(jù)庫角色:readAnyDatabase、readWriteAnyDatabase、userAdminAnyDatabase、dbAdminAnyDatabase
6. 超級(jí)用戶角色:root
// 這里還有幾個(gè)角色間接或直接提供了系統(tǒng)超級(jí)用戶的訪問(dbOwner 、userAdmin、userAdminAnyDatabase)
7. 內(nèi)部角色:__system
- 具體角色
Read:允許用戶讀取指定數(shù)據(jù)庫
readWrite:允許用戶讀寫指定數(shù)據(jù)庫
dbAdmin:允許用戶在指定數(shù)據(jù)庫中執(zhí)行管理函數(shù),如索引創(chuàng)建、刪除,查看統(tǒng)計(jì)或訪問system.profile
userAdmin:允許用戶向system.users集合寫入,可以找指定數(shù)據(jù)庫里創(chuàng)建、刪除和管理用戶
clusterAdmin:只在admin數(shù)據(jù)庫中可用,賦予用戶所有分片和復(fù)制集相關(guān)函數(shù)的管理權(quán)限。
readAnyDatabase:只在admin數(shù)據(jù)庫中可用,賦予用戶所有數(shù)據(jù)庫的讀權(quán)限
readWriteAnyDatabase:只在admin數(shù)據(jù)庫中可用,賦予用戶所有數(shù)據(jù)庫的讀寫權(quán)限
userAdminAnyDatabase:只在admin數(shù)據(jù)庫中可用,賦予用戶所有數(shù)據(jù)庫的userAdmin權(quán)限
dbAdminAnyDatabase:只在admin數(shù)據(jù)庫中可用,賦予用戶所有數(shù)據(jù)庫的dbAdmin權(quán)限。
root:只在admin數(shù)據(jù)庫中可用。超級(jí)賬號(hào),超級(jí)權(quán)限
- 顯示當(dāng)前所有用戶:show users
- 列出admin數(shù)據(jù)庫中的用戶內(nèi)容:db.system.users.find()
- 在控制臺(tái)中執(zhí)行認(rèn)證
- db.auth("username","password")
- 輸出1為認(rèn)證成功0為認(rèn)證失敗
- 刪除用戶
- db.dropUser("userName");
啟用認(rèn)證
- 在mongodb.conf中添加auth=true,重啟mongodb service mongodb restart --config mongodb.conf
- 啟動(dòng)時(shí)使用 mongod --auth
mogoose驗(yàn)證
- mongoose.connect("mongodb://user:pwd@127.0.0.1:27017/test");
安全
一. 使用Linux IPtables限制IP范圍
這種方法和MongoDB本身沒有關(guān)系,而是借用Linux的iptables功能,限制允許訪問MongoDB端口的IP地址,具體的做法如下:
# 拒絕所有訪問27017端口的請求
sudo iptables -I INPUT -p tcp --dport 27017 -j DROP
# 允許本地訪問mongo端口
sudo iptables -I INPUT -s 127.0.0.1 -p tcp --dport 27017 -j ACCEPT
sudo iptables-save
OK,這樣就只允許通過本地訪問MongoDB服務(wù)了。
備份
mongodump備份數(shù)據(jù)庫
-
常用命令格
mongodump -h IP --port 端口 -u 用戶名 -p 密碼 -d 數(shù)據(jù)庫 -o 文件存在路徑 如果沒有用戶誰,可以去掉-u和-p。 如果導(dǎo)出本機(jī)的數(shù)據(jù)庫,可以去掉-h。 如果是默認(rèn)端口,可以去掉--port。 如果想導(dǎo)出所有數(shù)據(jù)庫,可以去掉-d。 如果存在當(dāng)前文件夾,可以去掉-o。 可輸入mongodump --help查看說明 導(dǎo)出所有數(shù)據(jù)庫:mongodump -h 127.0.0.1 -o /home/mongodb/
- 導(dǎo)出指定數(shù)據(jù)庫:mongodump -h 192.168.1.108 -d tank -o /home/mongodb/
mongorestore還原數(shù)據(jù)庫
- 常用命令格式
mongorestore -h IP --port 端口 -u 用戶名 -p 密碼 -d 數(shù)據(jù)庫 --drop 文件存在路徑 --drop的意思是,先刪除所有的記錄,然后恢復(fù)。 - 恢復(fù)所有數(shù)據(jù)庫到mongodb中
mongorestore /home /mongodb/ #這里的路徑是所有庫的備份路徑
-
還原指定的數(shù)據(jù)庫
- mongorestore -d tank /home/mongodb/tank/ #tank這個(gè)數(shù)據(jù)庫的備份路徑
- mongorestore -d tank_new /home/mongodb/tank/ #將tank還有tank_new數(shù)據(jù)庫中
這二個(gè)命令,可以實(shí)現(xiàn)數(shù)據(jù)庫的備份與還原,文件格式是json和bson的。無法指寫到表備份或者還原。
mongoexport導(dǎo)出表,或者表中部分字段
mongoexport -h IP --port 端口 -u 用戶名 -p 密碼 -d 數(shù)據(jù)庫 -c 表名 -f 字段 -q 條件導(dǎo)出 --type=csv -o 文件名
-f 導(dǎo)出指字段,以字號(hào)分割,-f name,email,age導(dǎo)出name,email,age這三個(gè)字段
-q 可以根查詢條件導(dǎo)出,-q '{ "uid" : "100" }' 導(dǎo)出uid為100的數(shù)據(jù)
--csv 表示導(dǎo)出的文件格式為csv的,這個(gè)比較有用,因?yàn)榇蟛糠值年P(guān)系型數(shù)據(jù)庫都是支持csv,在這里有共同點(diǎn)
- 導(dǎo)出整張表
mongoexport -d tank -c users -o /home/mongodb/tank/users.dat
- 導(dǎo)出表中部分字段
mongoexport -d tank -c users --csv -f uid,name,sex -o tank/users.csv
- 根據(jù)條件敢出數(shù)據(jù)
mongoexport -d tank -c users -q '{uid:{$gt:1}}' -o tank/users.json
mongoimport導(dǎo)入表,或者表中部分字段
- 還原整表導(dǎo)出的非csv文件
mongoimport -h IP --port 端口 -u 用戶名 -p 密碼 -d 數(shù)據(jù)庫 -c 表名 --upsert --drop 文件名
重點(diǎn)說一下--upsert,其他參數(shù)上面的命令已有提到,--upsert 插入或者更新現(xiàn)有數(shù)據(jù) - 還原部分字段的導(dǎo)出文件
mongoimport -h IP --port 端口 -u 用戶名 -p 密碼 -d 數(shù)據(jù)庫 -c 表名 --upsertFields 字段 --drop 文件名
--upsertFields根--upsert一樣 - 還原導(dǎo)出的csv文件
mongoimport -h IP --port 端口 -u 用戶名 -p 密碼 -d 數(shù)據(jù)庫 -c 表名 --type 類型 --headerline --upsert --drop 文件名
上面三種情況,還可以有其他排列組合的。 - 還原導(dǎo)出的表數(shù)據(jù)
mongoimport -d tank -c users --upsert tank/users.dat - 部分字段的表數(shù)據(jù)導(dǎo)入
mongoimport -d tank -c users --upsertFields uid,name,sex tank/users.dat - 還原csv文件
mongoimport -d tank -c users --type csv --headerline --file tank/users.csv
mongoose
- Schema、Model、Entity的關(guān)系請牢記,Schema生成Model,Model創(chuàng)造Entity,Model和Entity都可對數(shù)據(jù)庫操作造成影響,但Model比Entity更具操作性。
Schema.Types
NodeJS中的基本數(shù)據(jù)類型都屬于Schema.Type,另外Mongoose還定義了自己的類型
//舉例:
var ExampleSchema = new Schema({
name:String,
binary:Buffer,
living:Boolean,
updated:Date,
age:Number,
mixed:Schema.Types.Mixed, //該混合類型等同于nested
_id:Schema.Types.ObjectId, //主鍵
_fk:Schema.Types.ObjectId, //外鍵
array:[],
arrOfString:[String],
arrOfNumber:[Number],
arrOfDate:[Date],
arrOfBuffer:[Buffer],
arrOfBoolean:[Boolean],
arrOfMixed:[Schema.Types.Mixed],
arrOfObjectId:[Schema.Types.ObjectId]
nested:{
stuff:String,
}
});
關(guān)于Mixed
Schema.Types.Mixed是Mongoose定義個(gè)混合類型,該混合類型如果未定義具體形式。因此,如果定義具體內(nèi)容,就直接使用{}來定義,以下兩句等價(jià)
var AnySchema = new Schema({any:{}});
var AnySchema = new Schema({any:Schema.Types.Mixed});
混合類型因?yàn)闆]有特定約束,因此可以任意修改,一旦修改了原型,則必須調(diào)用markModified()
person.anything = {x:[3,4,{y:'change'}]}
person.markModified('anything');//傳入anything,表示該屬性類型發(fā)生變化
person.save();
關(guān)于ObjectId
主鍵,一種特殊而且非常重要的類型,每個(gè)Schema都會(huì)默認(rèn)配置這個(gè)屬性,屬性名為_id,除非自己定義,方可覆蓋
var mongoose = require('mongoose');
var ObjectId = mongoose.Schema.Types.ObjectId;
var StudentSchema = new Schema({}); //默認(rèn)會(huì)有_id:ObjectId
var TeacherSchema = new Schema({id:ObjectId});//只有id:ObjectId
該類型的值由系統(tǒng)自己生成,從某種意義上幾乎不會(huì)重復(fù),生成過程比較復(fù)雜,有興趣的朋友可以查看源碼。
2. Schema的擴(kuò)展
2.1 實(shí)例方法
有的時(shí)候,我們創(chuàng)造的Schema不僅要為后面的Model和Entity提供公共的屬性,還要提供公共的方法。
下面例子比快速通道的例子更加高級(jí),可以進(jìn)行高級(jí)擴(kuò)展:
var PersonSchema = new Schema({name:String,type:String});
//查詢類似數(shù)據(jù)
PersonSchema.methods.findSimilarTypes = function(cb){
return this.model('Person').find({type:this.type},cb);
}
使用如下:
var PersonModel = mongoose.model('Person',PersonSchema);
var krouky = new PersonSchema({name:'krouky',type:'前端工程師'});
krouky.findSimilarTypes(function(err,persons){
//persons中就能查詢到其他前端工程師
});
2.2 靜態(tài)方法
靜態(tài)方法在Model層就能使用,如下:
PersonSchema.statics.findByName = function(name,cb){
this.find({name:new RegExp(name,'i'),cb});
}
var PersonModel = mongoose.model('Person',PersonSchema);
PersonModel.findByName('krouky',function(err,persons){
//找到所有名字叫krouky的人
});
2.3 索引
索引或者復(fù)合索引能讓搜索更加高效,默認(rèn)索引就是主鍵索引ObjectId,屬性名為_id, 索引會(huì)作為一個(gè)專題來講解
2.4 虛擬屬性
Schema中如果定義了虛擬屬性,那么該屬性將不寫入數(shù)據(jù)庫,例如:
var PersonSchema = new Schema({
name:{
first:String,
last:String
}
});
var PersonModel = mongoose.model('Person',PersonSchema);
var krouky = new PersonModel({
name:{first:'krouky',last:'han'}
});
如果每次想使用全名就得這樣
console.log(krouky.name.first + ' ' + krouky.name.last);
顯然這是很麻煩的,我們可以定義虛擬屬性:
PersonSchema.virtual('name.full').get(function(){
return this.name.first + ' ' + this.name.last;
});
那么就能用krouky.name.full來調(diào)用全名了,反之如果知道full,也可以反解first和last屬性
PersonSchema.virtual('name.full').set(function(name){
var split = name.split(' ');
this.name.first = split[0];
this.name.last = split[1];
});
var PersonModel = mongoose.model('Person',PersonSchema);
var krouky = new PersonModel({});
krouky.name.full = 'krouky han';//會(huì)被自動(dòng)分解
console.log(krouky.name.first);//krouky
2.5 配置項(xiàng)
在使用new Schema(config)時(shí),我們可以追加一個(gè)參數(shù)options來配置Schema的配置,形如:
var ExampleSchema = new Schema(config,options);
或者使用
var ExampleSchema = new Schema(config);
ExampleSchema.set(option,value);
可供配置項(xiàng)有:safe、strict、capped、versionKey、autoIndex
2.5.1 safe——安全屬性(默認(rèn)安全)
一般可做如下配置:
new Schema({...},{safe:true});
當(dāng)然我們也可以這樣
new Schema({...},{safe:{j:1,w:2,wtimeout:10000}});
j表示做1份日志,w表示做2個(gè)副本(尚不明確),超時(shí)時(shí)間10秒
2.5.2 strict——嚴(yán)格配置(默認(rèn)啟用)
確保Entity的值存入數(shù)據(jù)庫前會(huì)被自動(dòng)驗(yàn)證,如果你沒有充足的理由,請不要停用,例子:
var ThingSchema = new Schema({a:String});
var ThingModel = db.model('Thing',SchemaSchema);
var thing = new Thing({iAmNotInTheThingSchema:true});
thing.save();//iAmNotInTheThingSchema這個(gè)屬性將無法被存儲(chǔ)
如果取消嚴(yán)格選項(xiàng),iAmNotInTheThingSchema將會(huì)被存入數(shù)據(jù)庫
該選項(xiàng)也可以在構(gòu)造實(shí)例時(shí)使用,例如:
var ThingModel = db.model('Thing');
var thing1 = new ThingModel(doc,true); //啟用嚴(yán)格
var thing2 = new ThingModel(doc,false); //禁用嚴(yán)格
注意:
strict也可以設(shè)置為throw,表示出現(xiàn)問題將會(huì)拋出錯(cuò)誤
2.5.3 shardKey
需要mongodb做分布式,才會(huì)使用該屬性
2.5.4 capped——上限設(shè)置
如果有數(shù)據(jù)庫的批量操作,該屬性能限制一次操作的量,例如:
new Schema({...},{capped:1024}); //一次操作上線1024條數(shù)據(jù)
當(dāng)然該參數(shù)也可是JSON對象,包含size、max、autiIndexId屬性
new Schema({...},{capped:{size:1024,max:100,autoIndexId:true}});
2.5.5 versionKey——版本鎖
版本鎖是Mongoose默認(rèn)配置(__v屬性)的,如果你想自己定制,如下:
new Schema({...},{versionKey:'__someElse'});
此時(shí)存入數(shù)據(jù)庫的版本鎖就不是__v屬性,而是__someElse,相當(dāng)于是給版本鎖取名字。
具體怎么存入都是由Mongoose和MongoDB自己決定,當(dāng)然,這個(gè)屬性你也可以去除
new Schema({...},{versionKey:false});
除非你知道你在做什么,并且你知道這樣做的后果
2.5.6 autoIndex——自動(dòng)索引
該內(nèi)容將在索引章節(jié)單獨(dú)講解
3. Documents
Document是與MongoDB文檔一一對應(yīng)的模型,Document可等同于Entity,具有屬性和操作性
注意:
Document的`CRUD都必須經(jīng)過嚴(yán)格驗(yàn)證的,參看2.5.2 Schema的strict嚴(yán)格配置
3.2 更新
有許多方式來更新文件,以下是常用的傳統(tǒng)方式:
PersonModel.findById(id,function(err,person){
person.name = 'MDragon';
person.save(function(err){});
});
這里,利用Model模型查詢到了person對象,該對象屬于Entity,可以有save操作,如果使用Model`操作,需注意:
PersonModel.findById(id,function(err,person){
person.name = 'MDragon';
var _id = person._id; //需要取出主鍵_id
delete person._id; //再將其刪除
PersonModel.update({_id:_id},person,function(err){});
//此時(shí)才能用Model操作,否則報(bào)錯(cuò)
});
update第一個(gè)參數(shù)是查詢條件,第二個(gè)參數(shù)是更新的對象,但不能更新主鍵,這就是為什么要?jiǎng)h除主鍵的原因。
當(dāng)然這樣的更新很麻煩,可以使用$set屬性來配置,這樣也不用先查詢,如果更新的數(shù)據(jù)比較少,可用性還是很好的:
PersonModel.update({_id:_id},{$set:{name:'MDragon'}},function(err){});
需要注意,Document的CRUD操作都是異步執(zhí)行,callback第一個(gè)參數(shù)必須是err,而第二個(gè)參數(shù)各個(gè)方法不一樣,update的callback第二個(gè)參數(shù)是更新的數(shù)量,如果要返回更新后的對象,則要使用如下方法
Person.findByIdAndUpdate(_id,{$set:{name:'MDragon'}},function(err,person){
console.log(person.name); //MDragon
});
類似的方法還有findByIdAndRemove,如同名字,只能根據(jù)id查詢并作update/remove操作,操作的數(shù)據(jù)僅一條
3.3 新增
如果是Entity,使用save方法,如果是Model,使用create方法
//使用Entity來增加一條數(shù)據(jù)
var krouky = new PersonModel({name:'krouky'});
krouky.save(callback);
//使用Model來增加一條數(shù)據(jù)
var MDragon = {name:'MDragon'};
PersonModel.create(MDragon,callback);
兩種新增方法區(qū)別在于,如果使用Model新增時(shí),傳入的對象只能是純凈的JSON對象,不能是由Model創(chuàng)建的實(shí)體,原因是:由Model創(chuàng)建的實(shí)體krouky雖然打印是只有{name:'krouky'},但是krouky屬于Entity,包含有Schema屬性和Model數(shù)據(jù)庫行為模型。如果是使用Model創(chuàng)建的對象,傳入時(shí)一定會(huì)將隱藏屬性也存入數(shù)據(jù)庫,雖然3.x追加了默認(rèn)嚴(yán)格屬性,但也不必要增加操作的報(bào)錯(cuò)
3.4 刪除
和新增一樣,刪除也有2種方式,但Entity和Model都使用remove方法
4.Sub Docs
如同SQL數(shù)據(jù)庫中2張表有主外關(guān)系,Mongoose將2個(gè)Document的嵌套叫做Sub-Docs(子文檔)
簡單的說就是一個(gè)Document嵌套另外一個(gè)Document或者Documents:
var ChildSchema1 = new Schema({name:String});
var ChildSchema2 = new Schema({name:String});
var ParentSchema = new Schema({
children1:ChildSchema1, //嵌套Document
children2:[ChildSchema2] //嵌套Documents
});
Sub-Docs享受和Documents一樣的操作,但是Sub-Docs的操作都由父類去執(zhí)行
var ParentModel = db.model('Parent',parentSchema);
var parent = new ParentModel({
children2:[{name:'c1'},{name:'c2'}]
});
parent.children2[0].name = 'd';
parent.save(callback);
parent在執(zhí)行保存時(shí),由于包含children2,他是一個(gè)數(shù)據(jù)庫模型對象,因此會(huì)先保存chilren2[0]和chilren2[1]。
如果子文檔在更新時(shí)出現(xiàn)錯(cuò)誤,將直接報(bào)在父類文檔中,可以這樣處理:
ChildrenSchema.pre('save',function(next){
if('x' === this.name) return next(new Error('#err:not-x'));
next();
});
var parent = new ParentModel({children1:{name:'not-x'}});
parent.save(function(err){
console.log(err.message); //#err:not-x
});
var child = parent.children.id(id);
4.2 新增、刪除、更新
子文檔是父文檔的一個(gè)屬性,因此按照屬性的操作即可,不同的是在新增父類的時(shí)候,子文檔是會(huì)被先加入進(jìn)去的。
如果ChildrenSchema是臨時(shí)的一個(gè)子文檔,不作為數(shù)據(jù)庫映射集合,可以這樣:
var ParentSchema = new Schema({
children:{
name:String
}
});
//其實(shí)就是匿名混合模式
5.Model
5.1 什么是Model
Model模型,是經(jīng)過Schema構(gòu)造來的,除了Schema定義的數(shù)據(jù)庫骨架以外,還具有數(shù)據(jù)庫行為模型,他相當(dāng)于管理數(shù)據(jù)庫屬性、行為的類
5.2 如何創(chuàng)建Model
你必須通過Schema來創(chuàng)建,如下:
//先創(chuàng)建Schema
var TankSchema = new Schema({
name:'String',
size:'String'
});
//通過Schema創(chuàng)建Model
var TankModel = mongoose.model('Tank',TankSchema);
5.2 操作Model
該模型就能直接拿來操作,具體查看API,例如:
var tank = {'something',size:'small'};
TankModel.create(tank);
注意:
你可以使用Model來創(chuàng)建Entity,Entity實(shí)體是一個(gè)特有Model具體對象,但是他并不具備Model的方法,只能用自己的方法。
//通過Model創(chuàng)建Entity
var tankEntity = new TankModel('someother','size:big');
tankEntity.save();
6.1.1 直接查詢
在查詢時(shí)帶有回調(diào)函數(shù)的,稱之為直接查詢,查詢的條件往往通過API來設(shè)定,例如:
PersonModel.findOne({'name.last':'dragon'},'some select',function(err,person){
//如果err==null,則person就能取到數(shù)據(jù)
});
具體的查詢參數(shù),請查詢API
6.1.2 鏈?zhǔn)讲樵?/h5>
在查詢時(shí)候,不帶回調(diào),而查詢條件通過API函數(shù)來制定,例如:
var query = PersonModel.findOne({'name.last':'dragon'});
query.select('some select');
query.exec(function(err,pserson){
//如果err==null,則person就能取到數(shù)據(jù)
});
這種方式相對直接查詢,分的比較明細(xì),如果不帶callback,則返回query,query沒有執(zhí)行的預(yù)編譯查詢語句,該query對象執(zhí)行的方法都將返回自己,只有在執(zhí)行exec方法時(shí)才執(zhí)行查詢,而且必須有回調(diào)。
因?yàn)閝uery的操作始終返回自身,我們可以采用更形象的鏈?zhǔn)綄懛?/p>
Person
.find({ occupation: /host/ })
.where('name.last').equals('Ghost')
.where('age').gt(17).lt(66)
.where('likes').in(['vaporizing', 'talking'])
.limit(10)
.sort('-occupation')
.select('name occupation')
.exec(callback);
7.Validation
數(shù)據(jù)的存儲(chǔ)是需要驗(yàn)證的,不是什么數(shù)據(jù)都能往數(shù)據(jù)庫里丟或者顯示到客戶端的,數(shù)據(jù)的驗(yàn)證需要記住以下規(guī)則:
驗(yàn)證始終定義在SchemaType中
驗(yàn)證是一個(gè)內(nèi)部中間件
驗(yàn)證是在一個(gè)Document被保存時(shí)默認(rèn)啟用的,除非你關(guān)閉驗(yàn)證
驗(yàn)證是異步遞歸的,如果你的SubDoc驗(yàn)證失敗,Document也將無法保存
驗(yàn)證并不關(guān)心錯(cuò)誤類型,而通過ValidationError這個(gè)對象可以訪問
7.1 驗(yàn)證器
required 非空驗(yàn)證
min/max 范圍驗(yàn)證(邊值驗(yàn)證)
enum/match 枚舉驗(yàn)證/匹配驗(yàn)證
validate 自定義驗(yàn)證規(guī)則
以下是綜合案例:
var PersonSchema = new Schema({
name:{
type:'String',
required:true //姓名非空
},
age:{
type:'Nunmer',
min:18, //年齡最小18
max:120 //年齡最大120
},
city:{
type:'String',
enum:['北京','上海'] //只能是北京、上海人
},
other:{
type:'String',
validate:[validator,err] //validator是一個(gè)驗(yàn)證函數(shù),err是驗(yàn)證失敗的錯(cuò)誤信息
}
});
7.2 驗(yàn)證失敗
如果驗(yàn)證失敗,則會(huì)返回err信息,err是一個(gè)對象該對象屬性如下
err.errors //錯(cuò)誤集合(對象)
err.errors.color //錯(cuò)誤屬性(Schema的color屬性)
err.errors.color.message //錯(cuò)誤屬性信息
err.errors.path //錯(cuò)誤屬性路徑
err.errors.type //錯(cuò)誤類型
err.name //錯(cuò)誤名稱
err.message //錯(cuò)誤消息
一旦驗(yàn)證失敗,Model和Entity都將具有和err一樣的errors屬性
修改器和更新器
更新修改器:
‘$inc’ 增減修改器,只對數(shù)字有效.下面的實(shí)例: 找到 age=22的文檔,修改文檔的age值自增1
Model.update({‘a(chǎn)ge’:22}, {’$inc’:{‘a(chǎn)ge’:1} } );
執(zhí)行后: age=23
‘$set’ 指定一個(gè)鍵的值,這個(gè)鍵不存在就創(chuàng)建它.可以是任何MondoDB支持的類型.
Model.update({‘a(chǎn)ge’:22}, {’$set’:{‘a(chǎn)ge’:‘haha’} } );
執(zhí)行后: age=‘haha’
‘$unset’ 同上取反,刪除一個(gè)鍵
Model.update({‘a(chǎn)ge’:22}, {’$unset’:{‘a(chǎn)ge’:‘haha’} } );
執(zhí)行后: age鍵不存在
數(shù)組修改器:
‘$push’ 給一個(gè)鍵push一個(gè)數(shù)組成員,鍵不存在會(huì)創(chuàng)建
Model.update({‘a(chǎn)ge’:22}, {’$push’:{‘a(chǎn)rray’:10} } );
執(zhí)行后: 增加一個(gè) array 鍵,類型為數(shù)組, 有一個(gè)成員 10
‘$addToSet’ 向數(shù)組中添加一個(gè)元素,如果存在就不添加
Model.update({‘a(chǎn)ge’:22}, {’$addToSet’:{‘a(chǎn)rray’:10} } );
執(zhí)行后: array中有10所以不會(huì)添加
‘$each’ 遍歷數(shù)組, 和 $push 修改器配合可以插入多個(gè)值
Model.update({‘a(chǎn)ge’:22}, {’$push’:{‘a(chǎn)rray’:{’$each’: [1,2,3,4,5]}} } );
執(zhí)行后: array : [10,1,2,3,4,5]
‘$pop’ 向數(shù)組中尾部刪除一個(gè)元素
Model.update({‘a(chǎn)ge’:22}, {’$pop’:{‘a(chǎn)rray’:1} } );
執(zhí)行后: array : [10,1,2,3,4] tips: 將1改成-1可以刪除數(shù)組首部元素
‘$pull’ 向數(shù)組中刪除指定元素
Model.update({‘a(chǎn)ge’:22}, {’$pull’:{‘a(chǎn)rray’:10} } );
執(zhí)行后: array : [1,2,3,4] 匹配到array中的10后將其刪除
條件查詢:
“$lt” 小于
“$lte” 小于等于
“$gt” 大于
“$gte” 大于等于
“$ne” 不等于
Model.find({“age”:{ “$get”:18 , “$lte”:30 } } );
查詢 age 大于等于18并小于等于30的文檔
或查詢 OR:
‘$in’ 一個(gè)鍵對應(yīng)多個(gè)值
‘$nin’ 同上取反, 一個(gè)鍵不對應(yīng)指定值
“$or” 多個(gè)條件匹配, 可以嵌套 $in 使用
“$not” 同上取反, 查詢與特定模式不匹配的文檔
Model.find({“age”:{ “$in”:[20,21,22.‘haha’]} } );
查詢 age等于20或21或21或’haha’的文檔
Model.find({"$or" : [ {‘a(chǎn)ge’:18} , {‘name’:‘xueyou’} ] });
查詢 age等于18 或 name等于’xueyou’ 的文檔
類型查詢:
null 能匹配自身和不存在的值, 想要匹配鍵的值 為null, 就要通過 “$exists” 條件判定鍵值已經(jīng)存在
"$exists" (表示是否存在的意思)
Model.find(“age” : { “$in” : [null] , “exists” : true } );
查詢 age值為null的文檔
Model.find({name: {$exists: true}},function(error,docs){
//查詢所有存在name屬性的文檔
});
Model.find({telephone: {$exists: false}},function(error,docs){
//查詢所有不存在telephone屬性的文檔
});
正則表達(dá)式:
MongoDb 使用 Prel兼容的正則表達(dá)式庫來匹配正則表達(dá)式
find( {“name” : /joe/i } )
查詢name為 joe 的文檔, 并忽略大小寫
find( {“name” : /joe?/i } )
查詢匹配各種大小寫組合
查詢數(shù)組:
Model.find({“array”:10} );
查詢 array(數(shù)組類型)鍵中有10的文檔, array : [1,2,3,4,5,10] 會(huì)匹配到
Model.find({“array[5]”:10} );
查詢 array(數(shù)組類型)鍵中下標(biāo)5對應(yīng)的值是10, array : [1,2,3,4,5,10] 會(huì)匹配到
‘$all’ 匹配數(shù)組中多個(gè)元素
Model.find({“array”:[5,10]} );
查詢 匹配array數(shù)組中 既有5又有10的文檔
‘$size’ 匹配數(shù)組長度
Model.find({“array”:{"$size" : 3} } );
查詢 匹配array數(shù)組長度為3 的文檔
‘$slice’ 查詢子集合返回
Model.find({“array”:{"$skice" : 10} } );
查詢 匹配array數(shù)組的前10個(gè)元素
Model.find({“array”:{"$skice" : [5,10] } } );
查詢 匹配array數(shù)組的第5個(gè)到第10個(gè)元素
where
用它可以執(zhí)行任意javacript語句作為查詢的一部分,如果回調(diào)函數(shù)返回 true 文檔就作為結(jié)果的一部分返回
find( {"$where" : function(){
for( var x in this ){
//這個(gè)函數(shù)中的 this 就是文檔
}
if(this.x !== null && this.y !== null){
return this.x + this.y === 10 ? true : false;
}else{
return true;
}
} } )
簡化版本
find( {"$where" : "this.x + this.y === 10" } )
find( {"$where" : " function(){ return this.x + this.y ===10; } " } )
游標(biāo):
- limit(3) 限制返回結(jié)果的數(shù)量,
- skip(3) 跳過前3個(gè)文檔,返回其余的
- sort( {“username”:1 , “age”:-1 } ) 排序 鍵對應(yīng)文檔的鍵名, 值代表排序方向, 1 升序, -1降序
mongoose在express中的使用
- 在express中使用mongoose時(shí)在主文件中定義連接,將連接上的connection定義在node全局的global上
global.db = mongoose.createConnection(uri); - 在路由中單獨(dú)列出model.js文件,因?yàn)?strong>model不可以重復(fù)定義,但路由總是會(huì)重復(fù)調(diào)用,所以需要將model單列出來。
- 在model文件中引入mongoose并定義Schema,和Schema的方法最后module.exports=bd.model(“model名”,scheam)導(dǎo)出model。(db是全局的,所以可以隨處調(diào)用)
- 在需要用到數(shù)據(jù)庫的路由中引用model.js,通過model創(chuàng)建實(shí)體Entity和操作數(shù)據(jù)庫。
- 每次對數(shù)據(jù)庫的操作結(jié)束后要記得db.close();
注意點(diǎn)
- 要查詢數(shù)據(jù)庫信息首先要按照數(shù)據(jù)庫內(nèi)的數(shù)據(jù)定義Schema,再定義model,注意:var Mymodel = db.model('test', weixinSchema); 對應(yīng)的是數(shù)據(jù)庫中tests表。之后才能通過model來查詢數(shù)據(jù)。即對數(shù)據(jù)庫中表的操作都需要通過model來操作。