Mongoose基礎(chǔ)入門

一. 介紹

  1. MongoDB 是文檔型數(shù)據(jù)庫(Document Database)责球,不是關(guān)系型數(shù)據(jù)庫(Relational Database)淮韭。而Mongoose可以將MongonDB 數(shù)據(jù)庫存儲的文檔(documents)轉(zhuǎn)化為javascript 對象毫胜,然后可以直接進(jìn)行數(shù)據(jù)的增刪改查渴语。
    注釋:Mongoose是在node.js異步環(huán)境下對mongodb進(jìn)行便捷操作的對象模型工具懂衩。Mongoose只能作為NodeJS的驅(qū)動(dòng)脯倒,不能作為其他語言的驅(qū)動(dòng)。
  2. Mongooose中有三個(gè)比較重要的概念漱病,分別是Schema买雾、Modelentity杨帽。它們的關(guān)系是Schema生成Model漓穿, Model實(shí)例出entity
  3. Schema不具備操作數(shù)據(jù)庫的能力注盈。Modelentity都可對數(shù)據(jù)庫操作造成影響晃危,Modelentity更具操作性。
  4. Schema用于定義數(shù)據(jù)庫的結(jié)構(gòu)。每個(gè)Schema都會(huì)映射到mongodb中的一個(gè)collection僚饭。
  5. Model是由Schema編譯而成的構(gòu)造器震叮,具有抽象屬性和行為,可以對數(shù)據(jù)庫進(jìn)行增刪查改鳍鸵。Model的每一個(gè)實(shí)例entity就是一個(gè)文檔document苇瓣。
    注釋: Schema對應(yīng)數(shù)據(jù)庫中一個(gè)集合collection里文檔document的數(shù)據(jù)結(jié)構(gòu)。Model對應(yīng)一個(gè)集合 collection偿乖。entityModel的實(shí)例击罪,對應(yīng)集合collection中的一個(gè)文檔document

二. 連接數(shù)據(jù)庫

  1. 使用connect()方法連接數(shù)據(jù)庫贪薪。
    語法: mongoose.connect(url, options)
    參數(shù):options為可選參數(shù)媳禁,優(yōu)先級高于urloptions可用選項(xiàng)如下:
選項(xiàng) 含義
db 數(shù)據(jù)庫設(shè)置
server 服務(wù)器設(shè)置
replset 副本集設(shè)置
user 用戶名
pass 密碼
auth 鑒權(quán)選項(xiàng)
mongos 連接多個(gè)數(shù)據(jù)庫
  1. 簡單連接画切,傳入url參數(shù)以及db數(shù)據(jù)庫名稱竣稽。
mongoose.connect('mongodb://127.0.0.1/test');

注意:默認(rèn)端口為27017

  1. 傳入用戶名槽唾、密碼丧枪、端口等參數(shù)光涂。
mongoose.connect('mongodb://username:password@host:port/database?options...');
  1. 通過mongoose.connection監(jiān)聽連接狀態(tài)庞萍。
var mongoose =  require('mongoose');

mongoose.connect('mongodb://127.0.0.1:27017/test');

mongoose.connection.on('connected', function() {
  console.log('MongoDB connected connected');
})
mongoose.connection.on('error', function() {
  console.log('MongoDB connected error');
})
mongoose.connection.on('disconnected', function() {
  console.log('MongoDB connected disconnected');
})
  1. 避免多次連接
    新建一個(gè)mongoose.js:
var mongoose = require("mongoose");
mongoose.connect('mongodb://127.0.0.1:27017/test');
module.exports = mongoose;

每個(gè)module中,引用var mongoose = require('./mongoose.js')忘闻。

  1. 斷開連接
    語法:mongoose.disconnect()
mongoose.connect('mongodb://127.0.0.1:27017/test');

setTimeout(()=> {
  mongoose.disconnect();
}, 2000);

mongoose.connection.on('connected', function() {
  console.log('MongoDB connected connected');
})
mongoose.connection.on('error', function() {
  console.log('MongoDB connected error');
})
mongoose.connection.on('disconnected', function() {
  console.log('MongoDB connected disconnected');
})

三. Scheme

  1. Schema主要用于定義MongoDB中集合collection里文檔document的結(jié)構(gòu)钝计。定義Schema非常簡單,指定字段名和類型即可齐佳。
  2. Scheme支持以下8種類型
    String字符串私恬、Number數(shù)字 、Date日期炼吴、Buffer二進(jìn)制本鸣、Boolean布爾值、Mixed 混合類型硅蹦、ObjectId對象ID荣德、Array數(shù)組。
  3. 通過mongoose.Schema來調(diào)用Schema童芹,然后使用new方法來創(chuàng)建schema對象涮瞻。
var mongoose = require('mongoose');
var Schema = mongoose.Schema;

var mySchema = new Schema({
  title:  String,
  author: String,
  body:   String,
  comments: [{ body: String, date: Date }],
  date: { type: Date, default: Date.now },
  hidden: Boolean,
  meta: {
    votes: Number,
    favs:  Number
  }
});
  1. 創(chuàng)建Schema對象時(shí),聲明字段類型有兩種方法假褪,一種是首字母大寫的字段類型署咽,另一種是引號包含的小寫字段類型。
var mySchema = new Schema({title:String, author:String});
//或者 
var mySchema = new Schema({title:'string', author:'string'});
  1. 如果需要在Schema定義后添加其他字段生音,可以使用add()方法宁否。
var MySchema = new Schema();
MySchema.add({ name: 'string', color: 'string', price: 'number' });
  1. schema中設(shè)置timestampstrue窒升,schema映射的文檔document會(huì)自動(dòng)添加createdAtupdatedAt這兩個(gè)字段,代表創(chuàng)建時(shí)間和更新時(shí)間慕匠。
var UserSchema = new Schema(
  {...},
  { timestamps: true }
);
  1. 每一個(gè)文檔document都會(huì)被mongoose添加一個(gè)不重復(fù)的_id异剥,_id的數(shù)據(jù)類型不是字符串,而是ObjectID類型絮重。如果在查詢語句中要使用_id冤寿,則需要使用findById語句,而不能使用findfindOne語句青伤。

四. Model

  1. 模型Model是根據(jù)Schema編譯出的構(gòu)造器督怜,或者稱為類。
  2. 使用model()方法將Schema編譯為Model
    語法:mongoose.model("模型名稱", Scheme, "Collection名稱(可選)")
    注意:如果未傳第三個(gè)參數(shù)指定Collection集合名稱狠角。則Mongoose會(huì)將Collection集合名稱設(shè)置為模型名稱的小寫版号杠。如果名稱的最后一個(gè)字符是字母,則會(huì)變成復(fù)數(shù)丰歌;如果名稱的最后一個(gè)字符是數(shù)字姨蟋,則不變。例如立帖,如果模型名稱為MyModel眼溶,則集合名稱為mymodels;如果模型名稱為Model1晓勇,則集合名稱為model1堂飞。
