1. 介紹缔莲、安裝、使用(簡單寫寫扎唾,不做詳細(xì)介紹)
1.1 介紹
- Mongodb是屬于NoSql的一種數(shù)據(jù)類型召川;
- MongoDB 是一個介于關(guān)系數(shù)據(jù)庫和非關(guān)系數(shù)據(jù)庫之間的產(chǎn)品,是非關(guān)系數(shù)據(jù)庫當(dāng)中功能最豐富胸遇,最像關(guān)系數(shù)據(jù)庫的荧呐;
- 它支持的數(shù)據(jù)結(jié)構(gòu)非常松散,是類似 json 的 bson 格式纸镊,因此可以存儲比較復(fù)雜的數(shù)據(jù)類型坛增;
- Mongo 最大的特點(diǎn)是他支持的查詢語言非常強(qiáng)大,其語法有點(diǎn)類似于面向?qū)ο蟮牟樵冋Z言薄腻,幾乎可以實(shí)現(xiàn)類似關(guān)系數(shù)據(jù)庫單表查詢的絕大部分功能收捣,而且還支持對數(shù)據(jù)建立索引;
- 它的特點(diǎn)是高性能 庵楷、易部署 罢艾、易使用 ,存儲數(shù)據(jù)非常方便 尽纽。
1.2 安裝
MacOS中推薦使用HomeBrew安裝咐蚯,簡單,沒啥理由弄贿。
安裝步驟:https://docs.mongodb.com/manual/tutorial/install-mongodb-on-os-x/
1.3 使用
- 啟動 MongoDb 服務(wù): mongod 開啟數(shù)據(jù)庫服務(wù)
mongod --dbpath <path to data directory> - 輸入 mongo 命令連接數(shù)據(jù)庫
mongo --host 127.0.0.1:27017
具體步驟:https://docs.mongodb.com/manual/tutorial/install-mongodb-on-os-x/
2. Databases春锋、Collections和Document
noSql是無模式的文檔數(shù)據(jù)庫
mongodb | sqlServer | |
---|---|---|
database | => | database |
collection | => | table |
document | => | row |
Note:
row: 每一行都是一樣的字段,不可添加不可減少差凹,即field的個數(shù)在定義table時就已經(jīng)聲明好了
-
document: 每一個document都是獨(dú)立的期奔,也不是在collection定義的時候聲明好的。
3. Bson結(jié)構(gòu)解析以及$type和_id
3.1 Bson支持的數(shù)據(jù)類型
BSON is a binary serialization format used to store documents and make remote procedure calls in MongoDB.
Type | Number | Alias | Notes |
---|---|---|---|
Double | 1 | “double” | |
String | 2 | “string” | |
Object | 3 | “object” | |
Array | 4 | “array” | |
Binary data | 5 | “binData” | |
Undefined | 6 | “undefined” | Deprecated. |
ObjectId | 7 | “objectId” | |
Boolean | 8 | “bool” | |
Date | 9 | “date” | |
Null | 10 | “null” | |
Regular Expression | 11 | “regex” | |
DBPointer | 12 | “dbPointer” | Deprecated. |
JavaScript | 13 | “javascript” | |
Symbol | 14 | “symbol” | Deprecated. |
JavaScript (with scope) | 15 | “javascriptWithScope” | |
32-bit integer | 16 | “int” | |
Timestamp | 17 | “timestamp” | |
64-bit integer | 18 | “l(fā)ong” | |
Decimal128 | 19 | “decimal” | New in version 3.4. |
Min key | -1 | “minKey” | |
Max key | 127 | “maxKey” |
3.2 $type
可以使用$type操作符根據(jù)上表中列出的BSON數(shù)據(jù)類型從文檔中查詢數(shù)據(jù)危尿。
例如:
集合inventory中存儲了一條name值為undefined的數(shù)據(jù)呐萌,通過$type操作符找到這條數(shù)據(jù),具體操作如下圖:
db.inventory.find({name: { $type: 6 }})
若直接通過db.inventory.find({name: undefined})查找會報(bào)錯
3.2 ObjectId
ObjectIds are small, likely unique, fast to generate, and ordered. ObjectId values consist of 12 bytes, where the first four bytes are a timestamp that reflect the ObjectId’s creation. Specifically:
- a 4-byte value representing the seconds since the Unix epoch,
- a 5-byte random value, and
- a 3-byte counter, starting with a random value.
通過上述生成規(guī)則谊娇,是可以保證ObjectId做到全局唯一的肺孤。在Mongodb中,存儲在集合中的每一個文檔都需要一個唯一的[_id]作為主鍵。如果你插入文檔時忽略了[_id]赠堵,Mongodb會自動幫你生成[ObjectId]小渊。
4. mongodb shell、GUI與load()
可以通過終端茫叭、GUI以及外部js文件的形式(load()方法)來執(zhí)行CRUD命令酬屉。
5. sql和Mongodb statement對照關(guān)系
有熟悉關(guān)系型數(shù)據(jù)庫的可以參考https://docs.mongodb.com/manual/faq/fundamentals/#does-mongodb-support-sql看看兩者之間的區(qū)別。
6. mongodb運(yùn)算符
6.1 查詢運(yùn)算符
6.1.1 比較運(yùn)算符
比較不同的BSON類型值杂靶,使用3.2節(jié)中所講的$type
操作符
Name | Description |
---|---|
$eq |
等于 |
$gt |
大于 |
$gte |
大于等于 |
$in |
匹配在數(shù)組中的任意值 |
$lt |
小于 |
$lte |
小于等于 |
$ne |
不等于 |
$nin |
匹配不在數(shù)組中的值 |
6.1.1.1 $eq
- 語法:
{ <field>: { $eq: <value> } }
- 可用于比較
- 簡單類型值
- 復(fù)雜類型值(document梆惯、Array)
- Example
用于比較的集合為inventory,包含的documents如下:
- 匹配簡單類型
命令:db.inventory.find( { qty: { $eq: 20 } } )
結(jié)果: - 匹配復(fù)雜類型(document)
命令:db.inventory.find( { "item.name": { $eq: "ab" } } )
結(jié)果: - 匹配數(shù)組
命令:db.inventory.find( { tags: { $eq: "B" } } )
結(jié)果:
Note:
這條命令查詢所有documents中tags字段中包含"B"的結(jié)果吗垮,也會匹配tags字段值為"B"的結(jié)果垛吗。這條命令相當(dāng)于db.inventory.find( { tags: { $in: ["B"] } } )
。
命令:db.inventory.find( { tags: { $eq: [ "A", "B" ] } } )
結(jié)果:
Note:
這條命令查詢所有documents中tags字段完全匹配[ "A", "B" ]的結(jié)果烁登,或者匹配tags字段的某一項(xiàng)值為[ "A", "B" ]的結(jié)果怯屉。這條命令相當(dāng)于db.inventory.find( { tags: { $in: [[ "A", "B" ]] } } )
。
6.1.1.2 $gt
- 語法:
{field: {$gt: value} }
- Example
命令:db.inventory.find( { qty: { $gt: 20 } } )
結(jié)果:
$gte
饵沧、$lt
锨络、$lte
、$ne
都是類型的狼牺,不累述羡儿。
6.1.1.3 $in
- 語法:
{ field: { $in: [<value1>, <value2>, ... <valueN> ] } }
- Examples
- 匹配普通值
命令:db.inventory.find( { qty: { $in: [ 5, 15 ] } } )
結(jié)果:
Note:
這條命令查詢所有documents中qty字段的值為5或者15的結(jié)果。當(dāng)然也可以使$or
操作符來完成這一操作是钥,這里選擇$in
而不是$or
是因?yàn)檫@是對同一個字段的值的匹配掠归。
- 匹配數(shù)組
命令:db.inventory.find( { tags: { $in: ["A", "B"] } } )
結(jié)果:
Note:
這條命令查詢所有documents中tags字段包含"A"或者 "B"元素的結(jié)果。
- 匹配正則
命令:db.inventory.find( { tags: { $in: [ /^be/, /^st/ ] } } )
結(jié)果:
Note:
這條命令查詢所有documents中tags字段包含以be或者st字母開頭的元素的結(jié)果悄泥。
$nin
與$in
相反虏冻,不累述。
6.1.2 邏輯運(yùn)算符
Name | Description |
---|---|
$and |
與 |
$not |
非 |
$nor |
或非 |
$or |
或 |
6.1.2.1 $and
- 語法:
{ $and: [ { <expression1> }, { <expression2> } , ... , { <expressionN> } ] }
- Examples
- 同一字段
命令:db.inventory.find( { $and: [ { price: { $ne: 1.99 } }, { price: { $exists: true } } ] } )
結(jié)果:查找price字段的值存在且不等于1.99的結(jié)果弹囚。等同于db.inventory.find( { price: { $ne: 1.99, $exists: true } } )
的執(zhí)行結(jié)果厨相。 - 同一操作符
命令:db.inventory.find( { $and : [ { $or : [ { price : 0.99 }, { price : 1.99 } ] }, { $or : [ { sale : true }, { qty : { $lt : 20 } } ] } ] } )
結(jié)果:查找price字段等于0.99或者1.99并且sale字段等于true或者qty字段值小于20的結(jié)果。
Note:
$and
操作符實(shí)現(xiàn)了短路操作鸥鹉,如果<expression1>執(zhí)行為false蛮穿,MongoDB將不會再去執(zhí)行剩下的expression。
6.1.2.2 $not
- 語法:
{ field: { $not: { <operator-expression> } } }
- Examples
命令:db.inventory.find( { price: { $not: { $gt: 1.99 } } } )
結(jié)果: 查找price字段值小于等于1.99的結(jié)果或者price字段不存在的結(jié)果宋舷。注意與db.inventory.find( { price: { $lte: 1.99 } } )
的區(qū)別绪撵。
6.1.2.3 $nor
- 語法:
{ $nor: [ { <expression1> }, { <expression2> }, ... { <expressionN> } ] }
- Examples
命令:db.inventory.find( { $nor: [ { price: 1.99 }, { sale: true } ] } )
結(jié)果: 匹配的結(jié)果有4種情況
- price字段的值不等于1.99并且sale字段的值不等于true
- price字段的值不等于1.99并且sale字段不存在
- price字段的值不存在并且sale字段的值不等于true
- price字段的值不存在并且sale字段不存在
6.1.2.4 $or
- 語法:
{ $or: [ { <expression1> }, { <expression2> }, ... , { <expressionN> } ] }
Note:
- 用法與
$and
類似。 - 如果
$or
操作在同一個字段上進(jìn)行多次祝蝠,則建議使用$in
。
6.1.3 元素查詢運(yùn)算符
Name | Description |
---|---|
$exists |
指定字段是否存在 |
$type |
匹配類型 |
6.1.3.1 $exists
- 語法:
{ field: { $exists: <boolean> } }
- 這個得益于document的無模式,不然也不會存在這個操作符
6.1.3.2 $type
- 語法:
{ field: { $type: [ <BSON type1> , <BSON type2>, ... ] } }
- 前面已經(jīng)演示過绎狭。
6.1.4 評估運(yùn)算符
Name | Description |
---|---|
$expr |
允許在查詢語言中使用聚合表達(dá)式 |
$jsonSchema |
匹配符合與給定的json綱要的文檔 |
$mod |
取模 |
$regex |
正則 |
$text |
文本對帶有文本索引的字段內(nèi)容執(zhí)行文本搜索 |
$where |
匹配滿足javascript表達(dá)式的文檔 |
6.1.4.1 $expr
- 語法:
{ $expr: { <expression> } }
6.1.4.2 $jsonSchema
- 語法:
{ $jsonSchema: <schema> }
6.1.4.3 $mod
- 語法:
{ field: { $mod: [ divisor, remainder ] } }
- Examples
命令:db.inventory.find( { qty: { $mod: [ 4, 0 ] } } )
結(jié)果:
Note:
- 給$mod操作符傳遞的數(shù)組元素少于兩個或者多于兩個都會報(bào)錯细溅。
6.1.4.4 $regex
- 語法:
{ <field>: { $regex: /pattern/, $options: '<options>' } }
{ <field>: { $regex: 'pattern', $options: '<options>' } }
-
{ <field>: { $regex: /pattern/<options> } }
$options
Option | Description |
---|---|
i |
忽略大小寫 |
m |
匹配使用了錨,例如^(代表開頭)和$(代表結(jié)尾)儡嘶,以及匹配\n后的字符串 |
x |
忽視所有空白字符 |
s |
允許點(diǎn)字符(.)匹配所有的字符喇聊,包括換行符 |
Note:
- 正則性能不是很高,模糊匹配只能是表掃描蹦狂。
- 不能在
$in
操作符中使用$regex
表達(dá)式誓篱。 - 要使用
x
option或者s
option,必須要使用$options
操作符凯楔。
6.1.4.5 $text
- 語法:
-
{
$text:
{
$search: <string>,
$language: <string>,
$caseSensitive: <boolean>,
$diacriticSensitive: <boolean>
}
}
- 用法:首先對一個字段進(jìn)行全文索引窜骄,再進(jìn)行搜索。
- 全文檢索【全文索引】
- 中文分詞很麻煩摆屯,所以不支持中文
- 用處不是很大
6.1.4.6 $where
1.非常強(qiáng)大的遍歷器邻遏,通過包含js表達(dá)式的字符串或者function的模式,一排一排地去找最后的數(shù)據(jù)虐骑。
- 靈活但性能較差的模式【不走索引准验,全表掃描】
- 函數(shù)中的this指向當(dāng)前的迭代文檔
- Examples
假設(shè)users文檔的結(jié)構(gòu)為:
找出與指定MD5 hash相同的name值所在文檔。
命令:db.foo.find( { $where: function() { return (hex_md5(this.name) == "9b53e667f30cd329dca1ec9e6a83e994") } } );
結(jié)果:
6.1.5 數(shù)組操作運(yùn)算符
Name | Description |
---|---|
$all |
匹配所有 |
$elemMatch |
匹配內(nèi)嵌文檔或數(shù)組中的部分field |
$size |
匹配數(shù)組長度為指定大小的文檔 |
6.1.5.1 $all
- 語法:
{ <field>: { $all: [ <value1> , <value2> ... ] } }
- 與in 只需滿足某一個值即可,而$all 必須滿足[ ]內(nèi)的所有值颠黎。
- Examples:
- 命令:
db.inventory.find({ tags: { $all: ['A', 'B' ] }})
-
結(jié)果:
- 命令:
db.inventory.find({ tags: { $in: ['A', 'B' ] }})
-
結(jié)果:
6.1.5.2 $elemMatch
- 語法:
{ <field>: { $elemMatch: { <query1>, <query2>, ... } } }
- 不能在
$elemMatch
中使用$where
或者$text
- Examples:
假設(shè)scores集合的結(jié)構(gòu)為:
- 命令:
db.scores.find( { results: { $elemMatch: { $gte: 80, $lt: 85 } } } )
-
結(jié)果:
6.1.5.3 $size
- Examples:
- 命令:
db.inventory.find( { tags: { $size: 1 } )
-
結(jié)果:
6.2 update運(yùn)算符
6.2.1 字段update運(yùn)算符
Name | Description |
---|---|
$currentDate |
設(shè)置指定字段為當(dāng)前時間 |
$inc |
將文檔中的某個field對應(yīng)的value自增/減某個數(shù)字 |
$min |
將文檔中的某字段與指定值作比較另锋,如果原值小于指定值,則不更新盏缤;若大于指定值砰蠢,則更新 |
$max |
與$min功能相反 |
$mul |
將文檔中的某個field對于的value做乘法操作 |
$rename |
重命名文檔中的指定字段的名 |
$set |
更新文檔中的某一個字段,而不是全部替換 |
$setOnInsert |
配合upsert操作唉铜,在作為insert時可以為新文檔擴(kuò)展更多的field |
$unset |
刪除文檔中的指定字段台舱,若字段不存在則不操作 |
6.2.1.1 $currentDate
- 語法:
{ $currentDate: { <field1>: <typeSpecification1>, ... } }
- Examples:
- 命令:
db.users.update( { _id: 1 }, { $currentDate: { lastModified: true, "cancellation.date": { $type: "timestamp" } }, } )
6.2.1.2 $inc
- 語法:
{ $inc: { <field1>: <amount1>, <field2>: <amount2>, ... } }
- Examples:
假設(shè)products集合的結(jié)構(gòu)為
{
_id: 1,
sku: "abc123",
quantity: 10,
metrics: {
orders: 2,
ratings: 3.5
}
}
- 命令:
db.products.update( { sku: "abc123" }, { $inc: { quantity: -2, "metrics.orders": 1 } } )
-
結(jié)果:
6.2.1.3
-
$min
語法:{ $min: { <field1>: <value1>, ... } }
-
$max
語法:{ $max: { <field1>: <value1>, ... } }
- Examples:
- 命令:
db.tags.update( { _id: 1 }, { $min: { dateEntered: new Date("2013-09-25") } } )
- 結(jié)果:更新_id為1所在文檔的dateEntered字段值,若原來dateEntered值對應(yīng)日期早于2013-09-25潭流,則不更新竞惋。
- 命令:
db.scores.update( { _id: 1 }, { $max: { highScore: 950 } } )
- 結(jié)果:更新_id為1所在文檔的highScore字段值,若原來highScore值大于950灰嫉,則不更新拆宛。
6.2.1.4 $mul
- 語法:
{ $mul: { <field1>: <number1>, ... } }
- Examples:
- 命令:
db.products.update( { _id: 1 }, { $mul: { qty: 2 } } )
- 結(jié)果: 將qty字段值乘以2
6.2.1.5 $rename
- 語法:
{$rename: { <field1>: <newName1>, <field2>: <newName2>, ... } }
6.2.1.6 $set
- 語法:
{ $set: { <field1>: <value1>, ... } }
- Example:
- 命令:
db.products.update( { _id: 100 }, { $set: { "details.make": "zzz" } } )
6.2.1.7 $setOnInsert
- 語法:
db.collection.update( <query>, { $setOnInsert: { <field1>: <value1>, ... } }, { upsert: true } )
- Examples:
假設(shè)products集合的結(jié)構(gòu)為
{
_id: 1,
sku: "abc123",
quantity: 10,
metrics: {
orders: 2,
ratings: 3.5
}
}
- 命令:
db.products.update({_id: 1}, {$set: {sku: 'test'}, $setOnInsert: {defaultQuantity: 100}}, {upsert: true})
-
結(jié)果:
- 命令:
db.products.update({_id: 2}, {$set: {sku: 'abc234'}, $setOnInsert: {defaultQuantity: 100}}, {upsert: true})
- 結(jié)果:
Note:
當(dāng) [upsert: true]時,更新操作如果導(dǎo)致插入了一個新文檔讼撒,那么[$setOnInsert
]將會為新文檔新文檔擴(kuò)展field浑厚,否則股耽,[$setOnInsert
]不起任何作用。
6.2.1.8 $unset
- 語法:
{ $unset: { <field1>: "", ... } }
- Examples:
- 命令:
db.products.update( { sku: "unknown" }, { $unset: { quantity: "", instock: "" } } )
- 結(jié)果:刪除sku字段值為 "unknown"的文檔的quantity和instock兩個字段钳幅。
6.2.2 數(shù)組update運(yùn)算符
6.2.3 位運(yùn)算符
7. mongodb之CURD眾方法
7.1 Insert
方法 | 描述 |
---|---|
db.collection.insertOne() | 插入一條文檔 |
db.collection.insertMany() | 插入多條文檔 |
db.collection.insert() | 插入一條或多條文檔 |
7.2 Delete
方法 | 描述 |
---|---|
db.collection.deleteOne() | 刪除符合條件的一條文檔 |
db.collection.deleteMany() | 刪除符合條件的多條文檔 |
db.collection.remove() | 刪除符合條件的一條或多條文檔 |
7.3 Update
方法 | 描述 |
---|---|
db.collection.update() | 根據(jù)過濾條件更新文檔 |
db.collection.updateOne() | 根據(jù)過濾條件更新一條文檔 |
db.collection.updateMany() | 根據(jù)過濾條件更新多條文檔 |
db.collection.replaceOne() | 根據(jù)過濾條件替換文檔 |
7.4 Query
方法 | 描述 |
---|---|
db.collection.find() | 查找文檔 |
db.collection.findAndModify() | 查找并修改 |
db.collection.findOne() | 查找第一個符合條件的文檔 |
db.collection.findOneAndDelete() | 根據(jù)條件查找并刪除某條文檔 |
db.collection.findOneAndReplace() | 根據(jù)條件查找并修改替換某條文檔 |
db.collection.findOneAndUpdate() | 根據(jù)條件查找并更新某條文檔 |
8. mongodb索引
8.1. 索引的目的
加速查找
Indexes support the efficient execution of queries in MongoDB. Without indexes, MongoDB must perform a collection scan, i.e. scan every document in a collection, to select those documents that match the query statement. If an appropriate index exists for a query, MongoDB can use the index to limit the number of documents it must inspect.
8.2. 索引的實(shí)現(xiàn)原理
Btree
8.3 mondodb中支持的索引類型:
- 單建索引(Single Field)
- 復(fù)合索引(Compound Index)一定要主要鍵值的順序物蝙,字段在前在后有嚴(yán)格的區(qū)別
- 多鍵值索引(Multikey Index)在array字段上建立一個索引,mongodb會將array的每一項(xiàng)進(jìn)行索引
- 地理位置索引(Geospatial Index)
- 全文索引(Text Indexes)不支持中文
- Hash索引(Hashed Indexes)hash是為了做定值查找的
only support equality matches and cannot support range-based queries
- 唯一索引(Unique Indexes)
- 部分索引(Partial Indexes)使用更少的存儲空間敢艰,減少性能開銷
- 稀疏索引(Sparse Indexes)
- 過期索引(TTL Indexes)可以用來做定時過期的效果
8.4 如何創(chuàng)建索引
db.collection.createIndex(keys, options)
8.5 Examples
- 集合
persons.js
var persons = [];
for (var i = 0; i < 10000; i++) {
var name = `Graceji${i}`;
var age = parseInt(Math.random() * 150) + 1;
persons.push({ name, age });
}
db.person.insertMany(persons);
通過load('/Users/jina194/Desktop/persons.js')
方法在person集合中插入了10000條數(shù)據(jù)诬乞。
- 創(chuàng)建單鍵索引:
db.person.createIndex({ age: 1 })
- 執(zhí)行
db.person.find({ age: 88 }).explain()
查看執(zhí)行效果:
{
"queryPlanner" : {
"plannerVersion" : 1,
"namespace" : "test.person",
"indexFilterSet" : false,
"parsedQuery" : {
"age" : {
"$eq" : 88
}
},
// winningPlan: 若干個計(jì)劃中挑選一個作為勝出者
"winningPlan" : {
"stage" : "FETCH",
"inputStage" : {
"stage" : "IXSCAN", // IXSCAN代表索引
"keyPattern" : {
"age" : 1
},
"indexName" : "age_1",
"isMultiKey" : false,
"multiKeyPaths" : {
"age" : [ ]
},
"isUnique" : false,
"isSparse" : false,
"isPartial" : false,
"indexVersion" : 2,
"direction" : "forward",
"indexBounds" : {
"age" : [
"[88.0, 88.0]"
]
}
}
},
"rejectedPlans" : [ ]
},
"serverInfo" : {
"host" : "C02T8D1GGY25.local",
"port" : 27017,
"version" : "3.4.6",
"gitVersion" : "c55eb86ef46ee7aede3b1e2a5d184a7df4bfb5b5"
},
"ok" : 1
}
- 執(zhí)行
db.person.find({ name: 'Graceji888' }).explain()
查看執(zhí)行效果:
{
"queryPlanner" : {
...
"parsedQuery" : {
"name" : {
"$eq" : "Graceji888"
}
},
"winningPlan" : {
// 沒有建立索引的name字段采用的執(zhí)行計(jì)劃則為行掃描(COLLSCAN)
"stage" : "COLLSCAN",
"filter" : {
"name" : {
"$eq" : "Graceji888"
}
},
"direction" : "forward"
},
...
},
...
}
- 創(chuàng)建復(fù)合索引:
db.person.createIndex({ name: 1, age: 1 })
// db.person.getIndexes()
[
{
"v" : 2,
"key" : {
"_id" : 1
},
"name" : "_id_",
"ns" : "test.person"
},
{
"v" : 2,
"key" : {
"age" : 1
},
"name" : "age_1",
"ns" : "test.person"
},
{
"v" : 2,
"key" : {
"name" : 1,
"age" : 1
},
"name" : "name_1_age_1",
"ns" : "test.person"
}
]
- 執(zhí)行
db.person.find({ name: 'Graceji888', age: 88 }).explain('executionStats')
查看執(zhí)行效果:
{
"queryPlanner" : {
"plannerVersion" : 1,
"namespace" : "test.person",
"indexFilterSet" : false,
"parsedQuery" : {
"$and" : [
{
"age" : {
"$eq" : 88
}
},
{
"name" : {
"$eq" : "Graceji888"
}
}
]
},
"winningPlan" : {
"stage" : "FETCH",
"inputStage" : {
"stage" : "IXSCAN", // 索引掃描
"keyPattern" : {
"name" : 1,
"age" : 1
},
"indexName" : "name_1_age_1", // 執(zhí)行計(jì)劃所用索引的名字
"isMultiKey" : false,
"multiKeyPaths" : {
"name" : [ ],
"age" : [ ]
},
"isUnique" : false,
"isSparse" : false,
"isPartial" : false,
"indexVersion" : 2,
"direction" : "forward",
"indexBounds" : {
"name" : [
"[\"Graceji888\", \"Graceji888\"]"
],
"age" : [
"[88.0, 88.0]"
]
}
}
},
// 拒絕的執(zhí)行計(jì)劃
"rejectedPlans" : [
{
"stage" : "FETCH",
"filter" : {
"name" : {
"$eq" : "Graceji888"
}
},
"inputStage" : {
"stage" : "IXSCAN",
"keyPattern" : {
"age" : 1
},
"indexName" : "age_1",
"isMultiKey" : false,
"multiKeyPaths" : {
"age" : [ ]
},
"isUnique" : false,
"isSparse" : false,
"isPartial" : false,
"indexVersion" : 2,
"direction" : "forward",
"indexBounds" : {
"age" : [
"[88.0, 88.0]"
]
}
}
}
]
},
// 執(zhí)行狀態(tài)
"executionStats" : {
"executionSuccess" : true,
"nReturned" : 0,
"executionTimeMillis" : 1,
"totalKeysExamined" : 0,
"totalDocsExamined" : 0,
"executionStages" : {
"stage" : "FETCH",
"nReturned" : 0, // 返回結(jié)果數(shù)
"executionTimeMillisEstimate" : 0, // 執(zhí)行時間
"works" : 2,
"advanced" : 0,
"needTime" : 0,
"needYield" : 0,
"saveState" : 0,
"restoreState" : 0,
"isEOF" : 1,
"invalidates" : 0,
"docsExamined" : 0, // 掃描條數(shù)
"alreadyHasObj" : 0,
"inputStage" : {
"stage" : "IXSCAN",
"nReturned" : 0,
"executionTimeMillisEstimate" : 0,
"works" : 1,
"advanced" : 0,
"needTime" : 0,
"needYield" : 0,
"saveState" : 0,
"restoreState" : 0,
"isEOF" : 1,
"invalidates" : 0,
"keyPattern" : {
"name" : 1,
"age" : 1
},
"indexName" : "name_1_age_1",
"isMultiKey" : false,
"multiKeyPaths" : {
"name" : [ ],
"age" : [ ]
},
"isUnique" : false,
"isSparse" : false,
"isPartial" : false,
"indexVersion" : 2,
"direction" : "forward",
"indexBounds" : {
"name" : [
"[\"Graceji888\", \"Graceji888\"]"
],
"age" : [
"[88.0, 88.0]"
]
},
"keysExamined" : 0,
"seeks" : 1,
"dupsTested" : 0,
"dupsDropped" : 0,
"seenInvalidated" : 0
}
}
},
...
}
- 創(chuàng)建多鍵值索引
db.coll.createIndex( { <field>: < 1 or -1 > } )
,與創(chuàng)建單鍵索引方式類似钠导。 - 創(chuàng)建Hash索引:將
hashed
作為索引key的value值震嫉,例如
db.collection.createIndex( { _id: "hashed" } )
- 創(chuàng)建部分索引:
通過db.collection.createIndex()
方法和partialFilterExpression
option創(chuàng)建。partialFilterExpression
option接受的過濾條件有:
- 相等性表達(dá)式 (例如:
field: value
或者使用$eq
操作符) -
$exists: true
表達(dá)式 -
$gt
,$gte
,$lt
,$lte
表達(dá)式 -
$type
表達(dá)式 -
$and
操作符
例如:
db.restaurants.createIndex(
{ cuisine: 1, name: 1 },
{ partialFilterExpression: { rating: { $gt: 5 } } }
)
以上例子創(chuàng)建了一個只針對rating字段值大于5的文檔的復(fù)合索引牡属∑倍拢可以在所有mongodb支持的索引類型中指定partialFilterExpression
option。
- 創(chuàng)建稀疏索引
假設(shè)有多個document一點(diǎn)關(guān)系也沒有(即結(jié)構(gòu)不一致湃望,除了主鍵_id是一樣的)换衬,這樣的documents怎么建立索引?——稀疏索引
稀疏索引:建立的index字段必須在有這個字段的document上才可以建立证芭。
Sparse indexes only contain entries for documents that have the indexed field, even if the index field contains a null value. The index skips over any document that is missing the indexed field. The index is “sparse” because it does not include all documents of a collection. By contrast, non-sparse indexes contain all documents in a collection, storing null values for those documents that do not contain the indexed field.
通過db.collection.createIndex()
方法和設(shè)置 sparse
option為true來創(chuàng)建稀疏索引瞳浦,例如:db.addresses.createIndex( { "xmpp_id": 1 }, { sparse: true } )
,在 addresses
集合的xmpp_id
字段上創(chuàng)建了稀疏索引废士。
- 過期索引
TTL indexes are special single-field indexes that MongoDB can use to automatically remove documents from a collection after a certain amount of time or at a specific clock time.
通過db.collection.createIndex()
方法和expireAfterSeconds
option(值的類型為date或者包含date的數(shù)組)來創(chuàng)建叫潦。例如:db.eventlog.createIndex( { "lastModifiedDate": 1 }, { expireAfterSeconds: 3600 } )
在eventlog集合的lastModifiedDate字段上創(chuàng)建了一個TTL索引。
Note:
- TTL 索引會讓文檔在指定的時間后過期官硝,即過期時間為被索引的字段值所代表的時間加上指定的過期秒數(shù)
- 如果被索引的字段為包含多個時間值的數(shù)組矗蕊,mongodb會使用最早的日期來計(jì)算過期時間
- 若被索引的字段不是date,或者包含date的數(shù)組氢架,則文檔不會過期
- 如果某文檔不包括被索引的字段傻咖,則該文檔不會過期
- 由于 TTL thread任務(wù)每60s執(zhí)行一次,所以TTL索引不能保證過期的數(shù)據(jù)能夠被立即刪除岖研,會存在一定的延時
- TTL索引為單鍵索引卿操,復(fù)合索引是不支持的,會自動忽略
expireAfterSeconds
option -
_id
字段不支持TTL索引 - 不能在capped collection中創(chuàng)建TTL索引
- 如果一個字段已經(jīng)存在非TTL的單鍵索引孙援,就不能在同一個字段上再創(chuàng)建TTL索引
8.6 索引管理
- 創(chuàng)建索引
db.collection.createIndex()
- 查看索引
db.collection.getIndexes()
- 移除索引
db.collection.dropIndex()
db.collection.dropIndexes()
- 修改索引
先刪除再創(chuàng)建
8.7 索引
9. MongoDB聚合管道(Aggregation Pipeline)
db.collection.aggregate()是基于數(shù)據(jù)處理的聚合管道害淤,每個文檔通過一個由多個階段(stage)組成的管道,可以對每個階段的管道進(jìn)行分組拓售、過濾等功能窥摄,然后經(jīng)過一系列的處理,輸出相應(yīng)的結(jié)果础淤。
9.1 db.collection.aggregate(pipeline, options)
- pipeline為array類型
- 因?yàn)?group和$sort都有內(nèi)存100M的限制崭放,所以將allowDiskUse開啟為true的話哨苛,就沒有此限制了,
db.collection.aggregate(pipeline, { allowDiskUse: true })
9.2 Aggregation Pipeline Stages(有點(diǎn)多莹菱,只介紹常用的)
9.2.1 db.collection.aggregate() Stages
db.collection.aggregate( [ { <stage> }, ... ] )
Stage | 描述 |
---|---|
$group | 將文檔依據(jù)指定字段的不同值進(jìn)行分組 |
$indexStats | 查詢過程中的索引情況 |
$limit | 限制返回的文檔個數(shù) |
$lookup | 與同一數(shù)據(jù)庫中其它集合之間進(jìn)行join操作 |
$match | 根據(jù)query條件篩選文檔 |
$out | 將最后計(jì)算結(jié)果寫入到指定的collection中 |
$project | 對輸入文檔進(jìn)行添加新字段或刪除現(xiàn)有的字段移国,可以自定義字段的顯示狀態(tài) |
$redact | 字段所處的document結(jié)構(gòu)的級別 |
$sample | 從待操作的集合中隨機(jī)返回指定數(shù)量的文檔 |
$skip | 從待操作集合開始的位置跳過文檔的數(shù)目 |
$sort | 對文檔按照指定字段排序 |
$unwind | 將數(shù)組分解為單個的元素吱瘩,并與文檔的其余部分一同返回(Note:1.如果$unwind目標(biāo)字段不存在或者數(shù)組為空道伟,則整個文檔都會被忽略過濾掉 2.如果$unwind目標(biāo)字段不是一個數(shù)組,則會報(bào)錯) |
9.2.1.1 $group
- 語法:
{ $group: { _id: <expression>, <field1>: { <accumulator1> : <expression1> }, ... } }
- 可以分組的數(shù)據(jù)執(zhí)行如下表達(dá)式計(jì)算:
Name | Description |
---|---|
$avg | 計(jì)算平均值 |
$first | 返回每組第一個文檔使碾,如果有排序蜜徽,按照排序,如果沒有按照默認(rèn)的存儲的順序的第一個文檔 |
$last | 返回每組最后一個文檔票摇,如果有排序拘鞋,按照排序,如果沒有按照默認(rèn)的存儲的順序的最后一個文檔 |
$max | 根據(jù)分組矢门,獲取集合中所有文檔對應(yīng)值的最大值 |
$min | 根據(jù)分組盆色,獲取集合中所有文檔對應(yīng)值的最小值 |
$push | 將指定的表達(dá)式的值添加到一個數(shù)組中 |
$addToSet | 將表達(dá)式的值添加到一個集合中(無重復(fù)值) |
$stdDevPop | 計(jì)算總體標(biāo)準(zhǔn)差 |
$stdDevSamp | 計(jì)算累積樣本標(biāo)準(zhǔn)差 |
$sum | 計(jì)算總和 |
- _id字段是必須的,其值是要進(jìn)行分組的key
9.2.1.2 $limit
- 語法:
{ $limit: <positive integer> }
9.2.1.3 $lookup
- 語法
- 相等性匹配
{
$lookup:
{
from: <參與join的輔表>,
localField: <參與join匹配的本表字段>
foreignField: <參與join的輔表字段>
as: <將輔表數(shù)據(jù)輸出到此字段中>
}
}
- 多個Join條件和不相關(guān)子查詢
{
$lookup:
{
from: <collection to join>,
let: { <var_1>: <expression>, …, <var_n>: <expression> },
pipeline: [ <pipeline to execute on the collection to join> ],
as: <output array field>
}
}
9.2.1.4 $match
- 語法:
{ $match: { <query> } }
Note:
- 根據(jù)query條件篩選文檔祟剔,符合條件的文檔將會傳遞給下一個stage;
- $match的語法和mongodb query語法一樣,且$match中不能使用aggregate expression或者比較操作悍汛,只能使用query操作允許的操作符;
- $match用于篩選數(shù)據(jù)费尽,為了提高后續(xù)的數(shù)據(jù)處理效率,盡可能的將$match放于pipeline的前端以減少數(shù)據(jù)讀取或者計(jì)算量叛薯,加快聚合速度浑吟;
- $match可以放在$out之前用于控制數(shù)據(jù)輸出量;
-
如果$match放于pipeline的開始耗溜,還可以使用到索引機(jī)制提高數(shù)據(jù)查詢的性能组力。
假設(shè)orders集合的結(jié)構(gòu)為:
Examples:
9.2.1.5 $out
- 語法:
{ $out: "<output-collection>" }
- 必須為pipeline最后一個階段管道,因?yàn)槭菍⒆詈笥?jì)算結(jié)果寫入到指定的collection中
- 如果不用$out抖拴,那么pipeline計(jì)算的結(jié)果并沒有序列化到硬盤燎字。
9.2.1.6 $project
- 語法:
{ $project: { <specification(s)> } }
9.2.1.7 $sort
- 語法:
{ $sort: { <field1>: <sort order>, <field2>: <sort order> ... } }
-
<sort order>
只能是以下值之一:
-
1
:升序 -
-1
:降序 -
{ $meta: "textScore" }
:根據(jù)textScore
metadata 降序排列
- 注意事項(xiàng):
- 如果將$sort放到管道前面的話可以利用索引提高效率
- 在管道中如果$sort出現(xiàn)在$limit之前的話,$sort只會對前$limit個文檔進(jìn)行操作城舞,這樣在內(nèi)存中也只會保留前$limit個文檔轩触,從而可以極大的節(jié)省內(nèi)存
- $sort操作符默認(rèn)在內(nèi)存中進(jìn)行,超過此限制會報(bào)錯家夺,若要允許處理大型數(shù)據(jù)集脱柱,allowDiskUse 要設(shè)置為true
- Examples:
- 命令1:
db.orders.aggregate([
{
$lookup:
{
from: "inventory",
localField: "item",
foreignField: "sku",
as: "inventory_docs"
}
}
])
- 結(jié)果1:通過orders集合中的item字段與inventory集合中的sku字段將兩個集合中的文檔進(jìn)行合并。
{
"_id" : 1,
"item" : "almonds",
"price" : 12,
"quantity" : 2,
"inventory_docs" : [
{ "_id" : 1, "sku" : "almonds", "description" : "product 1", "instock" : 120 }
]
}
{
"_id" : 2,
"item" : "pecans",
"price" : 20,
"quantity" : 1,
"inventory_docs" : [
{ "_id" : 4, "sku" : "pecans", "description" : "product 4", "instock" : 70 }
]
}
{
"_id" : 3,
"inventory_docs" : [
{ "_id" : 5, "sku" : null, "description" : "Incomplete" },
{ "_id" : 6 }
]
}
- 命令2:
db.orders.aggregate([
{
$lookup:
{
from: "inventory",
localField: "item",
foreignField: "sku",
as: "inventory_docs"
}
}拉馋,
{
$match: {
price: { $gt: 0 }
}
}榨为,
])
- 結(jié)果2:加入$match stage惨好,篩選出price字段大于0的結(jié)果。
{
"_id" : 1,
"item" : "almonds",
"price" : 12,
"quantity" : 2,
"inventory_docs" : [
{ "_id" : 1, "sku" : "almonds", "description" : "product 1", "instock" : 120 }
]
}
{
"_id" : 2,
"item" : "pecans",
"price" : 20,
"quantity" : 1,
"inventory_docs" : [
{ "_id" : 4, "sku" : "pecans", "description" : "product 4", "instock" : 70 }
]
}
- 命令3:
db.orders.aggregate([
{
$lookup:
{
from: "inventory",
localField: "item",
foreignField: "sku",
as: "inventory_docs"
}
}随闺,
{
$match: {
price: { $gt: 0 }
}
}日川,
{
$group: {
_id: '$item',
totalPrice: {$sum: {$multiply: ['$price', '$quantity']}},
quantityAvg: {$avg: '$quantity'}
}
}
])
-
結(jié)果3:加入$group stage,根據(jù)item字段分組矩乐,并計(jì)算totalPrice和QuantityAvg龄句。
- 命令4:
db.orders.aggregate([
{
$lookup:
{
from: "inventory",
localField: "item",
foreignField: "sku",
as: "inventory_docs"
}
},
{
$match: {
price: { $gt: 0 }
}
}散罕,
{
$group: {
_id: '$item',
totalPrice: {$sum: {$multiply: ['$price', '$quantity']}},
quantityAvg: {$avg: '$quantity'}
}
},
{
$project: {
_id: 1,
totalPrice: 1,
demo: 'my demo',
}
}
])
- 結(jié)果4:加入$project stage分歇,去掉quantityAvg字段,并添加一個demo字段欧漱。
- 命令5:
db.orders.aggregate([
{
$lookup:
{
from: "inventory",
localField: "item",
foreignField: "sku",
as: "inventory_docs"
}
}职抡,
{
$match: {
price: { $gt: 0 }
}
},
{
$group: {
_id: '$item',
totalPrice: {$sum: {$multiply: ['$price', '$quantity']}},
quantityAvg: {$avg: '$quantity'}
}
},
{
$project: {
_id: 1,
totalPrice: 1,
demo: 'my demo',
}
},
{
$sort: { totalPrice: -1 }
}
])
- 結(jié)果5:加入$sort误甚,根據(jù)totalPrice字段降序排序缚甩。
- 命令6:
db.orders.aggregate([
{
$lookup:
{
from: "inventory",
localField: "item",
foreignField: "sku",
as: "inventory_docs"
}
},
{
$match: {
price: { $gt: 0 }
}
}窑邦,
{
$group: {
_id: '$item',
totalPrice: {$sum: {$multiply: ['$price', '$quantity']}},
quantityAvg: {$avg: '$quantity'}
}
},
{
$project: {
_id: 1,
totalPrice: 1,
demo: 'my demo',
}
},
{
$limit: 1
}
])
- 結(jié)果6:加入$limit擅威,限制返回的文檔的個數(shù)。
- 命令6:
db.orders.aggregate([
{
$lookup:
{
from: "inventory",
localField: "item",
foreignField: "sku",
as: "inventory_docs"
}
}奕翔,
{
$match: {
price: { $gt: 0 }
}
}裕寨,
{
$group: {
_id: '$item',
totalPrice: {$sum: {$multiply: ['$price', '$quantity']}},
quantityAvg: {$avg: '$quantity'}
}
},
{
$project: {
_id: 1,
totalPrice: 1,
demo: 'my demo',
}
},
{
$limit: 1
},
{
$out: 'myaggretion'
},
])
- 結(jié)果7:加入$out,將結(jié)果寫入到myaggretion集合中派继。
9.2.2 db.aggregate() Stages
db.aggregate( [ { <stage> }, ... ] )