玩轉(zhuǎn)MongoDB:基礎(chǔ)雜萃

圖片來(lái)自Google搜索

0 由于工作需要咽袜,最近開(kāi)始接觸MongoDB妨马,對(duì)于一個(gè)已經(jīng)習(xí)慣了傳統(tǒng)SQL的程序員來(lái)說(shuō),進(jìn)入NoSQL可以說(shuō)是需要莫大的勇氣的弱恒。最大的不適應(yīng)辨萍,是NoSQL和SQL的存儲(chǔ)原理與思維方式的不一致。當(dāng)然返弹,筆者也是NoSQL中的菜鳥(niǎo)锈玉,在這里分享一下自己的一些學(xué)習(xí)總結(jié)與心得。

1义起、數(shù)組

在MongoDB中拉背,文檔(Document)即表示數(shù)據(jù)庫(kù)中的一個(gè)集合中的一條記錄,相當(dāng)于關(guān)系型數(shù)據(jù)庫(kù)中的行(row)默终。在MongoDB中椅棺,數(shù)組是使用JSON語(yǔ)法表示的,在MongoDB中齐蔽,也稱為BSON格式两疚。數(shù)組既可以作為有序?qū)ο髞?lái)操作,也可以作為無(wú)序?qū)ο髞?lái)操作含滴。有序?qū)ο蟊热缌斜淼扔詹常瑹o(wú)序?qū)ο蟊热缂系鹊取?/p>

{
  "a" : 0,               //這明顯不是一個(gè)數(shù)組
  "b" : [],              //定義一個(gè)空的數(shù)組 
  "c" : [ "1","2","3"]   //包含3個(gè)元素的數(shù)組
}
2、文檔嵌套

在NoSQL中蛙吏,文檔與文檔之間是可以隨意嵌套的源哩,比如某個(gè)字段的值為某個(gè)類(lèi)型的對(duì)象。理論上鸦做,MongoDB支持無(wú)限級(jí)的自我嵌套励烦,比如:

{
  "a": {"b":1,"c":2}
}
3、文檔標(biāo)識(shí)

如果標(biāo)記當(dāng)前這個(gè)文檔泼诱,按照關(guān)系型數(shù)據(jù)庫(kù)的習(xí)慣坛掠,則可以說(shuō),如果保證一條記錄在數(shù)據(jù)庫(kù)里是唯一的治筒,在傳統(tǒng)SQL中屉栓,使用主鍵ID來(lái)標(biāo)識(shí)記錄的全局唯一性,而在MongoDB中耸袜,則使用ObjectId來(lái)確保文檔的唯一標(biāo)識(shí)友多,即_id的默認(rèn)類(lèi)型。
ObjectID是一個(gè)12字節(jié)的BSON數(shù)據(jù)類(lèi)型堤框,格式如下:

  1. 前四個(gè)字節(jié)表示時(shí)間戳
  2. 接下來(lái)三個(gè)字節(jié)是機(jī)器標(biāo)識(shí)碼
  3. 緊接的兩個(gè)字節(jié)由進(jìn)程ID組成域滥,即PID
  4. 最后三個(gè)字節(jié)是隨機(jī)數(shù)

MongoDB中的每個(gè)文檔必須有一個(gè)名為_id的鍵纵柿,可以是任意類(lèi)型,默認(rèn)為ObjectID類(lèi)型启绰。

以下是在Shell中關(guān)于ObjectId的一些方法

#生成一個(gè)新的ObjectId
newObjectId = ObjectId()

#返回的id為:ObjectId("5349b4ddd2781d08c09890f3")

#獲取文檔
ObjectId("5349b4ddd2781d08c09890f3").getTimestamp()

#返回時(shí)間為:ISODate("2016-07-06T21:49:17Z")

#將ObjectId轉(zhuǎn)化為字符串
new ObjectId().str

#返回結(jié)果為:5349b4ddd2781d08c09890f3
3昂儒、添加文檔

插入文檔使用db.col.insert(document)

db.Collection_Name.insert({
  "name":"demo"
});
  1. 如果文檔不包含_id鍵委可,MongoDB會(huì)自動(dòng)創(chuàng)建一個(gè)ObjectId類(lèi)型的_id值
  2. 默認(rèn)情況下插入操作時(shí)渊跋,MongoDB只檢查傳入數(shù)據(jù)是否包含_id以及數(shù)據(jù)大小是否超過(guò)16MB,所以可以得到更高的性能插入着倾,但同時(shí)也可能錄入無(wú)效數(shù)據(jù)拾酝。
  3. 因?yàn)樵诓迦霑r(shí)是不執(zhí)行任何代碼的,所以與傳統(tǒng)SQL相比屈呕,MongoDB不存在SQL注入風(fēng)險(xiǎn)微宝。
