MongoDB常用操作整理


Mongodb:是一種NoSQL數據庫,NoSQL:Not Only SQL
SQL: 數據表->JDBC讀取->POJO(VO、PO)->控制層轉化為JSON數據->客戶端
這種轉換太麻煩了,如果有直接數據庫存放要顯示的內容,就能夠省略所有需要進行轉換的過程。
所以在實際開發中,往往除了關系型數據庫之外還要提供一個NoSql數據庫,其中NoSql數據庫負責數據的讀取,因為直接保存的就是JSON(前提:MongoDB中的數據是排列好的組合數據)。
例如:現在要求顯示出每個雇員的編號、姓名、部門名稱、部門位置、工資等級。傳統的關系型數據庫之中一定要存放大量的冗余數據,不合理。而又nosql數據庫,可以直接在業務層里面將數據交給nosql數據庫保存。按照指定的結構進行存儲。
MongoDB數據庫之中與Oracle數據庫有如下的概念對應:
oracle:表 nosql:集合
oracle:行 nosql:文檔
oracle:列 nosql: 成員
oracle: 主鍵: nosql:object id(自動)
Node.js中一定要使用MongoDB,而node.js(基於JavaScript操作)在國內最成功的應用-taobao。
Mongodb:面向集合的存儲過程,模式自由(無模式)。支持索引、支持短暫數據保留、填充、具備完整的數據庫狀態監控、基於BSON的應用。
Mongodb與mysql是一種互補的關系。

命令:
不設置端口號啟動MongoDB服務:
mongod --dbpath D:\MongoDB\db 表示要再這個目錄保存所有的數據庫文件的操作
設置端口號啟動:
mongod --dbpath D:\MongoDB\db --port=2700
如果通過程序訪問數據庫的話,一定要設置端口號。
使用mongo命令連接數據庫(重新啟動一個cmd)
mongo
范例:查詢所有的數據庫
show databases
此時,只存在一個local的本地數據庫,不過這個數據庫不使用。
在實際的開發中,在Mongodb中設置一些相應參數:端口號、是否啟用用戶驗證、數據文件的位置等等。
在D:\MongoDB目錄下,建立一個文件"mongodb.conf",
同時,建立一個保存日志信息的文件" mongodb.log"

重新啟動mongodb的命令:
mongod -f d:\MongoDB\mongodb.conf

在Mongo數據庫的基礎操作:
1、使用mldn數據庫:
use mldn
實際上這個時候並不會創建數據庫,只有在數據庫里面保存集合數據之后才能真正創建數據庫。
2、創建一個集合
db.createCollection("emp") 創建一個emp集合
此時,利用show databases;可以看到創建的數據庫即mldn數據庫才會真正的存在。
3、但是很多時候如果按照以上代碼的形式進行會覺得你不正常,正常是直接保存一個數據。
db.dept.insert({"deptno":10,"dname":"財務部","loc":"北京"});

4、查看所有集合的命令:show collections
可以看出dept的集合自動創建

5、查看emp標的數據
db.集合名稱.find({若干條件})
從傳統的數據表來看(集合就相當於表的結構),表的結構一旦定義就要按照其定義的內容來編寫,但是MongoDB不一樣,可以隨意擴展數據。

6、可以隨意增加數據
var deptData = {
"deptno":20,
"dname":"研發部",
"loc":"深圳",
"count":20,
"avg":8000.0
};
db.dept.insert(deptData)
此時dept集合的內容可以由用戶隨意去定義,完全不用考慮其它的結構,那么實際上就必須明確一點,在MongoDB數據庫中是絕對不可能存在有查看集合結構的操作。

7、關於ID的問題
在MongoDB集合中的每一行記錄都會自動的生成一個"_id“:" 數據組成是:時間戳+機器碼+PID+計數器",這個ID的信息是MongoDB自己為用戶服務的。
范例:查看單獨的一個文檔信息
db.dept.findOne();
8、范例:刪除數據
db.dept.remove({"_id" : ObjectId("5624fc5bfddcd6e2428e9ed0")})

10、更新數據
var timData = {
"account":"tim",
"passwd":"123",
"studentid":525
};
db.dept.update({"_id" : ObjectId("5624fe1ffddcd6e2428e9ed1")},timData);
11、刪除集合
語法:db.集合名稱.drop()
db.dept.drop()
12、刪除數據庫(刪除當前所在的數據庫信息)
db.dropDatbase()


數據操作
CRUD:除了增加之外,其它的都很麻煩。
1、數據增加
使用:db.集合.insert() 可以實現數據的增加操作。
范例:增加一個簡單的數據
db.infos.insert({"url":"www.mldn.cn"});
切換到mldn數據庫:use mldn
范例:保存數組
db.infos.insert([
{"url":"www.mldn.cn"},
{"url1":"www.mldnjava.cn"}
]);
范例:保存10000個數據
for(var x = 0;x<10000;x++){
db.infos.insert({"url":"mldn-"+x});
}
如果數據保存很多的情況下,列表不會全部列出,只會列出部分內容。
2、數據查詢
任何數據庫中,數據的查詢操作是最為麻煩的,而在MongoDB的數據庫中有關系運算、邏輯運算、正則運算等等。
語法:db.集合名稱.find({查詢條件}[,{設置顯示的字段}])
范例:最簡單的用法是用find()函數完成查詢:db.infos.find();
希望查詢出url為:www.mldn.cn
db.infos.find({"url":"www.mldn.cn"});
對於設置的顯示字段嚴格來說稱為數據的投影操作,如果不需要顯示的字段設置為”0“,而需要顯示的字段設置為”1“。
范例:不顯示ID
db.infos.find({"url":"www.mldn.cn"},{"_id":0,"url":1});
大部分的情況下,這種投影意義的查詢意義不大,數據的查詢可以使用”pretty()“函數進行漂亮顯示。
范例:漂亮顯示:
db.infos.find({"url":"www.mldn.cn"},{"_id":0,"url":1}).pretty();
數據列多的時候可以看出華麗的顯示。

