mongoDB讀書筆記

MongoDB

這幾天編寫程序,發(fā)現(xiàn)如果沒有理論的支持,即使時(shí)間花的再多,效率也是不高的,所以每天在編程之前都應(yīng)該先給自己充一下電。這次重新看了mongoDB權(quán)威開發(fā)指南的前四章,做了下面的讀書筆記。

  • mongoDB 是面向文檔的數(shù)據(jù)庫,不是關(guān)系型數(shù)據(jù)庫;
  • 文檔是MongoDb中數(shù)據(jù)的基本單元;
  • 每一個(gè)文檔都有一個(gè)特殊的鍵"_id",在文檔所處的集合中是唯一的??梢宰鳛槲臋n的唯一標(biāo)識符

文檔

文檔是MongoDB的核心概念。包括多個(gè)鍵和關(guān)聯(lián)的值,有序地存放在一起;

{"greeting": "Hello world!"};
  • 鍵: greeting;
  • 值: hello world;

多個(gè)鍵值對:

{
    "greeting": "hello world",  //字符串
    "foo": "2"  //整形
}

//另外的一個(gè)鍵值對
{
    "foo": "2",
    "greeting": "hello world"
}

文檔中的鍵/值對必須是有序的,上面兩個(gè)是不同的鍵值對

字符串作為鍵的要求:

  1. 鍵不能含有\0空字符,這個(gè)字符用來保存鍵的結(jié)尾
  2. .$有特別的含義,也不可以
  3. _開頭的鍵是保留的
  4. MongoDB 區(qū)分類型,也區(qū)分大小寫,不能有重復(fù)的鍵

集合是多個(gè)鍵值對的集合


mongoDB的使用

  • mongoDB在沒有參數(shù)的情況下會默認(rèn)數(shù)據(jù)目錄為/data/db
  • 默認(rèn)情況下,mongoDB監(jiān)聽27017端口
  • mongod還會啟動一個(gè)基本的http服務(wù)器,監(jiān)聽28017端口
  • 可以通過瀏覽器訪問http://localhost:28017來獲取數(shù)據(jù)庫的管理信息
  • shell下可以輸入ctrl+C來停止Mongd的運(yùn)行

MongoDB中存儲的文檔必須有一個(gè)_id值。

  • 這個(gè)值可以是任意類型的,默認(rèn)是ObjectId對象。
  • 在一個(gè)集合里面,每一個(gè)文檔都有一個(gè)唯一的_id值,來確保集合里面的每一個(gè)文檔都能被唯一標(biāo)識
  • ObjectID_id的默認(rèn)類型
  • 在插入文檔的時(shí)候沒有_id,系統(tǒng)會自動幫你創(chuàng)建一個(gè)。

強(qiáng)大的shell操作

  • insert添加一個(gè)文檔到集合里面
db.數(shù)據(jù)庫名.insert(自己定義好的一條數(shù)據(jù))
  • find查找數(shù)據(jù)庫,返回集合里面的所有文檔
db.數(shù)據(jù)庫名.find()
  • findOne(),返回?cái)?shù)據(jù)庫里面的一個(gè)文檔
db.數(shù)據(jù)庫名.findOne()
  • update()更改數(shù)據(jù)庫,接受至少兩個(gè)參數(shù),一個(gè)是更新的文檔限定條件,一個(gè)是新的文檔
db.blog.update({title:"my blog"}, post)
  • remove()刪除,沒有參數(shù)會刪除所有的文檔,一般接受一個(gè)條件
db.blog.remove({title:"my blog"})
  • 移除blog中所有opt-out為true的人
db.blog.remove({"opt-out": true});
  • 刪除數(shù)據(jù)是永久性的,不能撤銷也不能恢復(fù)

更改器的使用

  • $inc 修改器增加pageviews的值
//將name為1對應(yīng)的文檔中的pageview增加1
db.analytics.update({"name":"1"}, {"$inc": {"pageview": 1}});

注意:使用修改器不能修改_id的值。

  • $set修改器用來指定一個(gè)鍵的值,如果這個(gè)鍵不存在,那么就創(chuàng)建它;
//users表中username為why的文檔中的favoriteBook設(shè)置為c
db.users.update({"username": "why"}, {"$set": {"favoriteBook": "c"}});
  • $set甚至可以修改鍵的數(shù)據(jù)類型, $set還可以修改內(nèi)嵌的屬性