var schema = new mongoose.Schema({ num:Number, name: String, size: String});
var MyModel = mongoose.model('MyModel', schema);
  1. 生成實(shí)例entity
var schema = new mongoose.Schema({ num:Number, name: String, size: String});
var MyModel = mongoose.model('MyModel', schema);
var doc = new MyModel({ size: 'small' });
console.log(doc.size);//'small'
  1. 實(shí)例entity保存為文檔document
    通過new Model()創(chuàng)建的實(shí)例entity,必須通過save()方法绑咱,才能將對應(yīng)文檔document保存到數(shù)據(jù)庫的集合collection中绰筛。回調(diào)函數(shù)是可選項(xiàng)描融,第一個(gè)參數(shù)為err铝噩,第二個(gè)參數(shù)為保存的文檔document對象。
    語法:save(function (err, doc) {})
var mongoose =  require('mongoose');

mongoose.connect('mongodb://test:test@127.0.0.1:27017/test');

var schema = new mongoose.Schema({ num:Number, name: String, size: String});
var MyModel = mongoose.model('MyModel', schema);
var doc = new MyModel({ size: 'small' });
doc.save(function (err,doc) {
  //{ __v: 0, size: 'small', _id: 5970daba61162662b45a24a1 }
  console.log(doc);
})

注釋:① 一個(gè)實(shí)例Entiy對應(yīng)一條文檔document窿克。② 實(shí)例entitysave方法只能夠在文檔document中保存scheme結(jié)構(gòu)范圍內(nèi)的字段骏庸。

五. 自定義方法

  1. 實(shí)例方法
    Model的實(shí)例entity有很多內(nèi)置方法,例如 save让歼〕担可以通過Schema對象的methods屬性給entity自定義擴(kuò)展方法。
var schema = new mongoose.Schema({ num:Number, name: String, size: String });        
schema.methods.findSimilarSizes = function(cb){
  return this.model('MyModel').find({size:this.size},cb);
}

var MyModel = mongoose.model('MyModel', schema);
var doc1 = new MyModel({ name:'doc1', size: 'small' });
var doc2 = new MyModel({ name:'doc2', size: 'small' });
var doc3 = new MyModel({ name:'doc3', size: 'big' });
doc1.save();
doc2.save();
doc3.save();
setTimeout(function(){
    doc1.findSimilarSizes(function(err,docs){
        docs.forEach(function(item,index,arr){
            //doc1
            //doc2
            console.log(item.name)        
        })
    })  
},0) 
  1. 靜態(tài)方法
    可以通過Schema對象的statics屬性給 Model添加靜態(tài)方法谋右。
var schema = new mongoose.Schema({ num:Number, name: String, size: String });
schema.statics.findByName = function(name,cb){
    return this.find({name: new RegExp(name,'i')},cb);
}

var MyModel = mongoose.model('MyModel', schema);
var doc1 = new MyModel({ name:'doc1', size: 'small' });
var doc2 = new MyModel({ name:'doc2', size: 'small' });
var doc3 = new MyModel({ name:'doc3', size: 'big' });
doc1.save();
doc2.save();
doc3.save();
setTimeout(function(){
    MyModel.findByName('doc1',function(err,docs){
        //[ { _id: 5971e68f4f4216605880dca2,name: 'doc1',size: 'small',__v: 0 } ]
        console.log(docs);
    })
},0)

注釋:靜態(tài)方法是通過Schema對象的statics屬性給model添加方法硬猫;實(shí)例方法是通過Schema對象的methods是給entity添加方法。

  1. 查詢方法
    通過schema對象的query屬性,給model添加查詢方法啸蜜。
var schema = new mongoose.Schema({ age:Number, name: String});        
schema.query.byName = function(name){
    return this.find({name: new RegExp(name)});
}

var temp = mongoose.model('temp', schema);   
temp.find().byName('huo').exec(function(err,docs){
    //[ { _id: 5971f93be6f98ec60e3dc86c, name: 'huochai', age: 27 },
    // { _id: 5971f93be6f98ec60e3dc86e, name: 'huo', age: 30 } ]
    console.log(docs);
}) 

注釋:查詢方法需要有 .find() 作為開頭坑雅。
問題:靜態(tài)方法與查詢方法的本質(zhì)區(qū)別?

六. 新增文檔

  1. mongoose提供了三種新增文檔document的方法:
    (1) entitysave()方法
    (2) modelcreate()方法
    (3) modelinsertMany()方法
  2. entitysave()方法
    save([options], [options.safe], [options.validateBeforeSave], [fn])
mongoose.connect('mongodb://test:test@127.0.0.1:27017/test');

var schema = new mongoose.Schema({name: String, age: Number});
var model = mongoose.model('temp', schema);
var entity = new model({name: 'john', age: 18});
entity.save((err, doc) => {
  //{ _id: 5b63a49e8910503426acd587, name: 'john', age: 18, __v: 0 }
  console.log(doc);
})
  1. modelcreate()方法
    使用save()方法衬横,需要先實(shí)例化為entity裹粤,再使用save()方法保存文檔document。而create()方法直接在模型model上操作蜂林,并且可以同時(shí)新增多個(gè)文檔document遥诉。
    Model.create(doc(s), [callback])
mongoose.connect('mongodb://test:test@127.0.0.1:27017/test');

var schema = new mongoose.Schema({name: String, age: Number});
var model = mongoose.model('temp', schema);
model.create([{name:"lily"}, {name:'jane'}],(err, docs) => {
    console.log(docs);
    //[ { _id: 5b643cc96ca76572d64e242c, name: 'lily', __v: 0 },
    //{ _id: 5b643cc96ca76572d64e242d, name: 'jane', __v: 0 } ]
});
  1. modelinsertMany()方法
mongoose.connect('mongodb://test:test@127.0.0.1:27017/test');
var schema = new mongoose.Schema({name: String, age: Number});
var model = mongoose.model('temp', schema);
model.insertMany([{name:"a"}, {name:"b"}],(err, docs) => {
  console.log(docs);
  //[ { _id: 5b643db39d69e1731042a0f1, name: 'a', __v: 0 },
  // { _id: 5b643db39d69e1731042a0f2, name: 'b', __v: 0 } ]
});

注意:新增文檔方法的callback回調(diào)函數(shù)不能使用exec方法改寫。查詢文檔噪叙、更新文檔以及刪除文檔方法的callback回調(diào)函數(shù)大多數(shù)都可以使用exec方法改寫矮锈。
問題:modelcreate()方法與insertMany()方法的區(qū)別。