關系查詢:
在MongoDB里面支持的關系查詢操作:大於($gt)、小於($lt)、大於等於($gte)、小於等於($lte)、不等於($ne)、等於(key:value,$eq)。但是要想讓這些操作可以正常使用,那么需要准備出一個數據集合。
范例:定義一個學生信息集合:
db.students.drop();
db.students.insert({"name":"張三","sex":"男","age":19,"score":89,"address":"北京"});
db.students.insert({"name":"李四","sex":"女","age":21,"score":59,"address":"廣州"});
db.students.insert({"name":"王五","sex":"男","age":20,"score":99,"address":"天津"});
db.students.insert({"name":"趙六","sex":"女","age":19,"score":100,"address":"上海"});
db.students.insert({"name":"陳七","sex":"男","age":25,"score":20,"address":"深圳"});
db.students.insert({"name":"王八","sex":"男","age":19,"score":78,"address":"福州"});
范例:查詢名字為張三的信息:
db.students.find({"name":"張三"}).pretty();
范例:查詢性別是男的信息:
db.students.find({"sex":"男"}).pretty();
范例:查詢年齡大於20歲:
db.students.find({"age":{"$gt":20}}).pretty();
范例:查詢成績大於等於60分:
db.students.find({"score":{"$gte":60}}).pretty();
范例:查詢姓名不是王五的信息:
db.students.find({"name":{"$ne":"王五"}}).pretty();
此時與之前最大的區別就在於,在一個JSON結構里面需要定義其他的JSON結構,這種風格通過程序進行操作依然如此。

邏輯運算主要就是三種:與($and)、或($or)、非($not、$nor)。
范例:查詢年齡在19~20歲的學生信息
db.student.find({"age":{"$gte":19,"$lte":20}}).pretty();
在進行邏輯運算的時候,"and”的連接是最容易的,因為只需要利用","分割若干個條件就可以了。
范例:查詢年齡不是19歲。
db.student.find({"age":{"$ne":{"$gte":19}}});
范例:查詢年齡大於19歲、或者成績大於90分的學生信息:
db.students.find({"$or":[
{"age":{"$gt":19}},
{"score":{"$gt":90}};
] }).pretty();
針對於或的運算,可以實現一個求反的功能。
在這幾個邏輯運算之中,與的連接最簡單,而或的連接需要為數據設置數據的過濾條件。

求模
模的運算使用:“$mod” 語法:“{$mod:[數字,余數]}”
db.students.find({"age":{"$mod",[20,0]}}).pretty()
利用求模運算可以編寫一些數學公式。

范圍查詢:
只要是數據庫,必須存在有"$in"(在范圍之中)、"$nin"(不在范圍之中)
范例:查詢姓名是"張三"、”李四"、“王五"的信息。
db.students.find({"name":{"$in":["張三","李四","王五"]}}).pretty();
在實際的工作之中,范圍的操作很重要。

數組查詢:
在mongodb是支持數組保存的,就需要對數組的數據進行匹配。
范例:保存一部分數組內容:
db.student.insert({"name":大神,"course":["語文","英語","數學"]});
此時的數據包含有數組的內容,而后需要針對數組數據進行判斷,可以使用幾個運算符:$alll、$size、$slice、$elemMatch
范例:查詢同時參加語文和數學課程的學生:
現在兩個數組內容都需要保存,所以使用"{"$all",[內容1,內容2,...]}"
db.students.find({"course":{"$all":["語文","數學"]}}).pretty();
現在顯示所有信息包含語文和數學的內容,而如果差一個內容的不會顯示。
雖然,$all 計算可以用於數組上,但是也可以用於一個數據的匹配上。
范例:查詢學生地址是“海淀區"的信息
db.students.find({"address":{"$all":[海淀區]}}).pretty();
既然在集合里面保存的是數組信息,那么數組就可以利用索引操作,使用"key.index”的方式來定義索引。
范例:查詢數組中第二個內容(index=1,索引下標從0開始)為數學的信息。
db.students.find({"course.1":"數學"}).pretty();
范例:要求查詢出只參加兩門課程的學生:(使用$size 來進行數量的控制)
db.students.find({"course":{"$size":2}})
在進行數據查詢的時候,只要是內容符合條件,數組的內容就全部顯示出來,但是現在希望可以控制數組返回的數量,那么可以使用$slice 進行控制。
范例:返回年齡為19歲,所有學生的信息,但是只顯示兩門參加課程。
db.students.find({"age":19},{"$course":{"$slice":2}}).pretty()
現在只取得前兩門的信息,也可以設置取得后兩門的信息,把上面的2設置為-2.
db.students.find({"age":19},{"$course":{"$slice":-2}}).pretty()
或者只是取得中間部分的信息:
db.students.find({"age":19},{"$course":{"$slice":[1,3]}}).pretty()
在此時設置的兩個數據里面,第一個數據表示跳過的數據量,而第二個表示返回的數據量。