//將favoirteBook的鍵值設(shè)置為一個(gè)數(shù)組
db.users.update({"username": "why"},{"$set": {"favoriteBook": ["c","c++"]}});
  • $unset可以完全刪除字段
db.users.update({"username": "why"},{"$unset": {"favoriteBook": "c"}});
  • $inc可以累計(jì)一個(gè)屬性,如果不存在,那么會事先創(chuàng)建一個(gè)新的屬性
//可以自己給它創(chuàng)建一個(gè)score: 50的屬性
{"$inc": {"score": 50}}
//score+1
{"$inc": {"score": 1}}
//結(jié)果將變?yōu)閟core: 51;

$inc只能用來修改數(shù)字,如果想要改變其他類型的值,可以選擇用$set


數(shù)組的操作

  • $push給已有的數(shù)組末尾添加一個(gè)元素,
  • 要是沒有這個(gè)數(shù)組,會自動創(chuàng)建一個(gè)新的數(shù)組
  • 繼續(xù)添加元素,只需要再次使用$push
  • 如果一個(gè)值不再數(shù)組里面,那么先用$ne來創(chuàng)建一個(gè)新的字段,再把它push到這個(gè)數(shù)組里面
db.user.update({"username": "{"$ne": "WHY"}"}, {$push:{"username": "WHY"}})
  • 如果$ne行不通,可以直接使用$addToSet,這樣還可以避免重復(fù)
db.users.update({"username":"why"}, 
   {"$addToSet": {"emails":"qq.mail"}}
);

db.users.update({"username":"why"},
   {"$addToSet": {"emails":{"qq.mail","126.com"}} }
);
  • 將數(shù)組作為隊(duì)列或者是棧,可以使用$pop這個(gè)修改器

  • 從數(shù)組的任何一端刪除元素

  • {$pop: {key: 1}} 從數(shù)組末尾刪除一個(gè)元素

  • {$pop: {key: -1}} 從數(shù)組頭部刪除

  • $pull 會將數(shù)組中匹配的部分刪除掉

db.lists.insert({"todo": {"dishes": "dishes" ,"laundry","dry cleaning"}})