4、刪除文檔
db.Collection_Name.remove();   //清空集合內(nèi)的所有文檔
db.Collection_Name.remove(    //清空指定文檔
  {
     "a":"a"
  }
);

//當(dāng)集合內(nèi)數(shù)據(jù)過(guò)多時(shí)虎眨,可以考慮下面這個(gè)方法

db.drop_collection(Collection_Name);  //直接刪除集合
db.Collection_Name.ensureIndex();      //重建索引

5、更新文檔
//update語(yǔ)法定義
db.Collection_Name.update(query,document,upsert,multi);

//設(shè)定原文檔為:
var data = {_id:"xxxx","a":1,"b":2};

//query 是指查詢條件镶摘,相當(dāng)于SQL中的where子句嗽桩,比如:
db.Collection_Name.update(
  {_id:"xxxx","a":1,"b":2},       //定位條件,對(duì)符合_id=xxxx凄敢,a=1碌冶,b=2的文檔進(jìn)行更新
  {"a":2},                        //將a的值改為2,替換整個(gè)文檔
  true,                           //若查詢不到符合條件的文檔涝缝,則新增一個(gè)文檔
  true                            //允許更新多行
);

  1. update操作會(huì)替換整個(gè)匹配的文檔扑庞。而不是進(jìn)行某些特定字段的修改。如果需要更新某個(gè)特定字段值拒逮,則應(yīng)當(dāng)使用修改器罐氨。
  1. upsert模式是一個(gè)布爾值選項(xiàng),表示是否文檔更新時(shí)滩援,如果不存在栅隐,能夠自動(dòng)創(chuàng)建。
  2. multi模式也是一個(gè)布爾值選項(xiàng)玩徊,默認(rèn)情況下只更新匹配到的第一個(gè)文檔租悄,開(kāi)啟了multi模式后(即設(shè)置為true),則會(huì)更新所有匹配的文檔。
//更新文檔使用到的一些修改器恩袱,由$符號(hào)定義

//$inc 增加或減少數(shù)字的值泣棋,鍵不存在時(shí)自動(dòng)創(chuàng)建
db.Collection_Name.update(
  {"name" : "翹著二郎腿打代碼"},
  {"$inc" : { "lover" : 1 }}               //只將lover字段的值加1
);

//$set 設(shè)置某一項(xiàng)或者多個(gè)項(xiàng)目的值
db.Collection_Name.update(
  {"name" : "翹著二郎腿打代碼"},
  {"$set" : {"name" : "打代碼" }}
);

這里列舉一些常用的修改器

$inc      設(shè)置自增或者自減
$set      設(shè)置指定鍵的值
$unset    $set的反操作,會(huì)刪除鍵及鍵值
$push     將元素追加到數(shù)組末尾畔塔,數(shù)組不存在則自動(dòng)創(chuàng)建
$pushAll  $push的批量操作版本
$addToSet 與$push一樣潭辈,會(huì)自動(dòng)過(guò)濾重復(fù)元素
$pop      從數(shù)組中移除元素,1代表從末尾移除纪吮,-1代碼從開(kāi)頭移除
$pull     從數(shù)組中移除所有匹配的元素
$pullAll  $pull的批量操作版本
$rename   修改制定鍵的鍵名
$bit      對(duì)整型鍵進(jìn)行位操作

另外,還有一種方式可以實(shí)現(xiàn)文檔的更新:

//使用findAndModify()更新文檔
db.Collection_Name.findAndModify(
  {
      'query' : {"name" : "翹著二郎腿打代碼" },
      'update' : {"$set" : { "favour" : 100 } },
      'new' : true
  }
);

/**
其中的參數(shù)如下:

query   :  查詢條件萎胰,用來(lái)定位到匹配的文檔
sort    :  如果匹配到多個(gè)文檔碾盟,指定一個(gè)排序方式,-1降序技竟,1升序
remove  :  是否刪除匹配的文檔
new     :  是否返回更新后的文檔
update  :  更新操作
upsert  :  是否自動(dòng)創(chuàng)建冰肴,如果匹配不到文檔

**/

save()

//文檔不存在時(shí),執(zhí)行insert操作榔组,存在是執(zhí)行update操作熙尉。
db.demo.save(document);
6鸟廓、查詢

我們先來(lái)舉一些簡(jiǎn)單的例子:

//select * from demo;
db.demo.find();

//select * from demo where a = 1;
db.demo.find({"a":1});

//select a,b from demo where a = 1;
db.demo.find({"a":1},{a:1,b:1});

//select * from demo where a = 1 order by name asc;
db.demo.find({"a":1}).sort({"name":1});

//select * from demo where a > 1;
db.demo.find({"a":{$gt:1}});

//select * from demo where a like 'eee';
db.demo.find({"name":"/^eee/"});

//select * from demo limit 10 skip 20;
db.demo.find().limit(10).skip(20);