嵌套集合運算:
在MongoDB數據庫里面每一個集合數據可以繼續保存其它的集合數據,例如:有些學生需要保存家長信息。
范例:
此時給出的內容是嵌套的集合,而這種集合的數據的判斷只能夠通過"$elemMatch"完成
范例:查詢出父母有人是局長的信息。
db.students.find("$and":[{"age":{"$gte":19}},{"parents":{"$elemMatch":{"job":"局長"}}}]).pretty();
由於這種查詢的條件比較麻煩,所以盡可能不要搞這么復雜的數據結構組成。

判斷某個字段是否存在:
使用“$exists”可以判斷某個字段是否存在,如果設置為true表示存在,如果設置為false表示不存在.
范例:查詢具有parents成員的數據
db.students.find({"parents":{"$exists":true}}).pretty();
范例:查詢不具有course成員的數據:
db.students.find({"course":{"$exists":false}}).pretty();
可以利用此類查詢來進行一些不需要的數據過濾,但是建議數據組成一致。

條件過濾
實際上習慣於傳統關系型數據庫開發的我們對於數據的篩選,可能首先想到的一定是where子句,所以在mogo里面也提供有"$where"。
范例:使用where進行數據查詢
db.students.find({"$where":"this.age>20"}).pretty();
可以直接用:db.students.find("this.age>20").pretty();
對於“$where”是可以簡化的,但是這類的操作是屬於進行每一行的信息判斷,實際上對於數據量較大的情況是不方便使用。實際上,以上的代碼嚴格來講是屬於編寫一個操作的函數。
db.students.find(function(){ return this.age>20; }).pretty();
以上只是查詢了一個判斷,如果要想實現多個條件的判斷,那么就需要使用and連接。

db.students.find({"$and":[
{"$where":"this.age>19"}.
{"$where":"this.age<19"}
]}).pretty();
雖然這種形式的操作可以實現數據查詢,但是最大的缺點是將MongDB里面保存的BSON數據變為JavaSript的語法結構,這樣的方式不方便使用數據庫的索引機制。

正則運算
如果要想實現模糊查詢,那么必須使用正則表達式,而且正則表達式使用的語言Perl兼容的正則表達式的形式。如果要想實現正則使用,則按照如下的定義格式:
基礎語法:{key:正則標記}
完整語法:{key:{"$regex":正則標記,"$options":選項}}
其中,對於options主要是設置正則的信息查詢的標記。
"i":忽略字幕大小寫
"m":多行查找
"x":空白字符串除了被轉義的或在字符中意外的完全被忽略:
"s":匹配所有的字符(圓點、“.”),包括換行內容
需要注意的是,如果是直接使用(javascript)那么只能夠使用i和m,而"x"和"s"必須使用"$regex"
范例:查詢以"谷"開頭的姓名
db.students.find({"name":/谷/}).pretty();
范例:查詢姓名有字母a
db.students.find({"name":/a/i}).pretty(); 其中,i表示忽略大小寫
上面的寫法可以為:db.students.find("name":{"$regex":/a/i}).pretty();
如果要執行模糊查詢的操作,嚴格來說只需要編寫一個關鍵字。
正則操作之中,除了可以查詢單個字段的內容之外,也可以查詢數組數據。
范例:查詢數組數據。
db.students.find("course":{"$regex":/語/}).pretty();
mongodB中的正則符號和之前java正則是有一些小小差別。不建議使用以前的一些標記,正則就用在模糊數據的查詢上。

數據排序:
在mongoDB里面數據排序操作使用"sort()"函數,在進行排序的時候可以有兩個順序:升序(1)、降序(-1)
范例:數據排序
db.students.find().sort({"sort":-1}).pretty();
但是在進行排序的過程中,有一種方式稱為自然排序,按照數據保存的先后順序排序,使用"$natural"表示
范例:自然排序
db.students.find().sort({"$natural":-1}).pretty();
在MongoDB數據庫里面排序的操作相比傳統關系型數據庫的設置要簡單。

數據的分頁顯示:
在MongoDB里面的數據分頁顯示也是符合大數據要求的操作函數:
skip():表示跨過多少數據行。
limit(n):取出的數據行的個數限制。
范例:分頁顯示(第一頁,skip(0),limit(5))
db.students.find().skip(0).limit(5).sort({"age":-1}).spretty();
范例:分頁顯示(第二頁,skip(5),limit(5))
db.students.find().skip(5).limit(5).sort({"age":-1}).spretty();
這兩個分頁的控制操作,就是在以后只要有存在大數據的信息情況下都會使用它。

數據更新操作
對於MongoDB而言,數據的更新基本上是一件很麻煩的事情,如果在實際的工作中,真的具有此類的操作支持,那么最好的做法,在MongoDB里面對於數據的更新操作提供了兩類函數:save()、update()。