db.lists.update({}, {"$pull", "{"todo":"laundry"}");

db.list.find()
{
    "_id": ObjectId("XXXX"),
    "todo" : {
      "dishes",
      "dry cleaning"
    }
}

對于數(shù)組[1,1,2,1]執(zhí)行pull 1 ,那么他會刪掉重復(fù)的字段

  • 定位符$
//將原先author為tom的字段修改為why
db.blog.update({"comments.author": "tom"},
               {"$set": {"comments.$.author": "why"}});
  • $定位符之id那個(gè)匹配第一個(gè)匹配的元素。所以如果有多個(gè)評論人為tom的字段,只會修改第一個(gè)匹配的字段

  • $upsert

db.math.remove()
db.math.upsert({"count": 25}, {"$inc": {"count": 3}}, true);
db.math.findOne() {
    "id": ObjetcId(XXX);
    "count": 28
}

先清空了集合,然后里面就沒有文檔,
再用upsert創(chuàng)建一個(gè)count的值為25的文檔
然后將這個(gè)值加3,最后得到count為28的文檔。
如果沒有開啟upsert的選項(xiàng),{"count" : 25}不會匹配到任何的文檔,就不會有修改
再次運(yùn)行,由于沒有{"count": 25}的選項(xiàng),那么他會再次創(chuàng)建一個(gè)count為25的字段,
然后再次+3為28

  • $save是保存
db.users.save();

更新多個(gè)文檔

  • 默認(rèn)情況下,更新只能對符合匹配條件的第一個(gè)文檔執(zhí)行操作。
  • 要是有多個(gè)文檔符合條件,其余的文檔就沒有變化。
  • 要使得匹配到的文檔都得到更新,那么可以設(shè)置update的第四個(gè)參數(shù)為true
db.users.update({"birthday": "10/13/2016"},
{$set: {gift: {"happy birthday"}}, false, true});

如果想知道文檔到底更新了多少,可以運(yùn)行getLastError命令

db.runCommand({getLastError: 1});

{
    "err": null,
    "updateExisting": true,
    "n": 5,
    "ok": true
}
  • 這里的n=5就說明有5個(gè)文檔被更新了。
  • updateExisting: true說明是對已有的文檔進(jìn)行更新

getLastError只能獲取更新的信息,不能返回已經(jīng)更新的文檔,

我們可以通過findAndModify獲取更新好的文檔,缺點(diǎn)是有點(diǎn)慢,需要等待數(shù)據(jù)庫的響應(yīng)

db.runCommand({
    "findAndModify": "processes",
    "query": {},
    "sort": {},
    "update": {}
})
  • findAndModify: 字符串,集合的名字
  • query: 查詢文檔,用來查詢文檔的條件
  • sort: 排序的條件
  • update: 修改器文檔,對所有找到的文檔執(zhí)行更新
  • remove:布爾類型,表示是否刪除文檔
  • new: 布爾類型,表示返回的是更新前的文檔還是更新后的文檔,默認(rèn)是更新前的文檔。
  • update和remove必須有一個(gè),也只能有一個(gè),如果匹配不到文檔,那么則這個(gè)命令會返回一個(gè)錯(cuò)誤
  • 一次只能處理一個(gè)文檔,也不能執(zhí)行upsert操作,只能更新已有的文檔
  • 對于普通的更新來說,findAndModify速度比較慢,時(shí)間相當(dāng)一次查找,一次更新和一次getLastError

查詢

  • find查詢,查詢返回一個(gè)集合中文檔的子集,
    1. 子集的范圍是從0個(gè)文檔到整個(gè)集合
    2. find的第一個(gè)參數(shù)決定要返回哪些文檔,其形式也是一個(gè)文檔,說明要查詢的細(xì)節(jié)
    3. 空的查詢文檔, 會返回集合的全部內(nèi)容,如果不指定查詢文檔。默認(rèn)就是空。
/這樣會返回集合c中的全部內(nèi)容
db.users.find{}
  • 當(dāng)向查詢文檔中添加鍵值對時(shí),就以限定了查找的條件
    查找方式是:1. 整數(shù)匹配整數(shù),2. 布爾值匹配布爾值, 3. 字符串匹配字符串。
//查詢所有年齡為27歲的用戶
db.users.find({"age": 27});
//查詢username為joe的字段
db.users.find({"username": "joe"});
//這樣是多字段查詢,會返回username為joe,年齡為27的所以字段
db.users.find({"username": "joe", "age":27})

指定返回的鍵

  • 有時(shí)不需要將文檔中的所有鍵值對全部返回
  • 可以通過find()或者findOne()的第二個(gè)參數(shù)來指定想要的鍵
  • 這樣可以節(jié)省傳輸?shù)臄?shù)據(jù)量,也可以節(jié)省客戶端解碼文檔的時(shí)間和內(nèi)存消耗
db.users.find({}, {"username": 1, "email": 1})
  • 返回找到字段中的usernameemail
  • 還有一個(gè)是_id,這個(gè)鍵總是被返回,即使沒有指定_id顯示也是一樣
  • 也可以通過第二個(gè)參數(shù)來剔除查詢結(jié)果中的某個(gè)鍵值對
