1. 程式人生 > >NodeJS簡易部落格系統(四)Mongoose入門學習

NodeJS簡易部落格系統(四)Mongoose入門學習

一、模式(schemas)

1、定義schema

Mongoose的一切都始於一個Schema。每個schema對映到MongoDB的集合
(collection)和定義該集合(collection)中的文件的形式。
 

var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var blogSchema = 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
}
});

如果想後續新增額外的鍵(keys) ,使用Schema#add方法。在blogSchema每個key在我們的檔案將被轉換為相關SchemaType定義一個屬性。允許使用的SchemaTypes:

  • String
  • Number
  • Date
  • Buffer
  • Boolean
  • Mixed
  • ObjectId
  • Array

用default來設定預設值。

2、模型建立

使用剛剛的schema定義,我們需要將我們的blogschema轉成我們可以用的模型。
為此,我們通過 mongoose.model(modelName, schema) :
 

var Blog = mongoose.model('Blog', blogSchema);

3、自定義文件例項方法

// define a schema
var animalSchema = new Schema({ name: String, type: String });
// assign a function to the "methods" object of our animalSchema
animalSchema.methods.findSimilarTypes = function(cb) {
return this.model('Animal').find({ type: this.type }, cb);
};

呼叫:

var Animal = mongoose.model('Animal', animalSchema);
var dog = new Animal({ type: 'dog' });
dog.findSimilarTypes(function(err, dogs) {
console.log(dogs); // woof
});

4、自定義文件靜態例項方法

// assign a function to the "statics" object of our animalSchema
animalSchema.statics.findByName = function(name, cb) {
return this.find({ name: new RegExp(name, 'i') }, cb);
};
var Animal = mongoose.model('Animal', animalSchema);
Animal.findByName('fido', function(err, animals) {
console.log(animals);
});

二、模型(models)

Models 是從 Schema 編譯來的建構函式。 它們的例項就代表著可以從資料庫儲存和讀取的 documents。 從資料庫建立和讀取 document 的所有操作都是通過 model 進行的。

1、簡單例項

schema檔案

var mongoose = require("mongoose");
module.exports = new mongoose.Schema({
    username: String,
    password: String,
    isadmin:{
        type:Boolean,
        default:false
    }
});

model檔案

var mongoose = require("mongoose");
var userschama = require("../schemas/users");
module.exports = mongoose.model("User",userschama);

2、CRUD操作

1、Create

model物件.save()

2、Retrieve

用 mongoose 查詢文件相當容易啦,它支援 MongoDB 的高階( rich )查詢語法。 查詢文件可以用 model 的 find, findById, findOne, 和 where 這些靜態方法。查詢物件的size為small並且建立時間為近一年的資料:

model物件.find({ size: 'small' }).where('createdDate').gt(oneYearAgo).exec(callback);。

3、Update

model 的 update 方法可以修改資料庫中的文件,不過不會把文件返回給應用層。如果想更新單獨一條文件並且返回給應用層,可以使用 findOneAndUpdate 方法。

4、Delete

model 的 remove 方法可以刪除所有匹配查詢條件( conditions )的文件。

Tank.remove({ size: 'large' }, function (err) {
  if (err) return handleError(err);
  // todo after removing
});

三、文件(documents)

1、更新

Document 更新的方法同樣有很多,我們先看看一個傳統的實現,使用 findById:

Object.findById(id, function (err, object) {
  if (err) return handleError(err);

  object.size = 'large';
  object.save(function (err, updatedObject) {
    if (err) return handleError(err);
    res.send(updatedObject);
  });
});

這個方法先檢索資料,接著更新(使用了 save)。 如果僅僅需要更新而不需要獲取該資料, Model#update 就很適合:

Object.update({ _id: id }, { $set: { size: 'large' }}, callback);

如果我們確實需要返回文件,這個方法更適合:

Object.findByIdAndUpdate(id, { $set: { size: 'large' }}, { new: true }, function (err, object) {
  if (err) return handleError(err);
  res.send(object);
});

