玩轉MongoDB:基礎雜萃

圖片來自Google搜索

0 由于工作需要,最近開始接觸MongoDB,對于一個已經習慣了傳統SQL的程序員來說,進入NoSQL可以說是需要莫大的勇氣的。最大的不適應,是NoSQL和SQL的存儲原理與思維方式的不一致。當然,筆者也是NoSQL中的菜鳥,在這里分享一下自己的一些學習總結與心得。

1、數組

在MongoDB中,文檔(Document)即表示數據庫中的一個集合中的一條記錄,相當于關系型數據庫中的行(row)。在MongoDB中,數組是使用JSON語法表示的,在MongoDB中,也稱為BSON格式。數組既可以作為有序對象來操作,也可以作為無序對象來操作。有序對象比如列表等,無序對象比如集合等等。

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

在NoSQL中,文檔與文檔之間是可以隨意嵌套的,比如某個字段的值為某個類型的對象。理論上,MongoDB支持無限級的自我嵌套,比如:

{
  "a": {"b":1,"c":2}
}
3、文檔標識

如果標記當前這個文檔,按照關系型數據庫的習慣,則可以說,如果保證一條記錄在數據庫里是唯一的,在傳統SQL中,使用主鍵ID來標識記錄的全局唯一性,而在MongoDB中,則使用ObjectId來確保文檔的唯一標識,即_id的默認類型。
ObjectID是一個12字節的BSON數據類型,格式如下:

  1. 前四個字節表示時間戳
  2. 接下來三個字節是機器標識碼
  3. 緊接的兩個字節由進程ID組成,即PID
  4. 最后三個字節是隨機數

MongoDB中的每個文檔必須有一個名為_id的鍵,可以是任意類型,默認為ObjectID類型。

以下是在Shell中關于ObjectId的一些方法

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

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

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

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

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

#返回結果為:5349b4ddd2781d08c09890f3
3、添加文檔

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

db.Collection_Name.insert({
  "name":"demo"
});
  1. 如果文檔不包含_id鍵,MongoDB會自動創建一個ObjectId類型的_id值
  2. 默認情況下插入操作時,MongoDB只檢查傳入數據是否包含_id以及數據大小是否超過16MB,所以可以得到更高的性能插入,但同時也可能錄入無效數據。
  3. 因為在插入時是不執行任何代碼的,所以與傳統SQL相比,MongoDB不存在SQL注入風險。
4、刪除文檔
db.Collection_Name.remove();   //清空集合內的所有文檔
db.Collection_Name.remove(    //清空指定文檔
  {
     "a":"a"
  }
);

//當集合內數據過多時,可以考慮下面這個方法

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

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

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

//query 是指查詢條件,相當于SQL中的where子句,比如:
db.Collection_Name.update(
  {_id:"xxxx","a":1,"b":2},       //定位條件,對符合_id=xxxx,a=1,b=2的文檔進行更新
  {"a":2},                        //將a的值改為2,替換整個文檔
  true,                           //若查詢不到符合條件的文檔,則新增一個文檔
  true                            //允許更新多行
);

  1. update操作會替換整個匹配的文檔。而不是進行某些特定字段的修改。如果需要更新某個特定字段值,則應當使用修改器。
  1. upsert模式是一個布爾值選項,表示是否文檔更新時,如果不存在,能夠自動創建。
  2. multi模式也是一個布爾值選項,默認情況下只更新匹配到的第一個文檔,開啟了multi模式后(即設置為true),則會更新所有匹配的文檔。
//更新文檔使用到的一些修改器,由$符號定義

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

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

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

$inc      設置自增或者自減
$set      設置指定鍵的值
$unset    $set的反操作,會刪除鍵及鍵值
$push     將元素追加到數組末尾,數組不存在則自動創建
$pushAll  $push的批量操作版本
$addToSet 與$push一樣,會自動過濾重復元素
$pop      從數組中移除元素,1代表從末尾移除,-1代碼從開頭移除
$pull     從數組中移除所有匹配的元素
$pullAll  $pull的批量操作版本
$rename   修改制定鍵的鍵名
$bit      對整型鍵進行位操作