db.users.find({}, {"password": 0});`

這樣返回的字段中就不會出現(xiàn)password這個(gè)鍵值對

查詢條件

  • $lt,$lte,$gt,$gte,是全部的比較操作符,分別對應(yīng)<, <=,>, >=
  • 可以將它們組合起來查詢一個(gè)范圍的值
//查詢年齡是18-30歲(含)的所有用戶
db.users.find({"age": {"$gte": 18, "$lte": 30}})
//可以查詢在現(xiàn)在這個(gè)時(shí)間之前注冊過的用戶
start = new Date();
db.users.find({"registerDate": {"$lt": start}})
  • $ne表示不等
//找到名字不是joe的用戶
db.users.find({"username": {"$ne":"joe"}})

$ne可以用于所有類型的數(shù)據(jù)

OR查詢

mongoDB有兩種方式進(jìn)行OR查詢,$in可以查詢一個(gè)鍵的多個(gè)值,
$or可以用來完成多個(gè)鍵值對的任意給定值(更加通用)

db.users.find({"username": {"$in":["why","joe"]}})

這回匹配usernamewhy的文檔,也會匹配usernamejoe的文檔

如果$in中對于的數(shù)組只有一個(gè)值,那么這和直接匹配這個(gè)值得效果是一樣的

{ticket_no: {$in:[125]}}和{ticket_no: 125}是一樣的

與$in相反的是$nin,將返回與數(shù)組中所有條件都不匹配的文檔

db.users.find({"username": {"$nin":["why","joe"]}})

返回username既不是why,也不是joe的user

$in只能對單個(gè)鍵做OR查詢,而$or可以查詢包含所有可能條件的參數(shù)作為數(shù)組

db.raffie.find({"$or": [{"ticket_no": 125}, {"winner": true}]})

這樣會返回ticket_no"是125,winner是true的所有字段

$or還可以含有其他條件語句

db.raffie.find({"$or": [
   {"ticket_no": ["$in":[123,124,125]]},
   {"winner": true}
]})

條件句的規(guī)則

在查詢中,$lt在內(nèi)層文檔,而更新中$inc是外層文檔的鍵
條件句是內(nèi)層文檔的鍵,而修改器是外層文檔的鍵
一個(gè)鍵可以有多個(gè)條件,但是一個(gè)鍵不能對于多個(gè)更新更改器

//正確
db.users.find({"age": {"lt": 30, "$gt": 20})
//錯(cuò)誤
db.user.find({"$inc": {"age":1}, "$set": {age:40}})

null比較特殊,不僅僅匹配自身,而且還匹配不存在,所以我們在匹配鍵值為null的文檔的同時(shí),還要檢查該建筑是否存在

db.c.find({"z": {"$in":[null], "$exists": true}});

沒有$eq操作符,我們使用$in操作符代替

正則表達(dá)式

匹配名為Joe或者joe的用戶,可以用正則表達(dá)式匹配大小寫

db.users.find({"name":/joe/i})

正則表達(dá)式還可以插入到數(shù)據(jù)庫,自身也可以匹配

db.foo.insert({"bar": /baba/})
db.foo.find("bar": /baba/)
{
    "_id": ObjectId("XXXXX"),
    "bar": /baba/
}

查詢數(shù)組

  • 插入一個(gè)數(shù)組
db.food.insert({"fruit":["apple", "banana"]})
db.food.find({"fruit":"banana"})
//找得到,但是比較低效
  • $all 如果需要多個(gè)元素來匹配數(shù)組,那么就需要用到$all,這樣會匹配一組元素
db.find({"fruit": {$all: ["apple"]})
  • $size 可以用來查詢指定長度的數(shù)組
db.food.({"fruit": {"$size":3}})
  • $slice find的第二個(gè)參數(shù)是可選的,可以返回那些鍵,"$slice"返回的是數(shù)組的一個(gè)子集合
//返回的是前10條評論
db.blog.posts.findOne(criteria, {"comments": {"$slice": 10}})

//-10表示的是后10條評論
db.blog.posts.findOne(criteria, {"comments": {"$slice": -10}})

//這個(gè)操作會跳過前面的前23個(gè)元素,返回第24個(gè)到第33個(gè)元素。
//如果數(shù)組不夠33個(gè)元素,那么會返回第23個(gè)元素后面的全部元素
db.blog.posts.findOne(criteria, {"comments": {"$slice": [23,10]}})
  • limit()限制查詢的結(jié)果數(shù)量
//只返回3個(gè)結(jié)果
如果返回的結(jié)果不足3個(gè),那么返回匹配數(shù)量的結(jié)果。limit是上限而不是下線
db.c.find().limit(3);

//skip與limit類似
db.c.find().skip(3)
  • sort是用一個(gè)對象作為參數(shù):
    一組鍵值對,鍵對應(yīng)文檔的鍵名,值代表查詢的方向,排序方向可以是1升序-1降序 。 如果指定了多個(gè)鍵,那么按照鍵的順序逐個(gè)進(jìn)行進(jìn)行排序
db.c.find().sort({username: 1, age: -1})

簡單的分頁, 按照date的降序顯示文檔

var page1 = db.foo.find(cirterial).limit(100)
var latest = null;
while(page1.hasNext()) {
    latest = page1.next();
    display(latest);
}
//get next page
var page2 = db.foo.find({"date": {"$gt": latest.date}});
page2.sort({"date": -1}).limit(100);

唯一索引

db.people.ensureIndex({"username": 1}, {"unique": true});

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容