函數的基本使用
如果要修改數據最直接的使用函數就是update()函數,但是這個函數的語法要求很麻煩。
語法:db.集合.update(更新條件,新的對象數據(更新操作符),upsert,multi)
-update:如果要更新的數據不存在,則增加一條新的內容(ture為增加、false為不增加)
-multi:表示是否只更新滿足條件的第一行記錄,如果設置為false,則只更新第一行記錄,如果是true全更新。
以下是更新存在的數據:
范例:將年齡是19歲的人的成績更新為100分(此時會返回多條數據)
db.students.update({"age":19},{"$set":{"score":100}},false,false); 選擇只更新更新的第一條數據
db.students.update({"age":19},{"$set":{"score":100}},false,true); 所有滿足條件的數據都更新
范例:更新不存在的數據
db.students.update({"age":30},{"$set":{"name":"不存在"}},true,false)
由於沒有年齡是30歲的學生信息,所以此時相當於進行數據的創建。

那么除了update()函數之外,還提供有一個save()函數,這個函數的功能與更新不存在的內容相似。
范例:使用save()操作
db.students.save({"_id":ObjectId("5123145d2fde")},{"age":50})
save操作不好用,可以用update函數來使用。


修改器:
對於MongDB數據庫而言,數據的修改會牽扯到內容的變更、結構的變更(包含有數組),所以在進行MongoDB設計的時候,就提供有一系列修改器的應用,那么之前使用的"$set"就是一個修改器的使用
1、$inc:主要針對於一個數字字段,增加某個數字字段的內容;
語法:{"$inc":{成員:內容}}
范例:將所有年齡為19歲的學生成績一律減少30分,年齡加一歲
db.students.update({"age":19},{"$inc":{"score":-30,"age":1}},false,true);//全部執行,false,true

2、$set:進行內容的重新設置
語法:{"$set":{"成員":"新內容"}}
范例:將年齡是20歲的人的成績修改為89分
db.students.update({"age":20},{"$set":{"score":19}})
3、$unset:刪除某個成員的內容
語法:{"$unset":{"成員":1}}
范例:刪除"張三"的年齡與成績信息
db.students.update({"name":"張三"},{"$unset":{"age":1,"score":1}})
執行之后指定的成員內容就消失了。
4、$push:相當於將內容追加到指定的成員之中(基本上是數組)
語法:${"$push":{"成員":value}}
范例:向張三添加課程信息(此時張三信息里面沒有course信息)
db.students.update({"name":"張三"},{"$push":{"course":["語文","數學"]}})
范例:向"谷大聲-E"里面的課程追加一個"美術"
db.students.update({"name":"股大聲-E"},{"$push":{"course":"美術"]}})
push就是進行數組數據的添加操作使用,如果沒有數組則進行一個新的數組的創建,如果有則進行內容的追加。

5、$pushAll:與"$push"類似的,可以一次追加多個內容到數組里面:
語法:${"$pushAll":{成員:數組內容}}
范例:向"王五"的信息里面添加多個課程內容
db.students.update({"name":"王五"},{"$pushAll":{"course":["語文","數學"]}})

6、$addToSet:向數組里面添加一個新的內容,只有這個內容不存在的時候才會增加。
語法:{"$addToSet":{成員:內容}}
范例:向王五的信息增加新的內容
db.students.update({"name":"王五"},{"$addToSet":{"course":"跳舞"}})
此時會判斷要增加的內容在數組里面是否已經存在了,如果不存在則追加內容,如果存在,則不增加內容。

7、$pop:刪除數組內的數據:
語法:{"$pop":{成員:內容}},內容如果設置為-1表示刪除第一個,1表示刪除最后一個
范例:刪除王五的一個課程
db.students.update({"name":"王五"},{"$pop":{"course":"-1"}})
范例:刪除王五的最后一個課程:
db.students.update({"name":"王五"},{"$pop":{"course":"1"}})

8、$pull:從數組內刪除一個指定內容的數據
語法:{"$pull",{成員:數據}} 進行數據比對,如果是此數據則進行刪除
范例:刪除王五內的跳舞的課程,如果存在就刪除
db.students.update({"name":"王五"},{"$pull":{"course":"跳舞"}})
9、$pullAll:一次性刪除多個內容
語法:{"$pull":{成員:[數據1,數據2]}}
范例:刪除”顧大神-a“的二門課程
db.students.update({"name":"股大聲"},{"$pullAll":{"course":["跳舞","音樂"]}})

10、$rename:為成員名稱重命名
語法:{"$rename":{舊的成員名稱:新的成員名稱}}
范例:將"張三"name成員名稱修改為"姓名"
db.students.update({"name":"王五"},{"$rename":{"name":"姓名"}})
在整個的MongoDB數據庫里面,提供的修改器的支持很到位。

刪除數據:
在MongoDB里面數據的刪除實際上並不復雜,只需要使用"remove()"函數就可以了。
但是,整個函數是有兩個可選項。1、刪除條件:滿足條件的數據被刪除 2、是否只刪除一個數據,如果設置為true或者是1表示只刪除一個。
范例:清空info集合中的內容。
db.infos.remove({});
范例:刪除所有姓名里面帶有“谷”的信息。
db.students.remove({"name":/谷/})
范例:刪除姓名帶有"高"的信息,要求只刪除一個
db.students.remove({"name":/高/},true)
刪除操作里面依然需要使用限定查詢的相關操作內容。