七. 查詢文檔

  1. mongoose提供了三種查詢文檔document的方法:
    (1) find()
    (2) findById()
    (3) findOne()
  2. find()方法
    Model.find(conditions, [projection], [options], [callback])
mongoose.connect('mongodb://test:test@127.0.0.1:27017/test');

var schema = new mongoose.Schema({name: String, age: Number});
var model = mongoose.model('temp', schema);
for(let i = 1; i < 10; i++) {
  new model({name: `jake${i}`, age: i}).save();
}

//查找所有文檔
model.find((err, docs) => {
  console.log(docs);//1,2,3,4,5,6,7,8,9
});

//查找年齡大于等于6的文檔
model.find({age: {$gte: 6}}, (err, docs) => {
  console.log(docs); //6睁蕾,7苞笨,8,9
});

//查找年齡大于等于6文檔的另一種寫法
model.find({age: {$gte: 6}}).exec((err, docs) => {
  console.log(docs); //6子眶,7瀑凝,8,9
});

//年齡大于8,且名字存在'jake'的數(shù)據(jù)
model.find({age: {$gt: 8}, name: /jake/}, (err, docs) => {
  console.log(docs); //9
});

//年齡等于1臭杰,且只輸出'name'字段
model.find({age: {$lt: 3}}, 'name', (err, docs) => {
  console.log(docs);
  //[ { _id: 5b644b5a43048277c40834c6, name: 'jake1' },
  //{ _id: 5b644b5a43048277c40834c7, name: 'jake2' } ]
});

//年齡等于1粤咪,且不需要輸出_id
model.find({age: 1}, {name: 1, _id: 0}, (err, docs) => {
  console.log(docs); //[ { name: 'jake1' } ]
});

//搜索年齡大于2,且搜索結(jié)果跳過前2條, 只留3條硅卢,且按年齡倒序
model.find({age: {$gt: 2}}).skip(2).limit(3).sort({age: -1}).exec((err, docs) => {
  console.log(docs); //7,6,5
});

注釋: 可參考mongo.exe程序以及node原生查詢mongodb數(shù)據(jù)庫API射窒,接口類似。
注意:① 兩種寫法find(..., callback)find(...).exec(callback)将塑。② node查詢通過toArray(err, docs)方法獲取文檔數(shù)組,mongoose查詢通過exec(err, docs)方法獲取文檔數(shù)組蝌麸。

  1. findById()方法
    Model.findById(id, [projection], [options], [callback])
model.findById('5b644b5a43048277c40834c7', (err, doc) => {
  console.log(doc);
  //{ _id: 5b644b5a43048277c40834c7, name: 'jake2', age: 2, __v: 0 }
});

//另一種寫法
model.findById('5b644b5a43048277c40834c7').exec((err, doc) => {
  console.log(doc);
  //{ _id: 5b644b5a43048277c40834c7, name: 'jake2', age: 2, __v: 0 }
});

//只輸出name字段
model.findById('5b644b5a43048277c40834c7', {name: 1, _id: 0}).exec((err, doc) => {
  console.log(doc);
  //{ name: 'jake2' }
});

//輸出最少字段
model.findById('5b644b5a43048277c40834c7', {lean: true}).exec((err, doc) => {
  console.log(doc);
  //{ _id: 5b644b5a43048277c40834c7 }
});
  1. findOne()方法
    該方法返回查找到的所有實(shí)例的第一個(gè)点寥。
    Model.findOne([conditions], [projection], [options], [callback])
model.findOne({age: {$gt: 5}}, (err, doc) => {
  console.log(doc);
  //{ _id: 5b644b5a43048277c40834cb, name: 'jake6', age: 6, __v: 0 }
});

model.findOne({age: {$gt: 5}}, {_id: 0}).exec((err, doc) => {
  console.log(doc);
  //{ name: 'jake6', age: 6, __v: 0 }
});

model.findOne({age: {$gt: 5}}, {name: 1}).lean().exec((err, doc)=> {
  console.log(doc);
  //{ _id: 5b644b5a43048277c40834cb, name: 'jake6' }
});
  1. 常用的查詢條件如下
$or         或關(guān)系
$nor        或關(guān)系取反
$gt        大于
$gte        大于等于
$lt        小于
$lte        小于等于
$ne          不等于
$in          在多個(gè)值范圍內(nèi)
$nin         不在多個(gè)值范圍內(nèi)
$all        匹配數(shù)組中多個(gè)值
$regex      正則,用于模糊查詢
$size      匹配數(shù)組大小
$maxDistance 范圍查詢来吩,距離(基于LBS)
$mod      取模運(yùn)算
$near      鄰域查詢敢辩,查詢附近的位置(基于LBS)
$exists     字段是否存在
$elemMatch   匹配內(nèi)數(shù)組內(nèi)的元素
$within    范圍查詢(基于LBS)
$box       范圍查詢,矩形范圍(基于LBS)
$center     范圍醒詢弟疆,圓形范圍(基于LBS)
$centerSphere 范圍查詢戚长,球形范圍(基于LBS)
$slice      查詢字段集合中的元素(比如從第幾個(gè)之后,第N到第M個(gè)元素)
  1. $where操作符
    如果要進(jìn)行更復(fù)雜的查詢怠苔,需要使用$where操作符同廉,$where操作符功能強(qiáng)大而且靈活,它可以使用任意的JavaScript作為查詢的一部分,包含JavaScript表達(dá)式的字符串或者JavaScript函數(shù)迫肖。
    (1) 使用字符串
model.find({$where:"this.x == this.y"},(err,docs) => {
  console.log(docs);
  //[{ _id: 5972ed35e6f98ec60e3dc887,name: 'wang',age: 18,x: 1,y: 1},
  //{ _id: 5972ed35e6f98ec60e3dc889, name: 'li', age: 20, x: 2, y: 2 }]
});

model.find({$where:"obj.x == obj.y"}, (err,docs) =>{
  //[ { _id: 5972ed35e6f98ec60e3dc887,name: 'wang',age: 18,x: 1,y: 1},
  //{ _id: 5972ed35e6f98ec60e3dc889, name: 'li', age: 20, x: 2, y: 2 }]
  console.log(docs);
});

(2) 使用函數(shù)

model.find({$where:() => {
        return obj.x !== obj.y;
    }}, (err,docs) =>{
    //[ { _id: 5972ed35e6f98ec60e3dc886,name: 'huochai',age: 27,x: 1,y: 2 },
    //{ _id: 5972ed35e6f98ec60e3dc888, name: 'huo', age: 30, x: 2, y: 1 } ]
    console.log(docs);
}) 