findAndUpdate/Remove 系列靜態方法查詢並返回最多1個文件。
注意:findAndUpdate/Remove 不會修改資料庫時執行任何鉤子或驗證。 可以使用 runValidators 選項 獲取一個驗證的限制子集(待修改)。 但是你需要鉤子和全文件驗證,還是先 query 後 save 吧。

2、覆蓋

可以用 .set() 覆蓋整個文件。如果你要修改 在中介軟體中被儲存的文件,這樣就很方便。

Object.findById(id, function (err, object) {
  if (err) return handleError(err);
  // Now `otherObject` is a copy of `object`
  otherObject.set(object);
});

3、子文件(Subdocument)

子文件是指巢狀在另一個文件中的文件。 在 Mongoose 中,這意味著你可以在裡巢狀另一個 schema。 Mongoose 子文件有兩種不同的概念:子文件陣列和單個巢狀子文件。

var childSchema = new Schema({ name: 'string' });

var parentSchema = new Schema({
  // Array of subdocuments
  children: [childSchema],
  // Single nested subdocuments. Caveat: single nested subdocs only work
  // in mongoose >= 4.2.0
  child: childSchema
});

子文件與普通 document 類似。巢狀schema可以有自己的中介軟體、自定義檢驗邏輯、 虛擬值以及其他頂層schemas可用的特性。兩者主要的不同點是子文件不能單獨儲存,他們會在他們的頂級文件儲存時儲存。

var Parent = mongoose.model('Parent', parentSchema);
var parent = new Parent({ children: [{ name: 'Matt' }, { name: 'Sarah' }] })
parent.children[0].name = 'Matthew';

// `parent.children[0].save()` 無操作,雖然他觸發了中介軟體
// 但是**沒有**儲存文件。你需要 save 他的父文件
parent.save(callback);

子文件跟普通 document一樣有save和validate中介軟體。呼叫父文件的save()會觸發其所有子文件的save()中介軟體,validate()中介軟體同理。

childSchema.pre('save', function (next) {
  if ('invalid' == this.name) {
    return next(new Error('#sadpanda'));
  }
  next();
});

var parent = new Parent({ children: [{ name: 'invalid' }] });
parent.save(function (err) {
  console.log(err.message) // #sadpanda
});

子文件的 pre('save') 和 pre('validate') 中介軟體執行於 頂層document的pre('save') 之前,頂層 document的pre('validate')之後。 因為save()前的驗證就是一個內建中介軟體。

// 一下程式碼順序打出 1-4 
var childSchema = new mongoose.Schema({ name: 'string' });

childSchema.pre('validate', function(next) {
  console.log('2');
  next();
});

childSchema.pre('save', function(next) {
  console.log('3');
  next();
});

var parentSchema = new mongoose.Schema({
  child: childSchema,
    });

parentSchema.pre('validate', function(next) {
  console.log('1');
  next();
});

parentSchema.pre('save', function(next) {
  console.log('4');
  next();
});

查詢子文件

每個子文件都有一個預設的_id 。Mongoose document陣列有一個特別的id方法,這個方法只要傳入_id 就能返回文件陣列中特定文件。

var doc = parent.children.id(_id);

新增子文件到陣列

Mongoose 陣列方法有 push、 unshift、 addToSet、 及其他:

var Parent = mongoose.model('Parent');
var parent = new Parent;

// create a comment
parent.children.push({ name: 'Liesl' });
var subdoc = parent.children[0];
console.log(subdoc) // { _id: '501d86090d371bab2c0341c5', name: 'Liesl' }
subdoc.isNew; // true

parent.save(function (err) {
  if (err) return handleError(err)
  console.log('Success!');
});

create 方法可以新建子文件但不加入陣列。
 

var newdoc = parent.children.create({ name: 'Aaron' });

刪除子文件

每個子文件都有 remove方法。另外,對於子文件陣列,有一個等效方法 .pull()。 對於單個巢狀子文件,remove()與把這個文件的值設為null等效。

// 等效於 `parent.children.pull(_id)`
parent.children.id(_id).remove();
// 等效於 `parent.child = null`
parent.child.remove();
parent.save(function (err) {
  if (err) return handleError(err);
  console.log('the subdocs were removed');
});

掌握了這些就能應付這個小專案了,如果要看詳細api請參考官方文件。