游標:
所謂的游標就是指的數據可以進行一行行的進行操作,非常類似於ResultSet數據處理。在MongoDB數據里面對於游標的控制非常簡單,只需要使用find()函數就可以返回游標了。對於返回的游標如果要進行操作,則可以通過兩個函數:一個是是否有下一行數據:hasNext(),另一個是取出當前數據next();
var cursor = db.students.find();
cursor.hasNext()
cursor.next();
以上是游標的操作形式,但是實際上並不可能這么去用,因為必須利用循環才能輸出內容。
相當於每一個數據都單獨拿出來逐行的控制。當我們游標數據取出來的時候,實際上每行數據返回的都是Object型的內容,那么如果需要數據按照json的形式出現,可以使用printjson()函數完成。
在所有的已知數據里,MongoDB的游標操作是最簡單的,最直觀的。

索引
在任何的數據庫之中,索引都是一種提升數據庫檢索性能的手段,這一點在MongoDB數據庫之中同樣是存在的,在MongoDB數據庫之中,依然會存在兩種的索引的創建:一種是自動創建的;另一種是手工創建。
范例:重新准備一個新的簡單集合
此時,我們在students集合之上並沒有設置任何的索引,那么下面通過getIndexes()函數來觀察在students集合已經存在索引內容。
范例:查詢默認狀態下的students集合的索引內容。
db.students.getIndexes();
想創建自己的索引,如下:
索引創建:db.集合名稱.ensureIndex({列:1})
-設置的1表示索引將按照升序的方式進行排列,如果使用降序設置"-1"
范例:創建一個索引,在age字段上設置一個降序索引
db.students.ensureIndexes({"age":-1})
此時並沒有設置索引的名字,所以名字是自動命名的。命名規范:“字段名稱_索引的排序"
范例:針對於當前的age字段上的索引做一個分析
db.students.find("age":19).explain();
此時的查詢使用了索引的技術,但是下面再來觀察一個查詢,不使用索引字段。
范例:針對score字段上設置查詢
db.students.find({"score":{"$gt":60}}).explain();
此時在score上的字段並沒有設置索引,所以當前的索引形式就變為了全集合掃描的模式。
但是,如果說現在年齡和成績一起執行查詢。
db.students.find({"$or":[{"age":{"$gt":19}},{"score":{"$gt":60}}]}).explain();
這個時候雖然age字段上存在有索引,但是由於score字段上沒有索引,所以依然使用的是全表掃描操作,所以此時,為了解決這個問題,可以使用一個符合索引。
db.students.ensureIndex({"age":-1,"score":-1},{"name":"age_-1_score_-1"})
范例:默認使用索引
db.students.find({"age":19,"score":89}).explain()
但是如果換到了條件之中:
db.students.find({"$or":[{"age":{"$gt":19},{"score":{"$gt":60}}}]}).explain();
但是發現並沒有使用索引,所以如果這個時候看能夠強制使用一次索引。hint()函數為強制使用一次索引操作。
范例:強制使用索引
db.students.find({"$or":[{"age":{"$gt":19},{"score":{"$gt":60}}}]}).hint({"age":-1,"score":-1}).explain();
正常來講,這個代碼根本就不可能調用默認的索引執行,可使用hint()函數,告訴MongDB必須使用一次索引,由於此時在age和score兩個字段已經設設置了符合索引,那么現在 的復合索引。
但是在一個集合里面設置了過多的索引,實際上會導致性能下降,那么就可以刪除索引。
范例:刪除一個索引
db.students.dropIndex({"age":-1,"score":-1})
可是如果一個一個刪除索引,也會很麻煩,所以提供有刪除全部索引的操作。
范例:刪除全部索引。
db.students.dropIndex()
所謂的刪除全部索引,就是非"_id"的索引,所有的自定義索引。

唯一索引:
唯一索引的主要目的是用在某一個字段上,使該字段的內容不重復。
范例:創建一個唯一索引
db.students.ensureIndex({"name":1},{"unique":true})
在name字段上的內容絕對不允許重復。
范例:在students集合里面增加重復的數據。
此時除了,name字段上的內容之外,發現所有的數據都不一樣,但是由於在name字段上設置了唯一索引,所以整個程序里面,如果增加了重復內容,會出現以下的錯誤信息。

過期索引:
在一些程序點上回出現若干秒之后信息被刪除的情況,例如:手機的信息驗證碼,那么在MongoDB里面可以輕松的實現過期索引,但是這個時間往往不怎么准確。
范例:設置過期索引
db.phones.ensureIndex({"time":1},{expireAfterSeconds:10})
設置索引在10秒后過期
等到10秒之后(永遠不會那么准確)所保存的數據就會消失。這個操縱在進行一些臨時數據保存的時候非常有幫助,最早如果沒有MongoDB,而是使用最簡單的關系型數據庫進行開發這個過程是非常麻煩的。

范例:在一個叫做phone集合里面設置過期索引
如果要想實現過期索引,需要保存一個時間信息。
db.phones.insert({"tel":"110","code":"110","time":new Date()})