model.find({$where:() => {
        return this.x !== this.y;
    }},(err,docs) =>{
    //[ { _id: 5972ed35e6f98ec60e3dc886,name: 'huochai',age: 27,x: 1,y: 2 },
    //{ _id: 5972ed35e6f98ec60e3dc888, name: 'huo', age: 30, x: 2, y: 1 } ]
    console.log(docs);
}) 

八. 更新文檔

更新方法

  1. 文檔更新可以使用以下幾種方法锅劝。
    (1) update()
    (2) updateOne()
    (3) updateMany()
    (4) find() + save()
    (5) findOne() + save()
    (6) findByIdAndUpdate()
    (7) findOneAndUpdate()
  2. update()
    第一個(gè)參數(shù)conditions為查詢條件,第二個(gè)參數(shù)doc為需要修改的數(shù)據(jù)蟆湖,第三個(gè)參數(shù)options為控制選項(xiàng)故爵,第四個(gè)參數(shù)是回調(diào)函數(shù)。
    Model.update(conditions, doc, [options], [callback])
    options有如下選項(xiàng):
  safe (boolean): 默認(rèn)為true隅津。安全模式诬垂。
  upsert (boolean): 默認(rèn)為false。如果不存在則不創(chuàng)建新記錄伦仍。
  multi (boolean): 默認(rèn)為false剥纷。是否更新多個(gè)查詢記錄。
  runValidators: 如果值為true呢铆,執(zhí)行Validation驗(yàn)證晦鞋。
  setDefaultsOnInsert: 如果upsert選項(xiàng)為true,在新建時(shí)插入文檔定義的默認(rèn)值棺克。
  strict (boolean): 以strict模式進(jìn)行更新悠垛。
  overwrite (boolean): 默認(rèn)為false。禁用update-only模式娜谊,允許覆蓋記錄确买。

(1) 只更新第一條滿足條件的數(shù)據(jù)

model.update({age: {$gt: 7}}, {age: 10}, (err, row)=> {
  //{ n: 1, nModified: 1, ok: 1 }
  console.log(row);
});

//使用exec的寫法
model.update({age: {$gt: 7}}, {age: 10}).exec((err, row)=> {
  //{ n: 1, nModified: 1, ok: 1 }
  console.log(row);
});

(2) 更新所有滿足條件的數(shù)據(jù)

model.update({age: {$gt: 7}}, {age: 10}, {multi: true}).exec((err, row)=> {
  //{ n: 2, nModified: 1, ok: 1 }
  console.log(row);
});

(3) 如果沒有符合條件的數(shù)據(jù),則什么都不做

model.update({age: 100}, {age: 1000}).exec((err, row)=> {
  //{ n: 0, nModified: 0, ok: 1 }
  console.log(row);
});

(4) 如果設(shè)置upsert參數(shù)為true纱皆,若沒有符合查詢條件的文檔湾趾,mongo將會(huì)綜合第一第二個(gè)參數(shù)向集合插入一個(gè)新的文檔。

model.update({age: 100}, {name: 'jake100'}, {upsert: true}).exec((err, row)=> {
  //{n: 1,
  // nModified: 0,
  // upserted: [ { index: 0, _id: 5b646510d22cf9feac0bd2f5 } ],
  // ok: 1 }
  console.log(row);
});

//驗(yàn)證插入文檔
model.find({age: '100'}).exec((err, docs) =>{
  console.log(docs)
  //[ { _id: 5b646510d22cf9feac0bd2f5,
  //     age: 100,
  //     __v: 0,
  //     name: 'jake100' } ]
});

注意:update()方法中的回調(diào)函數(shù)不能省略派草,否則數(shù)據(jù)不會(huì)被更新搀缠。如果無需在回調(diào)函數(shù)中做進(jìn)一步操作,則可以使用exec()簡化代碼近迁。
例如:temp.update({name:/aa/},{age: 0},{upsert:true}).exec();

  1. updateOne()
    updateOne()方法只能更新找到的第一條數(shù)據(jù)艺普,即使設(shè)置{multi:true}也無法同時(shí)更新多個(gè)文檔。
model.updateOne({age: {$gt: 8}}, {name: 'jake80'}).exec((err, res)=> {
  //{ n: 1, nModified: 1, ok: 1 }
  console.log(res);
});
  1. updateMany()
    updateMany()update()方法唯一的區(qū)別就是默認(rèn)更新多個(gè)文檔鉴竭,即使設(shè)置{multi:false}也無法只更新第一個(gè)文檔歧譬。
    Model.updateMany(conditions, doc, [options], [callback])
model.updateMany({age: {$gt: 8}}, {name: 'jake80'}).exec((err, res)=> {
  //{ n: 3, nModified: 3, ok: 1 }
  console.log(res);
});
  1. find() + save()
    如果需要更新的操作比較復(fù)雜,可以使用find()+save()方法來處理搏存。
model.find({age: {$gt: 8}}).exec((err, docs)=> {
  docs.forEach(doc => {
    doc.name = `jake${doc.age}`;
    doc.save();
  });
  console.log(docs);
  //[ { _id: 5b644b5a43048277c40834cd, name: 'jake10', age: 10, __v: 0 },
  //{ _id: 5b644b5a43048277c40834ce, name: 'jake10', age: 10, __v: 0 },
  //{ _id: 5b646510d22cf9feac0bd2f5, age: 100, __v: 0, name: 'jake100' } ]
});
  1. findOne() + save()
    如果需要更新的操作比較復(fù)雜瑰步,可以使用findOne()+save()方法來處理。
model.findOne({age: 10}).exec((err, doc)=> {
  doc.name = 'jake9';
  doc.age = 9;
  doc.save();
  console.log(doc);
  //{ _id: 5b644b5a43048277c40834cd, name: 'jake9', age: 9, __v: 0 }
});
  1. findByIdAndUpdate()
    Model.findOneAndUpdate([conditions], [update], [options], [callback])
  2. findOneAndUpdate()
    Model.findOneAndUpdate([conditions], [update], [options], [callback])

修改器

數(shù)據(jù)準(zhǔn)備工作璧眠,創(chuàng)建集合及文檔數(shù)據(jù)如下:

var mongoose =  require('mongoose');
mongoose.connect('mongodb://test:test@127.0.0.1:27017/test');

var schema = new mongoose.Schema({
  name: String,
  age: Number,
  array: [Number]
});
var Model = mongoose.model('temp', schema);
for(let i = 1; i < 10; i++) {
  new Model({name: `jake${i}`, age: i, array: []
 }).save();
}