另外,還有一種方式可以實現文檔的更新:

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

/**
其中的參數如下:

query   :  查詢條件,用來定位到匹配的文檔
sort    :  如果匹配到多個文檔,指定一個排序方式,-1降序,1升序
remove  :  是否刪除匹配的文檔
new     :  是否返回更新后的文檔
update  :  更新操作
upsert  :  是否自動創建,如果匹配不到文檔

**/

save()

//文檔不存在時,執行insert操作,存在是執行update操作。
db.demo.save(document);
6、查詢

我們先來舉一些簡單的例子:

//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);

當然還有很多其他的語法形式,這里不再一一列舉。下面列舉一些常見的查詢條件操作符

$lt    #小于
$lte   #小于等于
$gt    #大于
$gte   #大于等于
$all   #完全匹配
$mod   #取模
$ne    #不等于
$in    #在...內
$nin   #不在...內
$nor   #既不...也不...
$or    #或
$size  #匹配數組長度
$type  #匹配數據類型

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

db.demo.find({},{favours:{'$slice':1}});     //僅返回數組中的前1項
db.demo.find({},{favours:{'$slice':-1}});    //僅返回數組中的最后一項
db.demo.find({},{favours:{'$slice':[1,2]}}); //跳過前1項,返回接下來的10項
db.demo.find({},{favours:{'$slice':[-1,1]}});//跳過最后一項,返回接下來的1項
7、游標

MongoDB中的游標已經在各個版本的驅動程序中封裝好了,不需要像傳統SQL那樣使用PL/SQL結構化編程來聲明游標,在Shell中,游標的使用方式與Java中的迭代器十分相似。

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

$where操作符也是用來定位查詢的,這個SQL中的where非常類似,前面我們說過,匹配文檔的時候可以使用各種查詢條件操作符來實現,但是為什么還要有這個操作符呢?因為有些查詢是無法通過之前講過 的那些查詢操作符來實現的。值得注意的是,$where操作符的性能低,沒有使用也無法使用索引機制。

//傳統SQL
select * from demo where a > 1;

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

//使用$where實現
db.demo.find({"$where":"this.a > 1"});
db.demo.find("this.a > 1");
db.demo.find(function(){
  return this.a > 1;
});
9、 排序&分頁

MongoDB中提供了相關的方法進行排序和分頁,主要有limit(),skip()sort()

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

MongoDB的索引機制與傳統SQL的索引基本上是一樣的。

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

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

//創建復合索引
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 :分組依據
cond: 查詢條件
reduce:聚合操作
initial : 指定聚合計數器的初始對象

//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與傳統SQL的顯著區別

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

2、集合不能以system.開頭, 這是因為MongoDB中的系統集合保持的前綴。比如

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

3、 ObjectId類型是_id的默認類型,也可以自己指定其數據類型。MongoDB的初衷是設計成一個分布式的數據庫,所以不會自動實現_id的自增。插入文檔時,如果沒有指定_id的值,則系統自動創建一個ObjectId類型的值,一般在客戶端的驅動程序中完成。


4、插入文檔時,MongoDB會解析BSON數據,BSON數據格式與JSON基本一致,在MongoDB中稱為BSON。插入時會檢查是否包含_id以及檢查文檔數據是否超過16MB,其余全部不作檢查,從而實現其高效率性。


5、MongoDB在插入數據時,不會執行插入數據的代碼,而是將BSON數據直接寫入,不作任何的數據驗證,所以不存在類似于SQL中的注入風險。


說明

本篇文章也是筆者自己在學習過后總結出來的。也是針對習慣于傳統SQL的簡友們寫的,傳統的SQL數據庫與NoSQL個人感覺差別還算是挺大的。剛開始接觸NoSQL的時候最大的不適應就是在MongoDB中是不需要設計表和表結構的,全是基于JSON的數據操作,所以其邏輯原理都在程序代碼中實現,而MongoDB本身只負責分布式的數據存儲。


文章更新日志
2016-07-08 文章初稿
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容