當(dāng)然還有很多其他的語(yǔ)法形式驶沼,這里不再一一列舉。下面列舉一些常見(jiàn)的查詢條件操作符

$lt    #小于
$lte   #小于等于
$gt    #大于
$gte   #大于等于
$all   #完全匹配
$mod   #取模
$ne    #不等于
$in    #在...內(nèi)
$nin   #不在...內(nèi)
$nor   #既不...也不...
$or    #或
$size  #匹配數(shù)組長(zhǎng)度
$type  #匹配數(shù)據(jù)類(lèi)型

slice()函數(shù)用于數(shù)組的查詢

db.demo.find({},{favours:{'$slice':1}});     //僅返回?cái)?shù)組中的前1項(xiàng)
db.demo.find({},{favours:{'$slice':-1}});    //僅返回?cái)?shù)組中的最后一項(xiàng)
db.demo.find({},{favours:{'$slice':[1,2]}}); //跳過(guò)前1項(xiàng)苇经,返回接下來(lái)的10項(xiàng)
db.demo.find({},{favours:{'$slice':[-1,1]}});//跳過(guò)最后一項(xiàng)锨推,返回接下來(lái)的1項(xiàng)
7铅歼、游標(biāo)

MongoDB中的游標(biāo)已經(jīng)在各個(gè)版本的驅(qū)動(dòng)程序中封裝好了,不需要像傳統(tǒng)SQL那樣使用PL/SQL結(jié)構(gòu)化編程來(lái)聲明游標(biāo)换可,在Shell中椎椰,游標(biāo)的使用方式與Java中的迭代器十分相似。

var cursor = db.demo.find();  //聲明游標(biāo)
while(cursor.hasNext()){      //遍歷集合
  var element = cursor.next();
}
8沾鳄、$WHERE

$where操作符也是用來(lái)定位查詢的慨飘,這個(gè)SQL中的where非常類(lèi)似,前面我們說(shuō)過(guò)译荞,匹配文檔的時(shí)候可以使用各種查詢條件操作符來(lái)實(shí)現(xiàn)瓤的,但是為什么還要有這個(gè)操作符呢?因?yàn)橛行┎樵兪菬o(wú)法通過(guò)之前講過(guò) 的那些查詢操作符來(lái)實(shí)現(xiàn)的吞歼。值得注意的是圈膏,$where操作符的性能低,沒(méi)有使用也無(wú)法使用索引機(jī)制浆熔。

//傳統(tǒng)SQL
select * from demo where a > 1;

//使用查詢操作符實(shí)現(xiàn)
db.demo.find({a:{"$gt":1}});

//使用$where實(shí)現(xiàn)
db.demo.find({"$where":"this.a > 1"});
db.demo.find("this.a > 1");
db.demo.find(function(){
  return this.a > 1;
});
9本辐、 排序&分頁(yè)

MongoDB中提供了相關(guān)的方法進(jìn)行排序和分頁(yè),主要有limit(),skip()sort()

//每頁(yè)10條記錄医增,略過(guò)前面10條記錄慎皱,按a降序排序
db.demo.find().limit(10).skip(10).sort({a:-1});
10、索引

MongoDB的索引機(jī)制與傳統(tǒng)SQL的索引基本上是一樣的叶骨。

//創(chuàng)建索引
db.demo.ensureIndex({'a':1});

//創(chuàng)建子文檔索引
db.demo.ensureIndex({'a.b':-1});

//創(chuàng)建復(fù)合索引
db.demo.ensureIndex({
   "a":1,"b":-1
});

//在MongoDB中茫多,1表示升序,-1表示降序

//重新索引忽刽,一般是修改索引后重新索引操作
db.demo.reIndex();

//刪除索引
db.demo.dropIndexes();
11天揖、聚合

count()

//select count(a) from demo where a = 1;
db.demo.count({"a":1});

distinct()

db.demo.distinct("zip-code",{a:1});

group(key,cond,reduce,initial)
其中夺欲,
key :分組依據(jù)
cond: 查詢條件
reduce:聚合操作
initial : 指定聚合計(jì)數(shù)器的初始對(duì)象

//sql表示
select a,b,sum(c) from demo where a = 1 group by a,b;

//MongoDB表示
db.demo.gourp({
  "key":{
     "a": true,
     "b": true
  },
  "cond":{
     "a":1
  }
});

12、歸納一下MongoDB中的一些Tips

1今膊、MongoDB與傳統(tǒng)SQL的顯著區(qū)別

SQL MongoDB
表(Table) 集合(Collection)
行(row) 文檔(document)

2些阅、集合不能以system.開(kāi)頭, 這是因?yàn)镸ongoDB中的系統(tǒng)集合保持的前綴斑唬。比如

db.system.update(document);  //錯(cuò)誤