對象修改器

  1. $inc 增減修改器缩焦,只對數(shù)字有效读虏。
    找到age等于1的文檔,修改age字段值舌界,自減5掘譬。
Model.update({age: 1}, {$inc: {age: -5}}).exec()
  1. $set 指定一個(gè)鍵的值
Model.update({name: 'jake1'}, {$set: {age: 2}}).exec()
  1. $unset刪除一個(gè)鍵
Model.update({name: 'jake1'}, {$unset: {age: ''}}).exec()

注意:$unset操作符只匹配keyvalue可以是任意值呻拌。

數(shù)組修改器

  1. $push數(shù)組尾部插入葱轩。
    給匹配文檔的array鍵對應(yīng)數(shù)組插入數(shù)字1。
Model.update({age: 2}, {$push: {array: 1}}).exec();
  1. $addToSet數(shù)組尾部插入藐握,如果存在則不插入靴拱。
Model.update({age: 2}, {$addToSet: {array: 1}}).exec();
  1. $pop 數(shù)組尾部刪除。
    傳入1刪除數(shù)組尾元素猾普,傳入-1刪除數(shù)組首元素袜炕。
Model.update({age: 2}, {$pop: {array: 1}}).exec();
  1. $pull刪除數(shù)組指定元素。
Model.update({age: 2}, {$pull: {array: 6}}).exec();

九. 刪除文檔

  1. 有三種方法用于文檔刪除初家。
    (1) remove()
    (2) findOneAndRemove()
    (3) findByIdAndRemove()
    注意:這些方法中的回調(diào)函數(shù)不能省略偎窘,否則數(shù)據(jù)不會(huì)被刪除。當(dāng)然溜在,可以使用exec()方法來簡寫代碼陌知。
  2. remove()
    remove有兩種形式,一種是Modelremove()方法掖肋,一種是documentremove()方法仆葡。
    (1) Modelremove()方法
    該方法的第一個(gè)參數(shù)conditions為查詢條件,第二個(gè)參數(shù)為回調(diào)函數(shù)志笼。
    model.remove(conditions, [callback])
model.remove({age: {$gte:9}}, (err, res) => {
  console.log(res);
  //{ n: 3, ok: 1 }
});

//使用exec的寫法
model.remove({age: {$gte:9}}).exec((err, res) => {
  console.log(res);
  //{ n: 0, ok: 1 }
});

(2) documentremove()方法
document.remove([callback])

model.findOne({age: 8}).exec((err, doc) => {
    doc.remove((err, doc) => {
      console.log(doc);
      //{ _id: 5b64fdfa1bfab0852697bc00, name: 'jake8', age: 8, __v: 0 }
    });
});

注釋:①modelremove()方法回調(diào)可以使用exec()方法改寫沿盅, documentremove()方法不可以。②modelremove()方法刪除符合條件的所有document文檔纫溃,documentremove()方法刪除當(dāng)前文檔腰涧。

  1. findOneAndRemove()
    modelremove()會(huì)刪除符合條件的所有數(shù)據(jù),如果只刪除符合條件的第一條數(shù)據(jù)皇耗,則可以使用modelfindOneAndRemove()方法南窗。
    Model.findOneAndRemove(conditions, [options], [callback])
model.findOneAndRemove({age: {$gte: 0}}, (err, doc) => {
  console.log(doc);
  //{ _id: 5b644b5a43048277c40834c6, name: 'jake1', age: 1, __v: 0 }
});

model.findOneAndRemove({age: {$gte: 0}}).exec((err, doc) => {
  console.log(doc);
  //{ _id: 5b644b5a43048277c40834c7, name: 'jake2', age: 2, __v: 0 }
});
  1. findByIdAndRemove()
    Model.findByIdAndRemove(id, [options], [callback])
model.find().exec((err, docs) => {
  const docIdArr = docs.map(doc => doc._id);
  model.findByIdAndRemove(docIdArr[0]).exec((err, doc) => {
    console.log(doc);
    //{ _id: 5b644b5a43048277c40834c8, name: 'jake3', age: 3, __v: 0 }
  })
});

十. Promise

  1. Mongoose異步操作,例如.save()方法郎楼,會(huì)返回一個(gè)ES6標(biāo)準(zhǔn)的promises。你可以使用類似 MyModel.findOne({}).then()await MyModel.findOne({}).exec()的寫法窒悔。
var gnr = new Band({
  name: "Guns N' Roses",
  members: ['Axl', 'Slash']
});

var promise = gnr.save();
assert.ok(promise instanceof Promise);

promise.then(function (doc) {
  assert.equal(doc.name, "Guns N' Roses");
});
  1. mongoose queries 查詢操作雖然有then方法呜袁,但并不是一個(gè)完全的promise〖蛑椋可以使用exec()方法將其轉(zhuǎn)化為一個(gè)完全的promise阶界。
var query = Band.findOne({name: "Guns N' Roses"});
assert.ok(!(query instanceof Promise));

// A query is not a fully-fledged promise, but it does have a `.then()`.
query.then(function (doc) {
  // use doc
});

// `.exec()` gives you a fully-fledged promise
var promise = query.exec();
assert.ok(promise instanceof Promise);

promise.then(function (doc) {
  // use doc
});
  1. 可以通過重寫mongoose.Promise的方式使用第三方promise庫虹钮,例如 bluebird
var query = Band.findOne({name: "Guns N' Roses"});

// Use bluebird
mongoose.Promise = require('bluebird');
assert.equal(query.exec().constructor, require('bluebird'));

十一. 前后鉤子

  1. 前后鉤子即pre()post()方法膘融,又稱為中間件芙粱,是在執(zhí)行某些操作時(shí)可以執(zhí)行的函數(shù)。中間件在schema上指定氧映,類似于靜態(tài)方法或?qū)嵗椒ǖ取?br> 注意:①前后鉤子方法定義在schema上春畔。② 前后鉤子方法必須在Model創(chuàng)建之前定義,否則不生效岛都。
  2. 可以在model執(zhí)行下列操作時(shí)律姨,設(shè)置前后鉤子。
    init validate save remove count find findOne findOneAndRemove findOneAndUpdate insertMany update
  3. pre()中間件
    find()方法為例臼疫,在執(zhí)行find()方法之前择份,執(zhí)行pre()方法。
mongoose.connect('mongodb://test:test@127.0.0.1:27017/test');

var schema = new mongoose.Schema({name: String, age: Number});

schema.pre('find', (next) => {
  console.log('pre hook fu1');
  next();
});

schema.pre('find', (next) => {
  console.log('pre hook fu2');
  next();
});

var Model = mongoose.model('temp', schema);

Model.find((err, docs) => {
  console.log(docs[0]);
  //pre hook fu1
  //pre hook fu2
  //{ _id: 5b644b5a43048277c40834c9, name: 'jake4', age: 4, __v: 0 }
});
  1. post()中間件
    post()方法并不是在執(zhí)行某些操作后再去執(zhí)行的方法烫堤,而在執(zhí)行某些操作前最后執(zhí)行的方法荣赶,post()方法里不可以使用next()
mongoose.connect('mongodb://test:test@127.0.0.1:27017/test');

var schema = new mongoose.Schema({name: String, age: Number});

schema.post('find', (next) => {
  console.log('post hook fu1');
});

schema.post('find', (next) => {
  console.log('post hook fu2');
});

var Model = mongoose.model('temp', schema);

Model.find((err, docs) => {
  console.log(docs[0]);
  //post hook fu1
  //post hook fu2
  //{ _id: 5b644b5a43048277c40834c9, name: 'jake4', age: 4, __v: 0 }
});

十二. 查詢后處理

  1. 常用的查詢后處理的方法如下所示
  • sort 排序
  • skip 跳過
  • limit 限制
  • select 顯示字段
  • exect 執(zhí)行
  • count 計(jì)數(shù)
  • distinct 去重
  1. 方法示例
mongoose.connect('mongodb://test:test@127.0.0.1:27017/test');

var schema = new mongoose.Schema({name: String, age: Number});
var Model = mongoose.model('temp', schema);

for(let i = 1; i <= 15; i++) {
  new Model({name: `jake${i}`, age: i}).save();
}

Model.find((err, docs) => {
  console.log(docs); //1 - 15
});

//sort 排序
Model.find().sort({'age': -1}).exec((err, docs) => {
  console.log(docs); //15 - 1
});

//skip 跳過
Model.find().skip(3).exec((err, docs) => {
  console.log(docs); //4 - 15
});

//limit 限制
Model.find().limit(2).exec((err, docs) => {
  console.log(docs); //1 - 2
});

//select 限制字段
Model.find().select({name: 1, _id: 0}).exec((err, docs) => {
  console.log(docs[0]); //{ name: 'jake2' }
});

//鏈?zhǔn)讲僮?Model.find().sort({'age': -1}).skip(2).limit(1).select({age: 1, _id: 0}).exec((err, docs) => {
  console.log(docs); //[ { age: 13 } ]
});

//count顯示文檔數(shù)目
Model.find().count().exec((err, count) => {
  console.log(count); //15
});

//distinct 去重
Model.find().distinct('name').exec((err, arr) => {
  console.log(arr); //jake1-jake15
});

十三. 文檔驗(yàn)證

  1. 如果不進(jìn)行文檔驗(yàn)證鸽斟,保存文檔時(shí)拔创,就可以不按照Schema設(shè)置的字段進(jìn)行設(shè)置,分為以下幾種情況湾盗。
var mongoose =  require('mongoose');
mongoose.connect('mongodb://test:test@127.0.0.1:27017/test');

var schema = new mongoose.Schema({name: String, age: Number,x: Number, y: Number});
var Model = mongoose.model('temp', schema);

(1) 缺少字段的文檔也可以保存成功

new Model({age: 10}).save((err, doc) => {
  console.log(doc);
  //{ _id: 5b65862905b74ea05e211322, age: 10, __v: 0 }
});

(2) 包含未設(shè)置的字段的文檔也可以保存成功伏蚊,未設(shè)置的字段不被保存。

new Model({age: 11, z: 10}).save((err, doc) => {
  console.log(doc);
  //{ _id: 5b65865ca0864ba06fb02d37, age: 11, __v: 0 }
});

(3) 包含字段類型與設(shè)置不同的文檔可以保存成功格粪,不同字段類型的字段被保存為設(shè)置的字段類型

new Model({age:true,name:10}).save(function(err,doc){
  //{ _id: 5b6586b547589ba082d19c3c, age: 1, name: '10', __v: 0 }
  console.log(doc);
});
  1. 通過文檔驗(yàn)證躏吊,就可以避免以上幾種情況發(fā)生。文檔驗(yàn)證在SchemaType中定義帐萎,格式如下比伏。
    {name: {type:String, validator:value}}
    常用驗(yàn)證包括以下幾種:
    required: 數(shù)據(jù)必須填寫
    default: 默認(rèn)值
    min: 最小值(只適用于數(shù)字)
    max: 最大值(只適用于數(shù)字)
    match: 正則匹配(只適用于字符串)
    enum: 枚舉匹配(只適用于字符串)
    validate: 自定義匹配
  2. required文檔驗(yàn)證
    age設(shè)置為必填字段,如果沒有age字段疆导,文檔將不被保存赁项,且出現(xiàn)錯(cuò)誤提示。
var schema = new mongoose.Schema({age:{type:Number,required:true}, name: String,x:Number,y:Number});
var Model = mongoose.model('temp', schema);
new Model({name:"abc"}).save((err,doc) => {
  //Path `age` is required.
  console.log(err.errors['age'].message);
});
  1. default文檔驗(yàn)證
    設(shè)置age字段的默認(rèn)值為18澈段,如果不設(shè)置age字段悠菜,則會(huì)取默認(rèn)值。
var schema = new mongoose.Schema({ age:{type:Number,default:18}, name:String,x:Number,y:Number});
var Model = mongoose.model('temp', schema);
new Model({name:'a'}).save((err,doc) => {
  //{ __v: 0, name: 'a', _id: 59730d2e7a751d81582210c1, age: 18 }
  console.log(doc);
});
  1. min败富、max文檔驗(yàn)證
    age的取值范圍設(shè)置為[0,10]悔醋。如果age取值為20,文檔將不被保存兽叮,且出現(xiàn)錯(cuò)誤提示芬骄。
var schema = new mongoose.Schema({ age:{type:Number,min:0,max:10}, name: String,x:Number,y:Number});
var Model = mongoose.model('temp', schema);
new Model({age:20}).save((err,doc) => {
  //Path `age` (20) is more than maximum allowed value (10).
  console.log(err.errors['age'].message);
});
  1. match文檔驗(yàn)證
    namematch設(shè)置為必須存在'a'字符猾愿。如果name不存在'a',文檔將不被保存账阻,且出現(xiàn)錯(cuò)誤提示蒂秘。
var schema = new mongoose.Schema({ age:Number, name:{type:String,match:/a/},x:Number,y:Number});
var Model = mongoose.model('temp', schema);
new Model({name:'bbb'}).save((err,doc) => {
  //Path `name` is invalid (bbb).
  console.log(err.errors['name'].message);
});
  1. enum文檔驗(yàn)證
    name的枚舉取值設(shè)置為['a','b','c'],如果name不在枚舉范圍內(nèi)取值淘太,文檔將不被保存姻僧,且出現(xiàn)錯(cuò)誤提示。
var schema = new mongoose.Schema({ age:Number, name:{type:String,enum:['a','b','c']},x:Number,y:Number});
var Model = mongoose.model('temp', schema);
new Model({name:'bbb'}).save((err,doc) => {
  //`bbb` is not a valid enum value for path `name`.
  console.log(err.errors['name'].message)
});
  1. validate文檔驗(yàn)證
    validate實(shí)際上是一個(gè)函數(shù)琴儿,函數(shù)的參數(shù)代表當(dāng)前字段的值段化,返回true表示通過驗(yàn)證,返回false表示未通過驗(yàn)證造成。利用validate可以自定義任何條件显熏。
    例如,定義名字name的長度必須在4個(gè)字符以上晒屎。
var schema = new mongoose.Schema({ 
  name:{
    type: String, 
    validate: value =>value.length > 4
  }, 
  age: Number,
  x: Number,
  y: Number
});
var Model = mongoose.model('temp', schema);
new Model({name:'abc'}).save((err, doc) => {
  //Validator failed for path `name` with value `abc`
  console.log(err.errors['name'].message);
});

十四. population連表操作

  1. population介紹
    (1) MongoDB是文檔型數(shù)據(jù)庫喘蟆,所以它沒有關(guān)系型數(shù)據(jù)庫joins(數(shù)據(jù)庫的兩張表通過"外鍵"建立連接關(guān)系) 特性。在建立數(shù)據(jù)的關(guān)聯(lián)時(shí)會(huì)比較麻煩鼓鲁。為了解決這個(gè)問題蕴轨,Mongoose封裝了一個(gè)population功能。使用population可以實(shí)現(xiàn)在一個(gè) document中填充其他 collection(s)document(s)骇吭。
    (2) 在定義schema的時(shí)候橙弱,如果設(shè)置某個(gè) field 關(guān)聯(lián)另一個(gè)schema,那么在獲取 document 的時(shí)候就可以使用 population 功能通過關(guān)聯(lián)schemafield 找到關(guān)聯(lián)的另一個(gè) document燥狰,并且用被關(guān)聯(lián) document 的內(nèi)容替換掉原來關(guān)聯(lián)字段(field)的內(nèi)容棘脐。
  2. 連表關(guān)系場景
    場景:用戶user可以寫文章post,并且對文章post進(jìn)行評論comment龙致。
    分析:一個(gè)用戶user可以寫多篇文章post蛀缝。一篇文章post只能有一個(gè)作者user,但可以有多條評論comment目代。一條評論comment 只屬于一篇文章post屈梁,且只屬于一個(gè)用戶user
    示例:用戶A寫了文章A榛了、評論A在讶,用戶B寫了文章B、評論B霜大,用戶C寫了文章C真朗、評論C;用戶A在文章B上添加了評論A僧诚,用戶B在文章C上添加了評論B遮婶,用戶C在文章A上添加了評論C
  3. 連表關(guān)系示例代碼
    (1) 創(chuàng)建用戶湖笨、文章旗扑、評論三個(gè)schemaModel
var mongoose =  require('mongoose');
mongoose.connect('mongodb://test:test@127.0.0.1:27017/test');

var Schema = mongoose.Schema;

//創(chuàng)建用戶scheme以及model
var userSchema = new Schema({
  name  : String,
  posts : [{ type: Schema.Types.ObjectId, ref: 'post' }]
});
var UserModel = mongoose.model('user', userSchema);

//創(chuàng)建文章scheme以及model
var postSchema = new Schema({
  name  : String,
  poster   : { type: Schema.Types.ObjectId, ref: 'user' },
  comments : [{ type: Schema.Types.ObjectId, ref: 'comment' }],
});
var PostModel = mongoose.model('post', postSchema);

//創(chuàng)建評論scheme以及model
var commentSchema = new Schema({
  name  : String,
  post      : { type: Schema.Types.ObjectId, ref: "post" },
  commenter : { type: Schema.Types.ObjectId, ref: 'user' },
});
var CommentModel = mongoose.model('comment', commentSchema);
  • 創(chuàng)建了三個(gè) ModelUserModelPostModel慈省,CommentModel臀防。
  • UserModel 的屬性 posts對應(yīng)是一個(gè) ObjectId 的數(shù)組,ref表示關(guān)聯(lián)PostModel边败。
  • PostModel的屬性 postercomments 分別關(guān)聯(lián)UserModelCommentModel袱衷。
  • CommentModel的屬性 postcommenter 分別關(guān)聯(lián)PostModelUserModel
    注意:① ref指向mongoose.model(name, schema);方法的name參數(shù)笑窜,而不是方法返回值model致燥。②userSchema中應(yīng)該保存而未保存comments數(shù)組。
    (2) 創(chuàng)建entity實(shí)例排截、建立關(guān)系并保存數(shù)據(jù)
//創(chuàng)建三個(gè)用戶userA嫌蚤、userB、userC
var userA = new UserModel({name: 'userA'});
var userB = new UserModel({name: 'userB'});
var userC = new UserModel({name: 'userC'});
//創(chuàng)建三篇文章postA断傲、postB脱吱、postC
var postA = new PostModel({name:  'postA'});
var postB = new PostModel({name:  'postB'});
var postC = new PostModel({name:  'postC'});
//創(chuàng)建三個(gè)評論commentA、commentB认罩、commentC
var commentA = new CommentModel({name: 'commentA'});
var commentB = new CommentModel({name: 'commentB'});
var commentC = new CommentModel({name: 'commentC'});

//建立用戶與文章的關(guān)系
userA.posts.push(postA._id);
//moongoose封裝的語法糖箱蝠,與userA.posts.push(postA)寫法含義相同
userB.posts.push(postB);
userC.posts.push(postC);
//建立文章與用戶、評論的關(guān)系
postA.poster = userA;
postB.poster = userB;
postC.poster = userC;
postA.comments.push(commentC);
postB.comments.push(commentA);
postC.comments.push(commentB);
//建立評論與用戶垦垂、文章的關(guān)系
commentA.post = postB;
commentB.post = postC;
commentC.post = postA;
commentA.commenter = userA;
commentB.commenter = userB;
commentC.commenter = userC;

//保存數(shù)據(jù)
userA.save();
userB.save();
userC.save();
postA.save();
postB.save();
postC.save();
commentA.save();
commentB.save();
commentC.save();
  1. population連表操作
    (1) query.populate
    語法:query.populate(path, [select], [model], [match], [options])
    pathString | Object 宦搬;指定要填充的關(guān)聯(lián)字段。
    selectObject | String乔外;指定填充 document中的哪些字段床三。
    modelModel;指定關(guān)聯(lián)字段的model杨幼,若未指定則使用Schemaref撇簿。
    matchObject;指定附加的查詢條件差购。
    optionsObject四瘫;指定附加的其他查詢選項(xiàng),如排序以及條數(shù)限制等欲逃。
