MongoDB 關(guān)系
MongoDB 的關(guān)系表示多個(gè)文檔之間在邏輯上的相互聯(lián)系贴彼。文檔間可以通過嵌入和引用來建立聯(lián)系蓄愁。MongoDB 中的關(guān)系可以是:
1:1 (1對(duì)1)
1:N (1對(duì)多)
N:1 (多對(duì)1)
N:N (多對(duì)多)
例如用戶與用戶地址的關(guān)系:一個(gè)用戶可以有多個(gè)地址暮胧,所以是一對(duì)多的關(guān)系好唯。以下是 user 文檔的簡單結(jié)構(gòu):
db.user.insert({"_id": ObjectId("52ffc33cd85242f436000001"),"name": "admin1","phone": "86-15212341111","birthday": "1991-10-20","email": "admin1@gmail.com"})
以下是 address 文檔的簡單結(jié)構(gòu):
db.address.insert({"_id": ObjectId("52ffc4a5d85242602e000000"),"province": "四川省","city": "成都市","area": "錦江區(qū)","street": "紅星路三段99號(hào)"})
嵌入式關(guān)系使用嵌入式方法鞋诗,可以把用戶地址嵌入到用戶的文檔中:
db.user.insert({"_id": ObjectId("52ffc33cd85242f436000002"),"name": "admin2","phone": "86-15212342222","birthday": "1992-10-20","email": "admin2@gmail.com","address": [{ "province": "四川省", "city": "成都市", "area": "錦江區(qū)", "street": "紅星路三段99號(hào)"},{ "province": "廣東省", "city": "深圳市", "area": "羅湖區(qū)", "street": "建設(shè)路1003號(hào)"}]})
以上數(shù)據(jù)保存一個(gè)文檔中穴店,數(shù)據(jù)容易獲取撕捍、維護(hù),查詢用戶的地址:
db.users.findOne({"name" : "admin2"}, {"address" : 1})
這種數(shù)據(jù)結(jié)構(gòu)的缺點(diǎn)是:如果用戶和用戶地址在不斷增加泣洞,數(shù)據(jù)量不斷變大忧风,會(huì)影響讀寫性能。
引用式關(guān)系引用式關(guān)系是設(shè)計(jì)數(shù)據(jù)庫時(shí)經(jīng)常用到的方法球凰,這種方法把用戶數(shù)據(jù)文檔和用戶地址數(shù)據(jù)文檔分開狮腿,通過引用文檔的 id 字段來建立關(guān)系。
db.user.insert({"id": ObjectId("52ffc33cd85242f436000003"),"name": "admin3","phone": "86-15212343333","birthday": "1993-10-20","email": "admin3@gmail.com","address_ids": [ ObjectId("52ffc4a5d85242602e000000"), ObjectId("52ffc4a5d85242602e000001")]})db.address.insert({"id": ObjectId("52ffc4a5d85242602e000000"),"province": "四川省","city": "成都市","area": "錦江區(qū)","street": "紅星路三段99號(hào)"})db.address.insert({"_id": ObjectId("52ffc4a5d85242602e000001"),"province": "廣東省","city": "深圳市","area": "羅湖區(qū)","street": "建設(shè)路1003號(hào)"})
以上示例中呕诉,用戶文檔的 address_ids 字段包含用戶地址的對(duì)象 id 數(shù)組缘厢。此時(shí)可以讀取這些用戶地址的對(duì)象 id 來獲取用戶地址信息。這種方式需要兩次查詢甩挫,第一次查詢用戶地址的對(duì)象 id 數(shù)組贴硫,第二次通過查詢到的 id 數(shù)組獲取用戶地址信息。如下:
var user = db.user.findOne({"name" : "admin3"}, {"address_ids" : 1})db.address.find({"_id" : {$in : user["address_ids"]}}).pretty()
注意:var user = db.user.findOne({"name" : "admin3"}, {"address_ids" : 1})中的 findOne() 不能寫成 find()伊者,因?yàn)?find() 返回的是一個(gè)數(shù)組夜畴,而 findOne() 返回的是一個(gè)對(duì)象拖刃。如果這句寫成 find(),即:
var user = db.user.find({"name" : "admin3"}, {"address_ids" : 1})
那么查詢用戶地址的語句應(yīng)改為:
db.address.find({"_id" : {$in : user0}}).pretty()
MongoDB 數(shù)據(jù)庫引用
MongoDB 引用有兩種:手動(dòng)引用(Manual References)贪绘、DBRefs兑牡。考慮這樣的一個(gè)場(chǎng)景:在不同集合中(如:address_home税灌、address_office均函、address_mailing等)存儲(chǔ)不同地址(住址、辦公室地址菱涤、郵件地址等)苞也。在調(diào)用不同地址時(shí),也需要指定集合粘秆,一個(gè)文檔從多個(gè)集合引用文檔如迟,就應(yīng)該使用 DBRefs 了。使用 DBRefs 的形式:{$ref : , $id : , $db : }其中攻走,$ref 表示集合名稱殷勘;$id 表示引用的 id;$db 表示數(shù)據(jù)庫名稱(可選參數(shù))昔搂。下例中用戶數(shù)據(jù)文檔使用了 DBRef 字段 address:
db.user.insert({"_id": ObjectId("52ffc33cd85242f436000004"),"name": "admin4","phone": "86-15212344444","birthday": "1994-10-20","email": "admin4@gmail.com","address": { "$ref": "address_home", "$id": ObjectId("52ffc4a5d85242602e000000"), "$db": "test"}})
address 字段指明引用的地址文檔是 test 數(shù)據(jù)庫下的 address_home 集合玲销,且其 id 為 52ffc4a5d85242602e000000。下面通過指定 $ref 參數(shù)(address_home 集合)來查找集合中指定 id 的用戶地址信息:
var user = db.user.findOne({"name" : "admin4"})var dbRef = user.addressdb[dbRef.$ref].findOne({"_id" : (dbRef.$id)})
上例中的第2摘符、3行也可以合并成一行贤斜,如下:
var user = db.user.findOne({"name" : "admin4"})db[user.address.$ref].findOne({"_id" : (user.address.$id)})
注:有人說 db[dbRef.$ref].findOne({"id" : (dbRef.$id)})在 MongoDB4.0 版本應(yīng)該這樣寫:db[dbRef.$ref].findOne({"id":ObjectId(dbRef.$id)}),我在4.2.0版本上測(cè)試會(huì)報(bào)錯(cuò)逛裤!
MongoDB 覆蓋索引查詢
MongoDB 官方文檔中說覆蓋查詢是指以下的查詢:
所有的查詢字段是索引的一部分瘩绒。
所有的查詢返回字段在同一個(gè)索引中。
由于所有查詢字段是索引的一部分带族,所以 MongoDB 無需在整個(gè)文檔中檢索匹配查詢條件和返回使用相同索引的查詢結(jié)果锁荔。因?yàn)樗饕嬖谟?RAM 中,從索引中獲取數(shù)據(jù)比通過掃描文檔讀取數(shù)據(jù)要快得多炉菲。
使用覆蓋索引查詢為了測(cè)試覆蓋索引查詢堕战,使用以下 user 集合:
db.user.insert({"_id": ObjectId("53402597d852426020000005"),? ? "name": "admin5",? ? "gender": "M",? ? "phone": "86-15212345555",? ? "birthday": "1995-10-20",? ? "email": "admin5@gmail.com"})
然后在 user 集合中創(chuàng)建聯(lián)合索引坤溃,字段為 name 和 gende:
db.user.createIndex({"name" : 1, "gender" : 1}, {"name" : "name_gender", "background" : true})
現(xiàn)在拍霜,該索引會(huì)覆蓋以下查詢:
db.user.find({"name" : "admin5"}, {"gender" : 1, "_id" : 0})
也就是說:對(duì)于上述查詢,MongoDB 不會(huì)去數(shù)據(jù)庫文件中查找薪介,而是從索引中提取數(shù)據(jù)祠饺,這是非常快速的數(shù)據(jù)查詢汁政。由于新建的索引中不包括 _id 字段道偷,而 _id 在查詢時(shí)默認(rèn)會(huì)返回缀旁,所以可以在查詢結(jié)果集中排除它。
下例中沒有排除 _id 字段勺鸦,查詢就不會(huì)被索引覆蓋:
db.user.find({"name" : "admin5"}, {"gender" : 1})
以下查詢將不能使用覆蓋索引
所有索引字段是一個(gè)數(shù)組并巍。
所有索引字段是一個(gè)子文檔。
MongoDB 查詢分析
MongoDB 查詢分析可以確保我們所建立的索引是否有效换途,是查詢語句性能分析的重要工具懊渡。常用的查詢分析函數(shù)有:explain() 和 hint()。
使用 explain()explain() 操作提供了查詢信息军拟、使用索引及查詢統(tǒng)計(jì)等剃执,有利于對(duì)索引的優(yōu)化。MongoDB 3.0+ 的 explain() 有三種模式:queryPlanner懈息、executionStats肾档、allPlansExecution。現(xiàn)實(shí)開發(fā)中最常用的是 executionStats 模式辫继。
通過設(shè)置 explain() 不同參數(shù)可以查看更詳細(xì)的查詢計(jì)劃:
queryPlanner:默認(rèn)參數(shù)怒见。
executionStats:會(huì)返回最佳執(zhí)行計(jì)劃的一些統(tǒng)計(jì)信息。
allPlansExecution:用來獲取所有執(zhí)行計(jì)劃骇两。
在 user 集合中創(chuàng)建 name 和 gender 的索引:
db.user.createIndex({"name" : 1, "gender" : 1}, {"name" : "name_gender", "background" : true})
1速种、在查詢語句中使用 explain("queryPlanner")
db.user.find({"name" : "admin5"}, {"gender" : 1}).explain()
等價(jià)于:
db.user.find({"name" : "admin5"}, {"gender" : 1}).explain("queryPlanner")
以上 explain() 查詢返回如下結(jié)果:
{
? ? "queryPlanner": {
? ? ? ? "plannerVersion":1,// 查詢計(jì)劃版本
? ? ? ? "namespace":"test.user",// 要查詢的集合(返回值是該查詢所涉及的集合)
? ? ? ? "indexFilterSet":false,// 是否使用索引(針對(duì)該查詢是否有 indexfilter)
? ? ? ? "parsedQuery": {// 查詢條件
? ? ? ? ? ? "name": {"$eq":"admin5"}
? ? ?? },
"queryHash":"438FDCFD",
"planCacheKey":"849EB436",
"winningPlan": {// 最佳執(zhí)行計(jì)劃
? ? "stage":"PROJECTION_SIMPLE",// 最優(yōu)執(zhí)行計(jì)劃的 stage(查詢方式),常見的有:COLLSCAN(全表掃描)低千、IXSCAN(索引掃描)配阵、FETCH(根據(jù)索引去檢索文檔)、SHARD_MERGE-(合并分片結(jié)果)示血、IDHACK(針對(duì) _id 進(jìn)行查詢)
? ? "transformBy": {"gender":1},
? ? "inputStage": {
? ? ? ? "stage":"FETCH",
? ? ? ? "inputStage": {// 用來描述子 stage棋傍,并且為其父 stage 提供文檔和索引關(guān)鍵字
? ? ? ? ? ? "stage":"IXSCAN",// 此處是 IXSCAN,表示進(jìn)行的是索引掃描
? ? ? ? ? ? "keyPattern": {// 所掃描的 index 內(nèi)容
? ? ? ? ? ? ? ? "name":1,
? ? ? ? ? ? ? ? "gender":1
? ? ? ? ? ? },
? ? ? ? ? ? "indexName":"name_gender",// winningPlan 所選用的索引的名稱
? ? ? ? ? ? "isMultiKey":false,// 是否是 Multikey难审。如果索引建立在 array 上瘫拣,此處將是 true
? ? ? ? ? ? "multiKeyPaths": {
? ? ? ? ? ? ? ? "name": [ ],
? ? ? ? ? ? ? ? "gender": [ ]
? ? ? ? ? ? },
? ? ? ? ? ? "isUnique":false,
? ? ? ? ? ? "isSparse":false,
? ? ? ? ? ? "isPartial":false,
? ? ? ? ? ? "indexVersion":2,
? ? ? ? ? ? "direction":"forward",// 此查詢的查詢順序
? ? ? ? ? ? "indexBounds": {// 所掃描的索引范圍,如果沒有指定則范圍就是[MaxKey, MinKey]告喊,這主要是直接定位到 MongoDB 的 chunck 中去查找數(shù)據(jù)麸拄,加快數(shù)據(jù)讀取
? ? ? ? ? ? ? ? "name": ["[\"admin5\", \"admin5\"]"],
? ? ? ? ? ? ? ? "gender": ["[MinKey, MaxKey]"]
? ? ? ? ? ? }
? ? ? ? }
? ? }
? ? ?? },
? ? "rejectedPlans": [ ]// 拒絕的執(zhí)行計(jì)劃(其他執(zhí)行計(jì)劃,非最優(yōu)而被查詢優(yōu)化器 reject 的)的詳細(xì)返回黔姜,其具體信息與 winningPlan 相同
? ? },
? ? "serverInfo": {// MongoDB 服務(wù)器信息
? ? ? ? "host":"LAPTOP-TAR7TI27",
? ? ? ? "port":27017,
? ? ? ? "version":"4.2.0",
? ? ? ? "gitVersion":"a4b751dcf51dd249c5865812b390cfd1c0129c30"
? ? },
? ? "ok":1
}
返回結(jié)果包含兩大塊信息:queryPlanner(即查詢計(jì)劃) 和 serverInfo(即 MongoDB 服務(wù)器的一些信息)拢切。
2、在查詢語句中使用 explain("executionStats")executionStats 會(huì)返回最佳執(zhí)行計(jì)劃的一些統(tǒng)計(jì)信息秆吵,即:
db.user.find({"name" : "admin5"}, {"gender" : 1}).explain("executionStats")
以上 explain() 查詢返回如下結(jié)果:
{
? ? "queryPlanner": {
? ? ? ? "plannerVersion":1,
? ? ? ? "namespace":"test.user",
? ? ? ? "indexFilterSet":false,
? ? ? ? "parsedQuery": {"name": {"$eq":"admin5"}},
? ? ? ? "queryHash":"438FDCFD",
? ? ? ? "planCacheKey":"849EB436",
? ? ? ? "winningPlan": {
? ? ? ? ? ? "stage":"PROJECTION_SIMPLE",
? ? ? ? ? ? "transformBy": {"gender":1},
? ? ? ? ? ? "inputStage": {
? ? ? ? ? ? ? ? "stage":"FETCH",
? ? ? ? ? ? ? ? "inputStage": {
? ? ? ? ? ? ? ? ? ? "stage":"IXSCAN",
? ? ? ? ? ? ? ? ? ? "keyPattern": {
? ? ? ? ? ? ? ? ? ? ? ? "name":1,
? ? ? ? ? ? ? ? ? ? ? ? "gender":1
? ? ? ? ? ? ? ? ? ? },
? ? ? ? ? ? ? ? ? ? "indexName":"name_gender",
? ? ? ? ? ? ? ? ? ? "isMultiKey":false,
? ? ? ? ? ? ? ? ? ? "multiKeyPaths": {
? ? ? ? ? ? ? ? ? ? ? ? "name": [ ],
? ? ? ? ? ? ? ? ? ? ? ? "gender": [ ]
? ? ? ? ? ? ? ? ? ? },
? ? ? ? ? ? ? ? ? ? "isUnique":false,
? ? ? ? ? ? ? ? ? ? "isSparse":false,
? ? ? ? ? ? ? ? ? ? "isPartial":false,
? ? ? ? ? ? ? ? ? ? "indexVersion":2,
? ? ? ? ? ? ? ? ? ? "direction":"forward",
? ? ? ? ? ? ? ? ? ? "indexBounds": {
? ? ? ? ? ? ? ? ? ? ? ? "name": ["[\"admin5\", \"admin5\"]"],
? ? ? ? ? ? ? ? ? ? ? ? "gender": ["[MinKey, MaxKey]"]
? ? ? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? }
? ? ? ? ? ? }
? ? ? ? },
? ? ? ? "rejectedPlans": [ ]
? ? },
? ? "executionStats": {
? ? ? ? "executionSuccess":true,// 是否執(zhí)行成功
? ? ? ? "nReturned":1,// 返回的結(jié)果數(shù)
? ? ? ? "executionTimeMillis":0,// 執(zhí)行耗時(shí)
? ? ? ? "totalKeysExamined":1,// 索引掃描次數(shù)
? ? ? ? "totalDocsExamined":1,// 文檔掃描次數(shù)
? ? ? ? "executionStages": {// 這個(gè)分類下描述執(zhí)行的狀態(tài)
? ? ? ? ? ? "stage":"PROJECTION_SIMPLE",// 掃描方式淮椰,具體可選值與上文的相同
? ? ? ? ? ? "nReturned":1,// 查詢結(jié)果數(shù)量
? ? ? ? ? ? "executionTimeMillisEstimate":0,// 預(yù)估耗時(shí)
? ? ? ? ? ? "works":2,// 工作單元數(shù),一個(gè)查詢會(huì)分解成小的工作單元
? ? ? ? ? ? "advanced":1,// 優(yōu)先返回的結(jié)果數(shù)
? ? ? ? ? ? "needTime":0,
? ? ? ? ? ? "needYield":0,
? ? ? ? ? ? "saveState":0,
? ? ? ? ? ? "restoreState":0,
? ? ? ? ? ? "isEOF":1,
? ? ? ? ? ? "transformBy": {"gender":1},
? ? ? ? ? ? "inputStage": {
? ? ? ? ? ? ? ? "stage":"FETCH",
? ? ? ? ? ? ? ? "nReturned":1,
? ? ? ? ? ? ? ? "executionTimeMillisEstimate":0,
? ? ? ? ? ? ? ? "works":2,
? ? ? ? ? ? ? ? "advanced":1,
? ? ? ? ? ? ? ? "needTime":0,
? ? ? ? ? ? ? ? "needYield":0,
? ? ? ? ? ? ? ? "saveState":0,
? ? ? ? ? ? ? ? "restoreState":0,
? ? ? ? ? ? ? ? "isEOF":1,
? ? ? ? ? ? ? ? "docsExamined":1,// 文檔檢查數(shù)目,與totalDocsExamined一致主穗。檢查了總共的個(gè)documents泻拦,而從返回上面的nReturne數(shù)量
? ? ? ? ? ? ? ? "alreadyHasObj":0,
? ? ? ? ? ? ? ? "inputStage": {
? ? ? ? ? ? ? ? ? ? "stage":"IXSCAN",
? ? ? ? ? ? ? ? ? ? "nReturned":1,
? ? ? ? ? ? ? ? ? ? "executionTimeMillisEstimate":0,
? ? ? ? ? ? ? ? ? ? "works":2,
? ? ? ? ? ? ? ? ? ? "advanced":1,
? ? ? ? ? ? ? ? ? ? "needTime":0,
? ? ? ? ? ? ? ? ? ? "needYield":0,
? ? ? ? ? ? ? ? ? ? "saveState":0,
? ? ? ? ? ? ? ? ? ? "restoreState":0,
? ? ? ? ? ? ? ? ? ? "isEOF":1,
? ? ? ? ? ? ? ? ? ? "keyPattern": {
? ? ? ? ? ? ? ? ? ? ? ? "name":1,
? ? ? ? ? ? ? ? ? ? ? ? "gender":1
? ? ? ? ? ? ? ? ? ? },
? ? ? ? ? ? ? ? ? ? "indexName":"name_gender",
? ? ? ? ? ? ? ? ? ? "isMultiKey":false,
? ? ? ? ? ? ? ? ? ? "multiKeyPaths": {
? ? ? ? ? ? ? ? ? ? ? ? "name": [ ],
? ? ? ? ? ? ? ? ? ? ? ? "gender": [ ]
? ? ? ? ? ? ? ? ? ? },
? ? ? ? ? ? ? ? ? ? "isUnique":false,
? ? ? ? ? ? ? ? ? ? "isSparse":false,
? ? ? ? ? ? ? ? ? ? "isPartial":false,
? ? ? ? ? ? ? ? ? ? "indexVersion":2,
? ? ? ? ? ? ? ? ? ? "direction":"forward",
? ? ? ? ? ? ? ? ? ? "indexBounds": {
? ? ? ? ? ? ? ? ? ? ? ? "name": ["[\"admin5\", \"admin5\"]"],
? ? ? ? ? ? ? ? ? ? ? ? "gender": ["[MinKey, MaxKey]"]
? ? ? ? ? ? ? ? ? ? },
? ? ? ? ? ? ? ? ? ? "keysExamined":1,
? ? ? ? ? ? ? ? ? ? "seeks":1,
? ? ? ? ? ? ? ? ? ? "dupsTested":0,
? ? ? ? ? ? ? ? ? ? "dupsDropped":0
? ? ? ? ? ? ? ? }
? ? ? ? ? ? }
? ? ? ? }
? ? },
? ? "serverInfo": {
? ? ? ? "host":"LAPTOP-TAR7TI27",
? ? ? ? "port":27017,
? ? ? ? "version":"4.2.0",
? ? ? ? "gitVersion":"a4b751dcf51dd249c5865812b390cfd1c0129c30"
? ? },
? ? "ok":1
}
注:nReturned-該條查詢返回的條目、totalKeysExamined-索引掃描條目忽媒、totalDocsExamined-文檔掃描條目争拐。這些都直觀地影響到 executionTimeMillis,因此掃描的越少速度越快晦雨。對(duì)于一個(gè)查詢陆错,理想狀態(tài)是:nReturned=totalKeysExamined=totalDocsExamined
stage 又影響到 totalKeysExamined 和 totalDocsExamined,其類型列舉如下:
COLLSCAN:全表掃描
IXSCAN:索引掃描
FETCH:根據(jù)索引去檢索指定document
SHARD_MERGE:將各個(gè)分片返回?cái)?shù)據(jù)進(jìn)行merge
SORT:表明在內(nèi)存中進(jìn)行了排序
LIMIT:使用limit限制返回?cái)?shù)
SKIP:使用skip進(jìn)行跳過
IDHACK:針對(duì)_id進(jìn)行查詢
SHARDING_FILTER:通過mongos對(duì)分片數(shù)據(jù)進(jìn)行查詢
COUNT:利用db.coll.explain().count()之類進(jìn)行count運(yùn)算
COUNTSCAN:count不使用Index進(jìn)行count時(shí)的stage返回
COUNT_SCAN:count使用了Index進(jìn)行count時(shí)的stage返回
SUBPLA:未使用到索引的$or查詢的stage返回
TEXT:使用全文索引進(jìn)行查詢時(shí)候的stage返回
PROJECTION:限定返回字段時(shí)候stage的返回
對(duì)于普通查詢金赦,希望的 stage 組合(查詢的時(shí)候盡可能用上索引):Fetch + IDHACK音瓷、Fetch + ixscan、Limit + (Fetch + ixscan)夹抗、PROJECTION + ixscan绳慎、SHARDING_FITER + ixscan、COUNT_SCAN漠烧、SORT_KEY_GENERATOR
不希望看到的 stage:COLLSCAN(全表掃描)杏愤、SORT(使用sort但是無index)、不合理的SKIP已脓、SUBPLA(未用到index的$or)珊楼、COUNTSCAN(不使用index進(jìn)行count)
3、在查詢語句中使用 explain("allPlansExecution")allPlansExecution:用來獲取所有執(zhí)行計(jì)劃度液,即:
db.user.find({"name" : "admin5"}, {"gender" : 1}).explain("allPlansExecution")
使用 hint()雖然 MongoDB 查詢優(yōu)化器一般工作的很不錯(cuò)厕宗,但是也可以使用 hint 來強(qiáng)制 MongoDB 使用一個(gè)指定的索引,這種方法某些情形下會(huì)提升性能堕担。
如下查詢指定了使用 name 和 gender 索引字段來查詢:
db.user.find({"name" : "admin5"}, {"name" : 1, "_id" : 0}).hint({"name" : 1, "gender" : 1})
可以使用 explain() 函數(shù)來分析以上查詢:
db.user.find({"name" : "admin5"}, {"name" : 1, "_id" : 0}).hint({"name" : 1, "gender" : 1}).explain()
MongoDB 原子操作
MongoDB 不支持事務(wù)已慢,因此在實(shí)際項(xiàng)目中應(yīng)用時(shí)要注意這點(diǎn)。無論什么設(shè)計(jì)霹购,都不要要求 MongoDB 保證數(shù)據(jù)的完整性佑惠。但 MongoDB 提供了許多原子操作(如文檔的保存、修改齐疙、刪除等)膜楷。所謂的原子操作,就是這個(gè)文檔要么完全保存到 MongoDB贞奋,要么完全沒有保存到 MongoDB赌厅,不會(huì)出現(xiàn)查詢到的文檔沒有保存完整的情況。
原子操作數(shù)據(jù)模型考慮下面的例子忆矛,圖書館的書籍及結(jié)賬信息察蹲。該實(shí)例說明了在一個(gè)相同的文檔中如何確保嵌入字段關(guān)聯(lián)原子操作(update:更新)的字段是同步的。book = { "_id": 123456789, "title": "MongoDB: The Definitive Guide", "author": ["Kristina Chodorow", "Mike Dirolf"], "published_date": ISODate("2010-09-24"), "pages": 216, "language": "English", "publisher_id": "oreilly", "available": 3, "checkout": [{"by": "joe", "date": ISODate("2012-10-15")}]}
接下來可以使用 db.book.findAndModify() 方法來判斷書籍是否可結(jié)算并更新新的結(jié)算信息催训。在同一個(gè)文檔中嵌入的 available 和 checkout 字段來確保這些字段是同步更新的:
db.book.findAndModify({query: { "_id": 123456789, "available": {$gt: 0}},update: { $inc: {"available": -1}, $push: {"checkout": {"by": "abc", "date": new Date()}}}})
原子操作常用命令
$set:用來指定一個(gè)鍵并更新鍵值洽议,若鍵不存在并創(chuàng)建。{$set : {field : value}}
$unset:用來刪除一個(gè)鍵漫拭。{$unset : {field : 1}}
$inc:可以對(duì)文檔的某個(gè)值為數(shù)字型(只能為滿足要求的數(shù)字)的鍵進(jìn)行增減的操作亚兄。{$inc : {field : value}}
$push:把 value 追加到 field 里面去,field 必須是數(shù)組類型采驻,如果 field 不存在审胚,則會(huì)新增一個(gè)數(shù)組類型加進(jìn)去。{$push : {field : value}}
$pushAll:同 $push礼旅,只是一次可以追加多個(gè)值到一個(gè)數(shù)組字段內(nèi)膳叨。{$pushAll : {field : value_array}}
$pull:從數(shù)組 field 內(nèi)刪除一個(gè)等于 value 值。{$pull : {field : value}}
$addToSet:增加一個(gè)值到數(shù)組內(nèi)痘系,而且只有當(dāng)這個(gè)值不在數(shù)組內(nèi)才增加菲嘴。{$addToSet : {field : value}}
$pop:刪除數(shù)組的第一個(gè)或最后一個(gè)元素{$pop : {field : 1}}
$rename:修改字段名稱{$rename : {old_field_name : new_field_name}}
$bit:位操作,integer 類型{$bit : {field : {and : 5}}}
偏移操作符
db.mycol.find(){"_id" : ObjectId("4b97e62bf1d8c7152c9ccb74"),"title" : "ABC","comments" : [ {"by" : "joe", "votes" : 3}, {"by" : "jane", "votes" : 7}] }
db.mycol.update({'comments.by' : 'joe'}, {$inc : {'comments.$.votes' : 1}}, false, true)
db.mycol.find(){"_id" : ObjectId("4b97e62bf1d8c7152c9ccb74"),"title" : "ABC","comments" : [ {"by" : "joe", "votes" : 4}, {"by" : "jane", "votes" : 7}]}
MongoDB 高級(jí)索引
db.user.insert({? "address": {? ? ? "city": "Los Angeles",? ? ? "state": "California",? ? ? "pincode": "123"? },? "tags": ["music", "cricket", "blogs"],? "name": "Tom Benzamin"})
以上文檔包含了 address 子文檔和 tags 數(shù)組汰翠。
索引數(shù)組字段假設(shè)需要基于標(biāo)簽來檢索用戶龄坪,則需對(duì)集合中的數(shù)組 tags 建立索引。在數(shù)組中創(chuàng)建索引時(shí)需要對(duì)數(shù)組中的每個(gè)字段依次建立索引复唤。因此在為數(shù)組 tags 創(chuàng)建索引時(shí)健田,會(huì)為 music、cricket佛纫、blogs 三個(gè)值建立單獨(dú)的索引妓局。使用以下命令創(chuàng)建數(shù)組索引:
db.user.createIndex({"tags" : 1})
創(chuàng)建索引后就可以這樣檢索集合的 tags 字段了:
db.user.find({"tags" : "cricket"})
為了驗(yàn)證是否使用了索引,可以使用 explain() 方法:
db.user.find({"tags" : "cricket"}).explain()
索引子文檔字段假設(shè)需要通過 city呈宇、state跟磨、pincode 字段來檢索文檔,由于這些字段是子文檔的字段攒盈,因此需要對(duì)子文檔建立索引抵拘。使用以下命令為子文檔的三個(gè)字段創(chuàng)建索引:
db.user.createIndex({"address.city" : 1, "address.state" : 1, "address.pincode" : 1})
創(chuàng)建索引后就可以使用子文檔的字段來檢索數(shù)據(jù):
db.user.find({"address.city" : "Los Angeles"})
查詢表達(dá)不一定遵循指定的索引順序,MongoDB 會(huì)自動(dòng)優(yōu)化型豁。所以上面創(chuàng)建的索引將支持以下查詢:
db.user.find({"address.state" : "California", "address.city" : "Los Angeles"})
同樣也支持以下查詢:
db.user.find({"address.city" : "Los Angeles", "address.state" : "California", "address.pincode" : "123"})
MongoDB 索引限制
1僵蛛、額外開銷每個(gè)索引都要占據(jù)一定的存儲(chǔ)空間,在進(jìn)行插入迎变、更新充尉、刪除等操作時(shí)也需要對(duì)索引進(jìn)行操作。如果很少對(duì)集合進(jìn)行讀取操作衣形,建議不使用索引驼侠。
2姿鸿、內(nèi)存(RAM)使用由于索引是存儲(chǔ)在內(nèi)存(RAM)中的,所以需要確保該索引的大小不超過內(nèi)存的限制倒源。如果索引的大小大于內(nèi)存的限制查吊,MongoDB 就會(huì)刪除一些索引仙辟,這將導(dǎo)致性能下降。
3、查詢限制索引不能被以下查詢使用:
正則表達(dá)式及非操作符(如:$nin坤邪、$not 等)
算術(shù)運(yùn)算符(如:$mod 等)
$where 子句
所以茸苇,檢測(cè) MongoDB 查詢語句是否使用索引是一個(gè)好習(xí)慣唆垃,可以用 explain() 來查看接校。
4、索引鍵限制從 2.6 版本開始糖耸,如果現(xiàn)有的索引字段的值超過索引鍵的限制秘遏,MongoDB 中就不會(huì)創(chuàng)建索引。
5嘉竟、插入文檔超過索引鍵限制如果文檔的索引字段值超過了索引鍵的限制垄提,MongoDB 不會(huì)將任何文檔轉(zhuǎn)換成索引的集合。與 mongorestore 和 mongoimport 工具類似周拐。
6铡俐、最大范圍
集合中索引不能超過 64 個(gè)
索引名的長度不能超過 128 個(gè)字符
一個(gè)復(fù)合索引最多可以有 31 個(gè)字段
MongoDB ObjectId
MongoDB 的對(duì)象 Id(ObjectId) 是一個(gè)12字節(jié) BSON 類型數(shù)據(jù),具體格式為:
前4個(gè)字節(jié)表示時(shí)間戳
接下來的3個(gè)字節(jié)是機(jī)器標(biāo)識(shí)碼
緊接的兩個(gè)字節(jié)由進(jìn)程 id 組成(PID)
最后三個(gè)字節(jié)是隨機(jī)數(shù)
MongoDB 中存儲(chǔ)的文檔必須有一個(gè) _id 鍵妥粟,這個(gè)鍵的值可以是任何類型的审丘,默認(rèn)是個(gè) ObjectId 對(duì)象。在一個(gè)集合里面勾给,每個(gè)文檔都有唯一的 _id 值滩报,用以確保集合里面的每個(gè)文檔都能被唯一標(biāo)識(shí)。MongoDB 采用 ObjectId 而不是其他比較常規(guī)的做法(如:自增主鍵)主要是因?yàn)樵诙鄠€(gè)服務(wù)器上同步自動(dòng)增加主鍵值既費(fèi)力還費(fèi)時(shí)播急。
創(chuàng)建新的 ObjectId 的方式
ObjectId()
也可以使用已經(jīng)生成的 id 來取代 MongoDB 自動(dòng)生成的 ObjectId脓钾,如:
ObjectId("5ee783e424a0205812217f25")
創(chuàng)建文檔的時(shí)間戳由于 ObjectId 中存儲(chǔ)了 4 個(gè)字節(jié)的時(shí)間戳,因此不需要單獨(dú)為文檔保存時(shí)間戳字段桩警,需要時(shí)可以通過 getTimestamp() 函數(shù)來獲取文檔的創(chuàng)建時(shí)間:
ObjectId("5ee783e424a0205812217f25").getTimestamp()
ObjectId 轉(zhuǎn)換為字符串在某些情況下可训,可能需要將 ObjectId 轉(zhuǎn)換為字符串格式,方式為:
ObjectId().str 或 new ObjectId().str
MongoDB Map-Reduce
Map-Reduce 是一種計(jì)算模型捶枢,簡單的說就是將大批量的工作(數(shù)據(jù))分解(MAP)執(zhí)行握截,然后再將結(jié)果合并成最終結(jié)果(REDUCE)。MongoDB 中提供的 Map-Reduce 非常靈活烂叔,對(duì)大規(guī)模數(shù)據(jù)分析相當(dāng)實(shí)用谨胞。
Map-Reduce 命令MongoDB 中 Map-Reduce 的基本語法:
db.collectionName.mapReduce(function() {// map 函數(shù) emit(key, value);},function(key, values) {// reduce 函數(shù) return reduceFunction},{ out: collection, query: document, sort: document, limit: number})
使用 Map-Reduce 就必須要實(shí)現(xiàn)兩個(gè)函數(shù):Map 函數(shù)和 Reduce 函數(shù)。Map 函數(shù)調(diào)用 emit(key, value) 遍歷集合中所有記錄, 將 key 與 value 傳遞給 Reduce 函數(shù)進(jìn)行處理蒜鸡。Map 函數(shù)必須調(diào)用 emit(key, value) 返回鍵值對(duì)胯努。參數(shù)說明:
map:映射函數(shù)(生成鍵值對(duì)序列牢裳,作為 reduce 函數(shù)參數(shù))。
reduce:統(tǒng)計(jì)函數(shù)叶沛,reduce 函數(shù)的任務(wù)就是將 key-values 變成 key-value蒲讯,也就是把 values 數(shù)組變成單個(gè) value。
out:統(tǒng)計(jì)結(jié)果存放集合(不指定則使用臨時(shí)集合,在客戶端斷開后自動(dòng)刪除)恬汁。
query:一個(gè)篩選條件,只有滿足條件的文檔才會(huì)調(diào)用 map 函數(shù)(注:query辜伟、limit氓侧、sort 可以隨意組合)。
sort:和 limit 結(jié)合的 sort 排序參數(shù)(在發(fā)往 map 函數(shù)前就會(huì)給文檔排序)导狡,可以優(yōu)化分組機(jī)制约巷。
limit:發(fā)往 map 函數(shù)的文檔數(shù)量的上限(若沒有 limit,單獨(dú)使用 sort 的用處不大)旱捧。
以下實(shí)例在集合 orders 中查找 status:"A" 的數(shù)據(jù)独郎,然后根據(jù) cust_id 來分組,最后計(jì)算 amount 的總和:
db.orders.insertMany([? ? {"cust_id" : "A123", "amount" : 500, "status" : "A"},? ? {"cust_id" : "A123", "amount" : 250, "status" : "A"},? ? {"cust_id" : "A212", "amount" : 200, "status" : "A"},? ? {"cust_id" : "A123", "amount" : 300, "status" : "D"}])
db.orders.mapReduce(function() { emit(this.cust_id, this.amount);},function(key, values) { return Array.sum(values);},{ query : {"status" : "A"}, out : "total_amount"}).find()
使用 Map-Reduce添加數(shù)據(jù):
db.posts.insertMany([{ "post_text": "菜鳥教程枚赡,最全的技術(shù)文檔氓癌。", "user_name": "mark", "status": "active"},{ "post_text": "菜鳥教程,最全的技術(shù)文檔贫橙。", "user_name": "mark", "status": "active"},{ "post_text": "菜鳥教程贪婉,最全的技術(shù)文檔。", "user_name": "mark", "status": "active"},{ "post_text": "菜鳥教程卢肃,最全的技術(shù)文檔疲迂。", "user_name": "mark", "status": "active"},{ "post_text": "菜鳥教程,最全的技術(shù)文檔莫湘。", "user_name": "mark", "status": "disabled"},{ "post_text": "菜鳥教程尤蒿,最全的技術(shù)文檔。", "user_name": "runoob", "status": "disabled"},{ "post_text": "菜鳥教程幅垮,最全的技術(shù)文檔腰池。", "user_name": "runoob", "status": "disabled"},{ "post_text": "菜鳥教程,最全的技術(shù)文檔忙芒。", "user_name": "runoob", "status": "active"}])
接下來在 posts 集合中使用 mapReduce 函數(shù)來選取已發(fā)布的文章(status:"active")巩螃,并通過 user_name 分組,計(jì)算每個(gè)用戶的文章數(shù):
db.posts.mapReduce(function() { emit(this.user_name, 1);}, function(key, values) { return Array.sum(values);},{ query : {"status" : "active"}, out : "post_total"})
以上 mapReduce 的輸出結(jié)果如下:{ "result" : "post_total", "timeMillis" : 32, "counts" : { "input" : 5, "emit" : 5, "reduce" : 1, "output" : 2 }, "ok" : 1}結(jié)果表明:共有5個(gè)符合查詢條件(status:"active")的文檔匕争,在 map 函數(shù)中生成了5個(gè)鍵值對(duì)文檔避乏,最后使用 reduce 函數(shù)將相同的鍵值分為2組。具體參數(shù)說明:
result:儲(chǔ)存結(jié)果的 collection 的名字甘桑。這是個(gè)臨時(shí)集合拍皮,MapReduce 的連接關(guān)閉后就被自動(dòng)刪除了歹叮。
timeMillis:執(zhí)行花費(fèi)的時(shí)間,毫秒為單位铆帽。
input:滿足條件被發(fā)送到 map 函數(shù)的文檔個(gè)數(shù)咆耿。
emit:在 map 函數(shù)中調(diào)用 emit 的次數(shù),也就是所有集合中的數(shù)據(jù)總量爹橱。
ouput:結(jié)果集合中的文檔個(gè)數(shù)(count 對(duì)調(diào)試非常有幫助)萨螺。
ok:是否成功,成功為 1
err:如果失敗愧驱,這里可以有失敗原因慰技,不過從經(jīng)驗(yàn)上來看,原因比較模糊组砚,作用不大吻商。
注:臨時(shí)集合參數(shù)還可以這樣寫 out: {"inline" : 1},如下:
db.posts.mapReduce(function() { emit(this.user_name, 1);}, function(key, values) { return Array.sum(values);},{ query : {"status" : "active"}, out : {"inline" : 1}}).find()
設(shè)置了 {"inline" : 1} 將不會(huì)創(chuàng)建臨時(shí)集合糟红,整個(gè) Map-Reduce 的操作就在內(nèi)存中進(jìn)行艾帐,但這個(gè)選項(xiàng)只有在結(jié)果集中單個(gè)文檔大小在 16MB 內(nèi)時(shí)才有效。
使用 find 操作符來查看 mapReduce 的查詢結(jié)果:
db.posts.mapReduce(function() { emit(this.user_name, 1);}, function(key, values) { return Array.sum(values);},{ query : {"status" : "active"}, out : "post_total"}).find()
以上查詢顯示如下結(jié)果:{"id" : "mark", "value" : 4}{"id" : "runoob", "value" : 1}用類似的方式盆偿,MapReduce 可以被用來構(gòu)建大型復(fù)雜的聚合查詢柒爸。Map 函數(shù)和 Reduce 函數(shù)可以使用 JavaScript 來實(shí)現(xiàn),使得 MapReduce 的使用非常靈活和強(qiáng)大事扭。
MongoDB 全文檢索
全文檢索對(duì)每一個(gè)詞建立一個(gè)索引揍鸟,指明該詞在文檔中出現(xiàn)的次數(shù)和位置,當(dāng)用戶查詢時(shí)句旱,檢索程序就根據(jù)事先建立的索引進(jìn)行查找阳藻,并將查找的結(jié)果反饋給用戶。這個(gè)過程類似于通過字典中的檢索字表查字的過程谈撒。MongoDB 從 2.4 版本開始支持全文檢索腥泥,目前支持15種語言的全文索引:danish、dutch啃匿、english蛔外、finnish、french溯乒、german夹厌、hungarian、italian裆悄、norwegian矛纹、portuguese、romanian光稼、russian或南、spanish孩等、swedish、turkish
啟用全文檢索MongoDB 在 2.6 版本以后默認(rèn)是開啟全文檢索的采够,如果使用之前的版本肄方,則需要使用以下方式來啟用全文檢索:
db.adminCommand({setParameter : true, textSearchEnabled : true})
或者使用命令:mongod --setParameter textSearchEnabled=true
創(chuàng)建全文索引考慮以下 posts 集合的文檔數(shù)據(jù),包含了文章內(nèi)容(post_text)及標(biāo)簽(tags):
db.posts.insert({"post_text": "enjoy the mongodb articles on Runoob","tags": [ "mongodb", "runoob"]})
可以對(duì) post_text 字段建立全文索引:
db.posts.createIndex({"post_text" : "text"})
使用全文索引上面已經(jīng)對(duì) post_text 建立了全文索引蹬癌,接下來就可以搜索文章中的關(guān)鍵詞 runoob 了:
db.posts.find({$text : {$search : "runoob"}})
如果使用的是舊版本 MongoDB权她,則可以使用以下命令:
db.posts.runCommand("text", {search : "runoob"})
使用全文索引可以提高搜索效率。
刪除全文索引使用 getIndexes() 列出所有的索引名稱:
db.posts.getIndexes()
根據(jù)索引名稱刪除索引逝薪,命令如下:
db.posts.dropIndex("索引名稱")
MongoDB 正則表達(dá)式
正則表達(dá)式是使用單個(gè)字符串來描述隅要、匹配一系列符合某個(gè)句法規(guī)則的字符串。許多程序設(shè)計(jì)語言都支持利用正則表達(dá)式進(jìn)行字符串操作翼闽。MongoDB 使用 $regex 操作符來設(shè)置匹配字符串的正則表達(dá)式拾徙。MongoDB 使用 PCRE(Perl Compatible Regular Expression)作為正則表達(dá)式語言洲炊。不同于全文檢索感局,我們使用正則表達(dá)式不需要做任何配置≡莺猓考慮以下 posts 集合的文檔數(shù)據(jù)询微,包含了文章內(nèi)容(post_text)及標(biāo)簽(tags):
db.posts.insert({"post_text": "enjoy the mongodb articles on Runoob","tags": [ "mongodb", "runoob"]})
使用正則表達(dá)式MongoDB 中使用正則表達(dá)式得語法:
db.collectionName.find({"field" : {$regex : "keyWords"}}) 或 db.collectionName.find({"field" : /keyWords/})
例、使用正則表達(dá)式查找包含指定字符串的文章:
db.posts.find({"post_text" : {$regex : "runoob"}}).pretty()
以上查詢也可以寫為:
db.posts.find({"post_text" : /runoob/}).pretty()
不區(qū)分大小寫的正則表達(dá)式如果檢索時(shí)不需要區(qū)分大小寫狂巢,則需要設(shè)置 $options 為 $i撑毛,命令如下:
db.collectionName.find({"field" : {$regex : "keyWords", $options : "$i"}})
以下命令將查找不區(qū)分大小寫的字符串 runoob:
db.posts.find({"post_text" : {$regex : "runoob", $options : "$i"}}).pretty()
數(shù)組元素使用正則表達(dá)式還可以在數(shù)組字段中使用正則表達(dá)式來查找內(nèi)容。 這在標(biāo)簽的實(shí)現(xiàn)上非常有用唧领,如果需要查找包含以 run 開頭的標(biāo)簽數(shù)據(jù)(ru 或 run 或 runoob)藻雌,命令如下:
db.posts.find({"tags" : {$regex : "run"}}).pretty()
優(yōu)化正則表達(dá)式查詢如果文檔中的字段設(shè)置了索引,那么使用索引比使用正則表達(dá)式匹配查找數(shù)據(jù)的查詢速度更快斩个。如果正則表達(dá)式是前綴表達(dá)式胯杭,所有匹配的數(shù)據(jù)將以指定的前綴字符串為開始(如: 正則表達(dá)式為 ^tut ,則查詢語句將查找以 tut 開頭的字符串)受啥。
使用正則表達(dá)式時(shí)需要注意:正則表達(dá)式中使用變量做个,一定要使用 eval 將組合的字符串進(jìn)行轉(zhuǎn)換,不能直接將字符串拼接后傳入給表達(dá)式滚局。否則沒有報(bào)錯(cuò)信息居暖,只是結(jié)果為空!
格式如下:
var name = eval("/" + "keyWords" + "/i")db.collectionName.find({"field" : name})
等同于:
db.collectionName.find({"field" : {$regex : "keyWords", $Option : "$i"}})
以下是模糊查詢包含 the 關(guān)鍵詞, 且不區(qū)分大小寫:
db.posts.find({"post_text" : eval("/the/i")}).pretty()
或
db.posts.find({"post_text" : eval("/" + "the" + "/i")}).pretty()
$regex 操作符介紹MongoDB 中使用 $regex 操作符來設(shè)置匹配字符串的正則表達(dá)式藤肢,使用 PCRE(Pert Compatible Regular Expression) 作為正則表達(dá)式語言太闺。
MongoDB 中的 $regex 操作符:{<field> : {$regex : /pattern/,$options : '<options>'}}{<field> : {$regex : 'pattern'嘁圈,$options : '<options>'}}{<field> : {$regex : /pattern/<options>}}
MongoDB 中的正則表達(dá)式對(duì)象:{<field> : /pattern/<options>}
$regex 與正則表達(dá)式對(duì)象的區(qū)別:
在 $in 操作符中只能使用正則表達(dá)式對(duì)象跟束,如:{"name" : {$in : [/^joe/i, /^jack/}}
在使用隱式的 $and 操作符中莺奸,只能使用 $regex,如:{"name" : {$regex : /^jo/i, $nin : ['john']}}
當(dāng) option 選項(xiàng)中包含 X 或 S 選項(xiàng)時(shí)冀宴,只能使用 $regex灭贷,如:{"name" : {$regex : /m.*line/, $options:"si"}}
$regex 操作符的使用$regex 操作符中的 option 選項(xiàng)可以改變正則匹配的默認(rèn)行為,它包括 i略贮、m甚疟、x、s 四個(gè)選項(xiàng)逃延,其含義分別如下:
i:忽略大小寫览妖,如 {<field> : {$regex/pattern/i}} 設(shè)置 i 選項(xiàng)后,模式中的字母會(huì)進(jìn)行大小寫不敏感匹配揽祥。
m:多行匹配模式讽膏,如 {<field> : {$regex/pattern/, $options : 'm'} 設(shè)置 m 選項(xiàng)后,會(huì)更改 ^ 和 $ 字符的默認(rèn)行為拄丰,分別使用于行的開頭和結(jié)尾匹配府树,而不是與輸入字符串的開頭和結(jié)尾匹配。
x:忽略非轉(zhuǎn)義的空白字符料按,如 {<field> : {$regex : /pattern/, $options : 'x'} 設(shè)置 x 選項(xiàng)后奄侠,正則表達(dá)式中的非轉(zhuǎn)義的空白字符將被忽略,同時(shí) # 會(huì)被解釋為注釋的開頭注载矿,只能顯式位于 option 選項(xiàng)中垄潮。
s:單行匹配模式,如 {<field> : {$regex : /pattern/, $options : 's'} 設(shè)置 s 選項(xiàng)后闷盔,會(huì)更改 . 字符的默認(rèn)行為弯洗,它會(huì)匹配所有字符,包括換行符(\n)逢勾,只能顯式位于 option 選項(xiàng)中牡整。
使用 $regex 操作符時(shí),需要注意幾個(gè)問題:
i敏沉、m果正、x、s 可以組合使用盟迟。如:db.user.find({"name" : {$regex : /j*k/, $options : "si"}}).pretty()
在設(shè)置索引的字段上進(jìn)行正則匹配可以提高查詢速度秋泳,而且當(dāng)正則表達(dá)式使用的是前綴表達(dá)式時(shí),查詢速度會(huì)進(jìn)一步提高攒菠。如:db.user.find({"name" : {$regex : /^joe/}}).pretty()
MongoDB GridFS
GridFS 用于存儲(chǔ)和恢復(fù)那些超過 16M(BSON 文件限制)的文件(如圖片迫皱、音頻、視頻等)。
GridFS 也是文件存儲(chǔ)的一種方式卓起,但它存儲(chǔ)在 MongoDB 的集合中和敬。
GridFS 可以更好的存儲(chǔ)大于 16M 的文件。
GridFS 會(huì)將大文件對(duì)象分割成多個(gè)小的 chunk(文件片段戏阅,一般為每個(gè) 256k 大小)昼弟,每個(gè) chunk 將作為 MongoDB 的一個(gè)文檔(document)被存儲(chǔ)在 chunks 集合中。
GridFS 用兩個(gè)集合來存儲(chǔ)一個(gè)文件奕筐,分別是:fs.files 與 fs.chunks舱痘。每個(gè)文件的實(shí)際內(nèi)容被存在 chunks(二進(jìn)制數(shù)據(jù))中,和文件有關(guān)的 meta 數(shù)據(jù)(如filename离赫、content_type芭逝、用戶自定義屬性等)將會(huì)被存在 files 集合中。
以下是簡單的 fs.files 集合文檔:{ "filename": "test.txt", "chunkSize": NumberInt(261120), "uploadDate": ISODate("2014-04-13T11:32:33.557Z"), "md5": "7b762939321e146569b07f72c62cca4f", "length": NumberInt(646)}
以下是簡單的 fs.chunks 集合文檔:{ "files_id": ObjectId("534a75d19f54bfec8a2fe44b"), "n": NumberInt(0), "data": "Mongo Binary Data"}
GridFS 添加文件假如使用 GridFS 的 put 命令來存儲(chǔ) mp3 文件渊胸,則需調(diào)用 ${MONGODB_HOME}/bin 下的 mongofiles.exe 工具旬盯。命令如下:
mongofiles.exe -d gridfs put song.mp3
參數(shù) -d gridfs:指定存儲(chǔ)文件的數(shù)據(jù)庫名稱,如果不存則會(huì)自動(dòng)創(chuàng)建翎猛。Song.mp3 是音頻文件名胖翰。使用以下命令來查看數(shù)據(jù)庫中文件的文檔:
db.fs.files.find()
以上命令執(zhí)行后返回以下文檔數(shù)據(jù):{? _id: ObjectId('534a811bf8b4aa4d33fdf94d'),? ? filename: "song.mp3",? ? chunkSize: 261120,? ? uploadDate: new Date(1397391643474), md5: "e4f53379c909f7bed2e9d631e15c1c41",? length: 10401959 }
可以看到 fs.chunks 集合中所有的區(qū)塊,還可以根據(jù)文件的 _id 獲取區(qū)塊(chunk)的數(shù)據(jù)办成,命令如下:
db.fs.chunks.find({files_id : ObjectId('534a811bf8b4aa4d33fdf94d')})
MongoDB 固定集合(Capped Collections)
MongoDB 固定集合(Capped Collections)是性能出色且有著固定大小的集合泡态,對(duì)于大小固定搂漠,可以將其想象成一個(gè)環(huán)形隊(duì)列迂卢,當(dāng)集合空間用完后,再插入的元素就會(huì)覆蓋最初始的頭部的元素桐汤!
創(chuàng)建固定集合通過 createCollection 來創(chuàng)建一個(gè)固定集合而克,且 capped 選項(xiàng)設(shè)置為 true,如下:
db.createCollection("collectionName", {capped : true, size : 10000})
還可以指定文檔個(gè)數(shù)怔毛,加上 max:1000 屬性即可:
db.createCollection("collectionName", {capped : true, size : 10000, max : 1000})
其中员萍,size 是整個(gè)集合空間大小,單位為 KB拣度;max 是集合文檔個(gè)數(shù)上線碎绎,單位是個(gè)。如果空間大小達(dá)到上限抗果,則插入下一個(gè)文檔時(shí)會(huì)覆蓋第一個(gè)文檔筋帖;如果文檔個(gè)數(shù)達(dá)到上限,插入下一個(gè)文檔時(shí)也會(huì)覆蓋第一個(gè)文檔冤馏。
判斷集合是否為固定集合:
db.collectionName.isCapped()
還可以將已存在的集合轉(zhuǎn)換為固定集合日麸,命令如下:
db.runCommand({"convertToCapped" : "collectionName", size : 10000})
固定集合查詢固定集合文檔按照插入順序儲(chǔ)存的,默認(rèn)情況下查詢就是按照插入順序返回的逮光,也可以使用 $natural 調(diào)整返回順序:
db.collectionName.find().sort({$natural : -1}).pretty()
固定集合的功能特點(diǎn)MongoDB 固定集合可以插入及更新代箭,但更新不能超出 collection 的大小墩划,否則更新失敗。不允許刪除嗡综,但可以調(diào)用 drop() 刪除集合中的所有行乙帮。注意 drop 后需要顯式地重建集合。在32位機(jī)器上一個(gè) cappped collection 的最大值約為 482.5M极景,64位上只受系統(tǒng)文件大小的限制蚣旱。
固定集合屬性及用法
屬性1、對(duì)固定集合進(jìn)行插入速度極快2戴陡、按照插入順序的查詢輸出速度極快3塞绿、能夠在插入最新數(shù)據(jù)時(shí),淘汰最早的數(shù)據(jù)
用法1恤批、儲(chǔ)存日志信息2异吻、緩存一些少量的文檔
MongoDB 自動(dòng)增長
MongoDB 沒有像 MYSQL 一樣有自增的功能,MongoDB 中的 _id 是系統(tǒng)自動(dòng)生成的12字節(jié)唯一標(biāo)識(shí)喜庞。但在某些情況下诀浪,我們可能需要實(shí)現(xiàn) ObjectId 自動(dòng)增長功能。由于 MongoDB 沒有實(shí)現(xiàn)這個(gè)功能延都,因此可以通過編程的方式來實(shí)現(xiàn)雷猪,以下將在 counters 集合中實(shí)現(xiàn) _id 字段自增。首先創(chuàng)建 counters 集合晰房,序列字段值可以實(shí)現(xiàn)自增:
db.createCollection("counters")
然后向 counters 集合中插入以下文檔求摇,使用 productid 作為 key:
db.counters.insert({"_id": "productid", "sequence_value": 0})
其中 sequence_value 字段是序列通過自動(dòng)增長后的一個(gè)值。
創(chuàng)建 Javascript 函數(shù)接下來創(chuàng)建函數(shù) getNextSequenceValue() 作為序列名的輸入殊者,指定的序列會(huì)自動(dòng)增長 1 并返回最新序列值与境。本文實(shí)例中序列名為 productid。
function getNextSequenceValue(sequenceName) {var sequenceDocument = db.counters.findAndModify({ query : {_id : sequenceName}, update : {$inc : {sequence_value : 1}}, "new" : true});return sequenceDocument.sequence_value;}
使用 Javascript 函數(shù)最后使用 getNextSequenceValue() 函數(shù)創(chuàng)建一個(gè)新的文檔猖吴, 并設(shè)置文檔 _id 為返回的序列值:
db.products.insert({"id" : getNextSequenceValue("productid"),"product_name" : "Apple iPhone","category" : "mobiles"})db.products.insert({"id" : getNextSequenceValue("productid"),"product_name" : "Samsung S3","category" : "mobiles"})
驗(yàn)證函數(shù)是否有效:
db.products.find().pretty()
本文參考:
1摔刁、https://www.runoob.com/mongodb/mongodb-tutorial.html
2、https://docs.mongodb.com/manual/