全文索引
在一些信息管理平台上經常要進行信息模糊查詢,最早的時候是利用了某個字段上實現的模糊查詢,但是這個時候返回的信息,並不會很准確,因為只能夠查詢A字段或者是B字段,而在MongoDB里面實現了非常簡單的全文檢索。
范例:定義一個新的集合:
db.news.insert({"titel":"mldn java","content":"gyh"})
范例:設置全文檢索
db.news.ensureIndex({"title":"text","content":"text"})
范例:實現數據的模糊查詢
如果要想表示出全文檢索,則使用"$text"判斷符,而要想進行數據查詢則使用"$search"運算符:
-查詢指定的關鍵字;{"$serach":"查詢關鍵字"}
-查詢多個關鍵字(或關系):{"$search":"查詢關鍵字 查詢關鍵字 ..."}
-查詢多個關鍵字(與關系){"$search":"\"查詢關鍵字\""}
-查詢多個關鍵字(排除某一個):{"$search":" "}
范例:查詢單個內容
db.news.find("{"$text":{"$search":"gyh"}}")
范例:包含有"gry"與"sfq"的信息
db.news.find("{"$text":{"$search":"gry sfq"}}")
范例:同時包含有"mldn" 與 "lxh"的內容
db.news.find({"$text":{"$search":"\"mldn\" \"lxh\""}})
范例:包含有"mldn",但是沒有"gyh"的信息。
db.news.find("$text":{"$serach":"\"mldn\" \"lxh\" -gyh"})
但是在進行全文檢索操作的時候,還可以使用相似度的打分來判斷檢索成果。
范例:為結果打分
db.news.find({"$text":{"$search":"gyh"}},{"score":{"$meta":"textScore"}}).sort({"score":{"score":{"$meta":"textScore"}})
按照打分的成績進行排列可以實現更加准確的信息搜索。
但是在這里面還有一個小問題,如果一個集合的字段太多了,那么每一個字段都分別設置全文檢索麻煩點。所以簡單一些,可以為所有字段設置全文檢索。
范例:為所有字段設置全文索引
db.news.ensureIndex({"$**":"text"})
這是一種最簡單的設置全文索引的方式,但是盡可能不用,速度慢。

地理信息索引
地理信息索引分為兩類:2D平面索引,另外就是2DSphere球面索引。在2D索引里面基本上能夠保存的信息都是坐標,而且坐標保存的就是經緯度坐標。
范例:定義一個商鋪的集合
db.shop.insert({loc:[10,11]})
范例:為shop集合定義2D索引
db.shop.ensureIndex({"loc":"2d"})
這個時候shop集合就可以實現坐標位置的查詢,而要進行查詢有兩種查詢方式:
-1:"$near"查詢,查詢距離某個點最近的坐標點
-2:"$geoWithin"查詢:查詢某個形狀內的點
范例:假設我現在的坐標是[11,11]
db.shop.find({"loc":{"$near":[11,11]}})
但是,如果執行以上的查詢,實際上會將集合里面的額前100個點的信息都返回來了,可是太遠了,設置了一個距離范圍--5個點內。
范例:設置查詢的距離范圍
db.shop.find({"loc":{"$near":[11,11],"$maxDistance":5}})
但是,我們需要注意一點,在2D的索引里面雖然支持最大距離,但是不支持最小距離。
但是,也可以設置一個查詢的范圍,使用"$geoWithin"查詢,可以設置以下幾種范圍:
-1、矩形范圍($box) {"$box":[[x1,y1],[x2,y2]]}
-2、圓形范圍($centor):{"$center":[[x1,y1],r]}
-3、多邊形($polygon):{"$polygon":[[x1,y1],[x2,y2],[x3,y3]]}
范例:查詢矩形
db.shop.find({"loc":{"$geoWithin":[[9,9],[11,11]]}})
范例:查詢圓形
db.shop.find({"loc":{"$geoWithin":[[9,9],2]}})
在MongoDB數據庫里面,除了一些支持的操作函數之外,還有一個重要的命令:runCommand(),這個函數可以執行特定的MongoDB命令。
范例:利用runCommand()實現信息查詢
db.runCommand({"geoNear":"shop",near:[10,10],maxDistance:5,num:2})
這類的命令可以說是MongoDB之中最為基礎的命令。

聚合函數:
MongoDB產生的背景是在大數據的環境下,實際上大數據就是進行信息的收集匯總。那么必須存在有信息的統計操作,而這樣的統計操作稱為聚合(直白:分組統計就是一種聚合操作)。

取得集合的數據量
對於集合的數據量而言,在MongoDB里面直接使用count()函數就可以完成。
范例:統計students表中的數據量
db.students.count();
范例:模糊查詢
db.students.count({"name":/張/i})
在信息查詢的時候,不設置條件永遠要比設置條件的查詢快很多。也就是說,在之前的代碼編寫里面不管是查詢全部還是模糊查詢,實際最終都使用模糊查詢的一種(沒有設置關鍵字)。

消除重復數據:
在學習SQL的時候,對於重復的數據可以使用"DISTINCT",那么這一操作在MongoDB之中依然支持。
范例:查詢所有name的信息(沒有重復)。
-本次的操作沒有直接的函數支持,只能怪利用runCommand()函數。
db.runCommand({"distinct":"students","key":"name"})
此時,實現了對於name數據重復值得篩選。

group操作
使用"group"操作可以實現數據的分組操作,在MongoDB里面會將集合指定的Key的不同進行分組操作,並且每一個組都會產生我們處理的文檔結果。
范例:查詢所有年齡大於等於19歲的學生信息,並且按照年齡分組。
db.runCommand({"group":{"ns":"students",
"key":{"age":true},
"initial":{"count":0},
"condition":{"age":{"$gte":19}},
"$reduce":function(doc,prev){
prev.count++;
}
}});
以上的操作代碼里面實現的就屬於一種MapReduce,但是這樣只是根據傳統的數據庫設計思路操作實現所謂的分組操作。但是這個分組操作的最終結果是有限的。

MapReduce
MapReduce是整個大數據的精髓所在(實際中別用),所謂的MapReduce就是分為兩步處理數據:
-Map:將數據分別取出
-Reduce:負責數據的最后處理。
可是要想在MongoDB里面實現MapReduce處理,那么復雜度是相當高的。
范例:建立一組雇員數據
db.emps.insert({"name":"張三","age":30,"sex":"男","job":"CLERK","salary":1000})
使用MapReduce操作會將處理結果保存在一個單獨的集合里面,而最終的處理效果如下:
范例:按照職位分組,取得每個職位的人名
- 編寫分組的定義:
var jobMapFun = function()
{
emit(this.job,this.name);//按照job分組,取出name
};
第一組:{key:"CLERK",values:[姓名,姓名,,,,]}
-編寫Reduce操作:
var jobReduceFun= function(key,values){
return{"job":key,"names":values};
}

var jobFinalizeFun = function(key,values){
if(key == "PRESIDENT"){
return{"job":key,"names":value,"info":"公司的老大"}
}
return{"job":key,"names":values};
}

-進行操作的整合:
db.runCommand({"mapreduce":"emps","map":jobMapFun,"reduce":jobReduceFun,"out":"t_job_emp",”“finalize":jobFinalizeFun})
現在執行之后,所有的處理結果都保存在"t_job_temp"集合里面。

范例:統計出個性別的人數、平均工資、最低工資、雇員姓名
var sexMapFun = function(){
//定義好分組的條件,以及每個集合要取出的內容
emit(this.sex,{"ccount":1,"cavg":this.salary,"cmax":this.salary,"cmin":this.salary,"cname":this.name});//emit表示分組處理函數
}
var sexReducnFun = function(key,values){
var total =0;//統計
var sum =0;//計算總工資
var max = values[0].cmax; //第一個數據是最高工資
var min = values[0].cmin;//第一個數據是最低工資
var names = new Array();//定義數組內容
for(var x in values){//表示循環取出里面的數據
total+= values[x].ccount;//特殊增加
sum+=values[x].csal;//就可以循環取出所有的工資,並且累加
if(max<values[x].cmax){//不是最高工資
max = values[x].cmax;
}
if(min>values[x].cmin){//不是最低工資
min = values[x].cmin;
}
name[x] = values[x].cname; //保存姓名

}
var avg = (sum/total).toFixed(2);//設置兩位小數
//返回數據的處理結果
return("count":total,"avg":avg,"sum":sum,"max":max,"min":min,"names":names);

};
db.runCommand({
"mapreduce”:"emps",
"map":sexMapFun,
"reduce":sexReduceFun,
"out","t_sex_emp"
});


雖然大數據的時代,提供有最強悍的MapReduce,但是從現實的開發中,真的不可能實現起來,很耗時。但是該功能可用定時任務來定期處理數據結果。

聚合框架:
MapReduce功能強大,但是它的復雜度和功能一樣強大,那么我們說很多時候,我們需要MapReduce功能,但是又不想把代碼寫的太復雜,所以從Mongo 2.x版本之后引入了聚合框架並且提供了聚合函數:aggregate()。

group操作
group主要進行分組的數據操作:
范例:實現聚合查詢的功能--求出每個職位的雇員人數
db.emps.aggregate(["$group":{"_id":"$job","job_count":{"$sum":1}}]);
這樣的操作,更加符合傳統的group by子句的操作使用。
范例:求出每個職位的總工資
db.emps.aggregate(["$group":{"_id":"$job","job_count":{"$sum":"$salary"}}]);
在整個聚合框架里面如果要使用每行的數據使用:"$字段名稱"
范例:計算出每個職位的平均工資
db.emps.aggregate(["$group":{"_id":"$job","job_sal":{"$sum":"$salary"},"job_avg":{"$avg":"$salary"}}]);
范例:求出最高與最低工資
db.emps.aggregate(["$group":{"_id":"$job","max_sal":{"$max":"$salary"},"min_sal":{"$min":"$salary"}}]);
以上的幾個與SQL類似的操作計算成功的實現了。
范例:計算出每個職位的工資數據(數組顯示)
db.emps.aggregate([{
"$group":{
"id":"$job",
"sal_data":{"$push":"$salary"}

}
}]);
范例:求出每個職位的人員:
db.emps.aggregate([{
"$group":{
"_id":"$job",
"sal_data":{"$push":"$name"}
}
}]);
以上使用"$push"的確可以將使用的數據變為數組進行保存,但是有一個問題出現了,重復的內容也會進行保存。在MongoDB里面提供有取消重復的設置。
范例:取消重復的數據
db.emps.aggregate([{
"$group":{
"id":"$job",
"addToSet":{"$push":"$name"}
}
}]);
默認情況下,是將所有的數據都保存進去,但是希望可以保存第一個和最后一個的數據。
范例:保存第一個內容
db.emps.aggregate([{
"$group":{
"id":"$job",
"addToSet":{"$first":"$name"}
}
}]);
范例:保存最后一個內容
db.emps.aggregate([{
"$group":{
"id":"$job",
"addToSet":{"$last":"$name"}
}
}]);
雖然可以實現分組處理,但是有一點需要注意,所有的分組數據是無序的,並且都是在內存之中完成,所以不可能支持大數據量。

$project
可以利用"$project"來顯示數據列的顯示規則,那么可以執行的規則如下:
-普通列({成員:1|true}):表示要顯示的內容
-"_id"列({"_id":0|false}):表示"_id"列的顯示是否顯示
-條件過濾列({成員:表達式}):滿足表達式之后的數據可以進行顯示
范例:只顯示name、job列
db.emps.aggregate({"$project":{"_id":0,"name":1}})
此時,只有設置進去的列才可以被顯示出來,而其他的列不能夠被顯示出來。實際上這就屬於數據庫的投影機制。
實際上在進行數據投影的過程里面也支持四則運算:加法("$add")、減法("$subtract")、乘法("$multiply")、除法("$divide")、求模("$mod")
范例:觀察四則運算
db.emps.aggregate([{"$project":{
"_id":0,
"name":1
"job":1,
"salary":{"年薪":{"$multiply":["$salary",12]}}
}}])
除了四則運算之外,也支持如下的各種運算fu :
-關系運算:大小比較("$cmp")、等於("$eq")、大於("$gt")、大於等於("$gte")、小於“$lt”、小於等於"$lte"、不等於"$ne"、判斷NULL("$ifNull"),這些操作返回的結果都是布爾型數據。
-邏輯運算:與"$and"、或"$or"、非"$not"
-字符串操作:連接"$concat"、截取"$substr"、轉小寫"$ToLower"、轉大寫"$ToUpper"、不區分大小寫比較"$strcasecmp"
范例:找出所有工資大於等於2000的雇員的姓名、年齡、工資
db.emps.aggregate([{"$project":{
"_id":0,
"name":1,
"job":1,
"工資":"$salary",
"salary":{"$gte":["$salary",2000]}
}}])
范例:查詢職位是manager的信息
-MongoDB的數據是區分大小寫的:
db.emps.aggregate([{"$project":{
"_id":0,
"name":1,
"職位":"$job",
"工資":"$salary",
"job":{"$eq":["$job",{"$toUpper":"manager"}]}
}}])
范例:使用字符串截取
db.emps.aggregate([{"$project":{
"_id":0,
"name":1,
"職位":"$job",
"job":{"前面三位":{"$substr":["$job",0,3]}}
}}])
利用"$project"實現的投影操作功能相當強大,所有可以出現的操作幾乎都能夠使用。

$sort
使用"$sort"可以實現排序,設置1表示升序,設置-1表示降序。
范例:實現排序
db.emps.aggregate([{"sort":{"age":-1,"salary":1}}])

分頁處理:$limit $skip
$limit:負責數據的取出個數
$skip:數據的跨過個數

$unwind
在查詢數據的時候經常會返回數組信息,但是數組並不方便信息的瀏覽,所以提供有"$unwind"可以將數組數據變為獨立的字符串內容。

$geoNear
使用"$geoNear"可以得到附近的坐標點

$out
"$out":利用此操作可以將查詢結果輸出到指定的集合里面。
范例:將投影的結果輸出到集合之中
db.emps.aggregate([{
"$project":{"_id":0,"name":1,"salary":1,"job":1}},
{"$out":"emp_infos"}
])
這類的操作相當於實現最早的數據表的復制操作。

固定集合:
所謂的固定集合指的是規定集合大小,如果要保存的內容已經操作了集合的長度,那么會采用LRU的算法(最近最少使用原則)將最早的數據移出,從而保存新的數據。
默認情況下一個集合可以使用createCollection()函數創建,或者使用增加數據后自動創建,如果使用固定集合,必須明確的創建一個空集合。
范例:創建一個空集合(固定集合)
db.createCollection("depts",{"capped":true,"size":1024,"max":5})
其中,"capped":為一個固定集合,而"size:1024"指的是集合所占的空間容量(字節),"max:5"表示最多只能有5條記錄文檔。
范例:向集合里面保存5條數據。
db.depts.insert({"deptno":10,"dename":"財務部-A","loc":"北京"})
db.depts.insert({"deptno":11,"dename":"財務部-A","loc":"北京"})
db.depts.insert({"deptno":12,"dename":"財務部-A","loc":"北京"})
db.depts.insert({"deptno":13,"dename":"財務部-A","loc":"北京"})
db.depts.insert({"deptno":14,"dename":"財務部-A","loc":"北京"})
此時已經達到集合的上限,那么繼續保存新的內容。
db.depts.insert({"deptno":15,"dename":"財務部-F","loc":"北京"})
此時,最早保留的數據已經消失了。實際上,這種操作跟緩存機制非常相似的,例如:在百度上經常會出現搜索的關鍵詞,這些詞都是會被不斷替換的。

GridFS
在MongoDB里面支持大數據的存儲(例如:圖片、音樂、各種二進制數據),但是這個做法需要用戶自己處理了。使用"mongofiles"完成。
1、利用命令行進入到所在的路徑下
2、將文件保存到文件庫之中:
mongofiles --port=27001 put photo.tif
此時,會向數據庫里面寫入要保存的二進制數據。
3、查看保存的文件
mongofiles --port=27001 list
4、在MongoDB里面有fs系統的集合,這個集合默認保存在test集合的數據庫中
use test;
db.fs.files.find();
5、刪除文件
mongofiles --prot=27001 delete photo.tif
等於在MongoDB里面支持二進制數據的保存,但是存在的意義不大。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM