前言

最近在用mongodb數據庫的時候,在運用在一些實際場景時,發現一時間沒有關系型數據庫這么習慣,官方的文檔也顯得很散亂不具體,為此我還去查詢了很多相關資料,因此收穫頗多,感覺是官方文檔裡面都沒有仔細提及的知識點,因此作此文總結一番。

sql語句

假設有一個數據庫集合結構為如下:

const BookSchema = new mongoose.Schema({
  name: String,
  author: String,
  price: String,
  chapter:{type:[Object],default:[]}
})

var Book = mongoose.model('Book', BookSchema);
const _book = new Book({
  name: '老人與海',
  author: '歐內斯特·海明威',
  price: 30,
  chapter:[
    {"cname" : "老人與海","cnum":"一"},
    {"cname" : "白象似的群山","cnum":"二"},
    {"cname" : "大雙心河(一)","cnum":"三"},
    {"cname" : "大雙心河(二)","cnum":"四"},
})
_book.save((err) => {});

刪除符合條件的第一個文檔:

Model.remove(conditions,callback);
Model.deleteOne(conditions,callback);

刪除符合條件的所有文檔:

Model.deleteMany(conditions,callback);

普通查詢:
查詢所有:

Model.find( conditions,[projection],[options],callback );

根據Id屬性查詢:

Model.findById( Id,[projection],[options],callback );

查詢符合條件的第一個文檔

Model.findOne( conditions,[projection],[options],callback );

聯表查詢:
待補充...

復雜查詢:

Book
      .find({ name: /libai/ }) 
      .where('chapter.cname').equals('Ghost')   // Book.chapter.cname是Ghost
      .where('price').gt(20).lt(100)  // 20 < Book.price <100
      .where('author').in(['李白', '杜甫'])//author是李白或者杜甫
      .limit(10)  //限制10條記錄
      .sort('-price')  //根據price的倒序排
      .select('name price') //選擇name和price欄位
      .exec(callback);

模糊匹配:

// or表示在數組里的條件滿足一個即可,$regex表示一個正則表達式,匹配了key,同時,加入了$option的$i表示忽略大小寫
Book.find({
        $or: [
          {'name': {'$regex': key, $options: '$i'}},
          {'author': {'$regex': key, $options: '$i'}}]
      })
      .populate('Store', 'name')
      .exec(function (err, books) {
        if (err) {
          callback(err);
        } else {
          callback(null, books);
        }
      })

  • 使用$set修改器更新欄位
// 更新某個欄位,最常見
Book.update({name:'老人與海'},{ $set:{name:'挪威的森林'}})
  • 使用數組修改器更新數組欄位
    $push:在一條文檔的對應的鍵數組中新增一個值,能新增重複的值,如果指定的鍵已經存在,它會向已有的數組末尾加入一個元素,要是沒有就會創建一個新的數組。
// 如果新增兩次,則會得到兩個正妹與野獸的欄位
Book.update({name:'老人與海'},{ $push:{chapter: {"cname" : "正妹與野獸","cnum":"一"}})
//結果變成:
{
  name: '老人與海',
  author: '歐內斯特·海明威',
  price: 30,
  chapter:[
    {"cname" : "老人與海","cnum":"一"},
    {"cname" : "白象似的群山","cnum":"二"},
    {"cname" : "大雙心河(一)","cnum":"三"},
    {"cname" : "大雙心河(二)","cnum":"四"},
    {"cname" : "正妹與野獸","cnum":"一"}
}

$pushAll:用法和$push相似,可以批量添加數組數據,即可以添加整個數組

Book.update({name:'老人與海'},{ $pushAll:{chapter: {"cname" : "正妹與野獸","cnum":"一"}, {"cname" : "老人與海","cnum":"一"}})
//結果變成:
{
  name: '老人與海',
  author: '歐內斯特·海明威',
  price: 30,
  chapter:[
    {"cname" : "老人與海","cnum":"一"},
    {"cname" : "白象似的群山","cnum":"二"},
    {"cname" : "大雙心河(一)","cnum":"三"},
    {"cname" : "大雙心河(二)","cnum":"四"},
    {"cname" : "正妹與野獸","cnum":"一"},
    {"cname" : "老人與海","cnum":"一"},
}

$ne:不重複插入數據。

// 如果已經有『正妹與野獸』這條數據,則不會再插入
Book.update({"chapter.cname":{$ne:"正妹與野獸"}},{$set:{"cname":"正妹與野獸","cnum":"一"}})

$addToSet:自動判斷數據是否存在,而且它和$each結合使用,還能同時在數組中插入多個數據,這是ne沒辦法辦到的。

Book.update(
{name:'老人與海'},
{$addToSet:{"chapter":{$each:[
{"cname" : "正妹與野獸","cnum":"一"},
{"cname" : "正妹與野獸2","cnum":"二"}
]}}})

$pop:刪除數組中的第一個惡或者最後一個。

// 刪除chapter數組中的最後一個值
Book.update({name:'老人與海'},{ $pop:{chapter: 1})
//結果變成:
{
  name: '老人與海',
  author: '歐內斯特·海明威',
  price: 30,
  chapter:[
    {"cname" : "老人與海","cnum":"一"},
    {"cname" : "白象似的群山","cnum":"二"},
    {"cname" : "大雙心河(一)","cnum":"三"},
}
// 刪除chapter數組中的第一個值
Book.update({name:'老人與海'},{ $pop:{chapter: -1})

$pull:刪除數組中匹配到的所有值

// 假設原來已經有兩個『大雙心河(一)』的域,則刪除會把2個都刪除掉
Book.update({name:'老人與海'},{ $pull:{chapter: {"cname" : "大雙心河(一)","cnum":"三"}})
//結果變成:
{
  name: '老人與海',
  author: '歐內斯特·海明威',
  price: 30,
  chapter:[
    {"cname" : "老人與海","cnum":"一"},
    {"cname" : "白象似的群山","cnum":"二"},
    {"cname" : "大雙心河(二)","cnum":"四"},
}

$pullAll:一次性刪除多個指定的數值

Book.update({name:'老人與海'},{ $pullAll:{chapter: {"cname" : "大雙心河(一)","cnum":"三"},{"cname" : "大雙心河(二)","cnum":"四"}})
//結果變成:
{
  name: '老人與海',
  author: '歐內斯特·海明威',
  price: 30,
  chapter:[
    {"cname" : "老人與海","cnum":"一"},
    {"cname" : "白象似的群山","cnum":"二"},
}

$unset:刪除指定的鍵值對。

Book.update({price:'3'},{ $unset:{price: 1 })
//結果變成:
{
  name: '老人與海',
  author: '歐內斯特·海明威',
  chapter:[
    {"cname" : "老人與海","cnum":"一"},
    {"cname" : "白象似的群山","cnum":"二"},
    {"cname" : "大雙心河(一)","cnum":"三"},
    {"cname" : "大雙心河(二)","cnum":"四"},
}

$:數組定位符 ,如果數組有多個數值我們只想對其中一部分進行操作我們就要用到定位器。

// 修改器名稱:$
// 語法:{ $set: { array.$.field : value} }
// 我們要把『cname』為『老人與海』的文檔修改『cnum』為100
Book.update({chapter.cname:'老人與海'},{ $set:{chapter.$.cnum: 『100』 })
//結果變成:
{
  name: '老人與海',
  author: '歐內斯特·海明威',
  price: 30,
  chapter:[
    {"cname" : "老人與海","cnum":"100"},
    {"cname" : "白象似的群山","cnum":"二"},
    {"cname" : "大雙心河(一)","cnum":"三"},
    {"cname" : "大雙心河(二)","cnum":"四"},
}

對於update方法,更建議用updateOne或者updateMany,例如:

// 把符合條件的文檔更新多條數據
Book.updateMany({ name: { $in: ['xxx','xxx','xxx'] } }, { $push: { chapter: {"cname" : "xxx","cnum":"xxx"} } }, { multi: true })

優化

  • 有時候會遇到這樣的後台輸出warnings:「DeprecationWarning: collection.update is deprecated. Use updateOne, updateMany」,其實是官方是想在未來版本中移除某些方法,而使用新的方法,所以建議我們用新方法,詳情
To fix all deprecation warnings, follow the below steps:

mongoose.set('useNewUrlParser', true);
mongoose.set('useFindAndModify', false);
mongoose.set('useCreateIndex', true);
Replace update() with updateOne(), updateMany(), or replaceOne()
Replace remove() with deleteOne() or deleteMany().
Replace count() with countDocuments(), unless you want to count how many documents are in the whole collection (no filter). In the latter case, use estimatedDocumentCount().
  • 更多的條件操作符:
$or    或關系
$nor    或關系取反
$gt    大於
$gte    大於等於
$lt     小於
$lte     小於等於
$ne        不等於
$in        在多個值範圍內
$nin       不在多個值範圍內
$all       匹配數組中多個值
$regex   正則,用於模糊查詢
$size   匹配數組大小
$maxDistance  範圍查詢,距離(基於LBS)
$mod     取模運算
$near   鄰域查詢,查詢附近的位置(基於LBS)
$exists    欄位是否存在
$elemMatch  匹配內數組內的元素
$within  範圍查詢(基於LBS)
$box    範圍查詢,矩形範圍(基於LBS)
$center       範圍醒詢,圓形範圍(基於LBS)
$centerSphere  範圍查詢,球形範圍(基於LBS)
$slice    查詢欄位集合中的元素(比如從第幾個之後,第N到第M個元素)

總結

總的來說,mongodb的nosql對於像mysql這種關系型數據庫也是有優勢的,數據結構更加隨意,不涉及到表與表之間的關系。

閱讀資料