UserModel.find().skip(1).limit(1)
  .populate('posts', 'name')
  .exec((err, docs) => {
    console.log(docs[0].posts);
    //[{"_id":"5b6655ac4f1303b2933a7dc0","name":"postB"}]
});

UserModel.findOne({name: 'userC'})
  .populate({
    path: 'posts',
    select: { name: 1, _id: 0}
  })
  .exec((err, doc) => {
    console.log(doc.posts); //[{"name":"postC"}]
  });

PostModel.findOne({name: 'postA'})
  .populate('poster comments', 'name -_id')
  .exec((err, doc)=> {
    console.log(doc.poster); //{ name: 'userA' }
    console.log(doc.comments); //[{"name":"commentC"}]
  });

PostModel.findOne({name: 'postC'})
  .populate([
    {path: 'poster', select: 'name'},
    {path: 'comments', select: {_id: 0}}
  ])
  .exec((err, doc) => {
    console.log(doc.poster);
    //{ _id: 5b6655ac4f1303b2933a7dbe, name: 'userC' }
    console.log(doc.comments);
    //[{"name":"commentB","post":"5b6655ac4f1303b2933a7dc1","commenter":"5b6655ac4f1303b2933a7dbd","__v":0}]
  });

(2) Model.populate
語法:Model.populate(docs, options, [cb(err,doc)])

CommentModel.findOne((err, doc) => {
  CommentModel.populate(doc, {path: 'post commenter', select: 'name'}, (err, doc) => {
    console.log(doc.post);
    //{ _id: 5b6655ac4f1303b2933a7dc0, name: 'postB' }
    console.log(doc.commenter);
    //{ _id: 5b6655ac4f1303b2933a7dbc, name: 'userA' }
  })
});

(3) document.populate
語法:Document.populate([path], [callback])

CommentModel.findOne((err, doc) => {
  doc.populate({path: 'post commenter', select: 'name'}, (err, doc) => {
    console.log(doc.post);
    //{ _id: 5b6655ac4f1303b2933a7dc0, name: 'postB' }
    console.log(doc.commenter);
    //{ _id: 5b6655ac4f1303b2933a7dbc, name: 'userA' }
  })
});

十五. 參考資料

Mongoose官網(wǎng)
Mongoose Promise語法
Mongoose基礎(chǔ)入門
Mongoose 之 Population 使用
Mongoose 使用之 Population

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末找蜜,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子稳析,更是在濱河造成了極大的恐慌洗做,老刑警劉巖弓叛,帶你破解...
    沈念sama閱讀 212,454評論 6 493
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異诚纸,居然都是意外死亡撰筷,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,553評論 3 385
  • 文/潘曉璐 我一進(jìn)店門畦徘,熙熙樓的掌柜王于貴愁眉苦臉地迎上來毕籽,“玉大人,你說我怎么就攤上這事井辆」赝玻” “怎么了?”我有些...
    開封第一講書人閱讀 157,921評論 0 348
  • 文/不壞的土叔 我叫張陵杯缺,是天一觀的道長蒸播。 經(jīng)常有香客問我,道長夺谁,這世上最難降的妖魔是什么廉赔? 我笑而不...
    開封第一講書人閱讀 56,648評論 1 284
  • 正文 為了忘掉前任,我火速辦了婚禮匾鸥,結(jié)果婚禮上蜡塌,老公的妹妹穿的比我還像新娘。我一直安慰自己勿负,他們只是感情好馏艾,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,770評論 6 386
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著奴愉,像睡著了一般琅摩。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上锭硼,一...
    開封第一講書人閱讀 49,950評論 1 291
  • 那天房资,我揣著相機(jī)與錄音,去河邊找鬼檀头。 笑死轰异,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的暑始。 我是一名探鬼主播搭独,決...
    沈念sama閱讀 39,090評論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼廊镜!你這毒婦竟也來了牙肝?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,817評論 0 268
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎配椭,沒想到半個(gè)月后虫溜,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,275評論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡颂郎,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,592評論 2 327
  • 正文 我和宋清朗相戀三年艰管,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了疚鲤。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片州弟。...
    茶點(diǎn)故事閱讀 38,724評論 1 341
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡渊季,死狀恐怖越走,靈堂內(nèi)的尸體忽然破棺而出音婶,到底是詐尸還是另有隱情赔硫,我是刑警寧澤滨达,帶...
    沈念sama閱讀 34,409評論 4 333
  • 正文 年R本政府宣布得滤,位于F島的核電站陨献,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏懂更。R本人自食惡果不足惜眨业,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 40,052評論 3 316
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望沮协。 院中可真熱鬧龄捡,春花似錦、人聲如沸慷暂。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,815評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽行瑞。三九已至奸腺,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間血久,已是汗流浹背突照。 一陣腳步聲響...
    開封第一講書人閱讀 32,043評論 1 266
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留氧吐,地道東北人讹蘑。 一個(gè)月前我還...
    沈念sama閱讀 46,503評論 2 361
  • 正文 我出身青樓,卻偏偏與公主長得像副砍,于是被迫代替她去往敵國和親衔肢。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,627評論 2 350

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

  • Mongoose 是什么?一般我們不直接用MongoDB的函數(shù)來操作MongoDB數(shù)據(jù)庫 Mongose就是一套操...
    獨(dú)孤久見閱讀 593評論 0 1
  • mongoose入門 MongoDB是一個(gè)開源的NoSQL數(shù)據(jù)庫豁翎,相比MySQL那樣的關(guān)系型數(shù)據(jù)庫角骤,它更顯得輕巧、...
    huilegezai閱讀 4,444評論 0 14
  • 關(guān)于Mongodb的全面總結(jié) MongoDB的內(nèi)部構(gòu)造《MongoDB The Definitive Guide》...
    中v中閱讀 31,913評論 2 89
  • 鏈表反轉(zhuǎn)的思路:1.利用棧后進(jìn)先出的特性,將鏈表的每個(gè)節(jié)點(diǎn)都Push進(jìn)棧邦尊,然后再Pop出棧背桐,保存進(jìn)鏈表,實(shí)現(xiàn)反轉(zhuǎn)蝉揍。...
    李澤興閱讀 211評論 0 0
  • 有一種文章邏輯叫SCQA(場景-沖突-問題-答案) 一秒鐘:長蟲(蛇)吻妲己
    花拳繡腿閱讀 126評論 0 0