3市埋、 ObjectId類(lèi)型是_id的默認(rèn)類(lèi)型,也可以自己指定其數(shù)據(jù)類(lèi)型恕刘。MongoDB的初衷是設(shè)計(jì)成一個(gè)分布式的數(shù)據(jù)庫(kù)缤谎,所以不會(huì)自動(dòng)實(shí)現(xiàn)_id的自增。插入文檔時(shí)褐着,如果沒(méi)有指定_id的值坷澡,則系統(tǒng)自動(dòng)創(chuàng)建一個(gè)ObjectId類(lèi)型的值,一般在客戶端的驅(qū)動(dòng)程序中完成含蓉。


4频敛、插入文檔時(shí),MongoDB會(huì)解析BSON數(shù)據(jù)谴餐,BSON數(shù)據(jù)格式與JSON基本一致姻政,在MongoDB中稱為BSON。插入時(shí)會(huì)檢查是否包含_id以及檢查文檔數(shù)據(jù)是否超過(guò)16MB,其余全部不作檢查岂嗓,從而實(shí)現(xiàn)其高效率性。


5鹊碍、MongoDB在插入數(shù)據(jù)時(shí)厌殉,不會(huì)執(zhí)行插入數(shù)據(jù)的代碼,而是將BSON數(shù)據(jù)直接寫(xiě)入侈咕,不作任何的數(shù)據(jù)驗(yàn)證公罕,所以不存在類(lèi)似于SQL中的注入風(fēng)險(xiǎn)。


說(shuō)明

本篇文章也是筆者自己在學(xué)習(xí)過(guò)后總結(jié)出來(lái)的耀销。也是針對(duì)習(xí)慣于傳統(tǒng)SQL的簡(jiǎn)友們寫(xiě)的楼眷,傳統(tǒng)的SQL數(shù)據(jù)庫(kù)與NoSQL個(gè)人感覺(jué)差別還算是挺大的。剛開(kāi)始接觸NoSQL的時(shí)候最大的不適應(yīng)就是在MongoDB中是不需要設(shè)計(jì)表和表結(jié)構(gòu)的熊尉,全是基于JSON的數(shù)據(jù)操作罐柳,所以其邏輯原理都在程序代碼中實(shí)現(xiàn),而MongoDB本身只負(fù)責(zé)分布式的數(shù)據(jù)存儲(chǔ)狰住。


文章更新日志
2016-07-08 文章初稿
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末张吉,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子催植,更是在濱河造成了極大的恐慌肮蛹,老刑警劉巖勺择,帶你破解...
    沈念sama閱讀 221,695評(píng)論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異伦忠,居然都是意外死亡省核,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,569評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門(mén)昆码,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)气忠,“玉大人,你說(shuō)我怎么就攤上這事未桥”噬玻” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 168,130評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵冬耿,是天一觀的道長(zhǎng)舌菜。 經(jīng)常有香客問(wèn)我,道長(zhǎng)亦镶,這世上最難降的妖魔是什么日月? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 59,648評(píng)論 1 297
  • 正文 為了忘掉前任,我火速辦了婚禮缤骨,結(jié)果婚禮上爱咬,老公的妹妹穿的比我還像新娘。我一直安慰自己绊起,他們只是感情好精拟,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,655評(píng)論 6 397
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著虱歪,像睡著了一般蜂绎。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上笋鄙,一...
    開(kāi)封第一講書(shū)人閱讀 52,268評(píng)論 1 309
  • 那天师枣,我揣著相機(jī)與錄音,去河邊找鬼萧落。 笑死践美,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的找岖。 我是一名探鬼主播陨倡,決...
    沈念sama閱讀 40,835評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼宣增!你這毒婦竟也來(lái)了玫膀?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 39,740評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤爹脾,失蹤者是張志新(化名)和其女友劉穎帖旨,沒(méi)想到半個(gè)月后箕昭,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,286評(píng)論 1 318
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡解阅,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,375評(píng)論 3 340
  • 正文 我和宋清朗相戀三年落竹,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片货抄。...
    茶點(diǎn)故事閱讀 40,505評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡述召,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出蟹地,到底是詐尸還是另有隱情积暖,我是刑警寧澤,帶...
    沈念sama閱讀 36,185評(píng)論 5 350
  • 正文 年R本政府宣布怪与,位于F島的核電站夺刑,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏分别。R本人自食惡果不足惜遍愿,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,873評(píng)論 3 333
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望耘斩。 院中可真熱鬧沼填,春花似錦、人聲如沸括授。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 32,357評(píng)論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)荚虚。三九已至羞海,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間曲管,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,466評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工硕糊, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留院水,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,921評(píng)論 3 376
  • 正文 我出身青樓简十,卻偏偏與公主長(zhǎng)得像檬某,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子螟蝙,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,515評(píng)論 2 359

推薦閱讀更多精彩內(nèi)容