1. 程式人生 > >在Node中基於Mongoose對MongoDB進行增刪查改(CRUD)操作(一)

在Node中基於Mongoose對MongoDB進行增刪查改(CRUD)操作(一)

關鍵詞:mongodb安裝 mongoose使用 robomongo mongoose的CRUD操作 mongoose的查詢,增加,修改,刪除

工具介紹

MongoDB

MongoDB是基於Javascript語言的資料庫,儲存格式是JSON,而Node也是基於JavaScript的環境(庫),所以node和mongoDB的搭配能減少因為資料轉換帶來的時間空間開銷。

Mongoose

是MongoDB的一個物件模型工具,它將資料庫中的資料轉換為JavaScript物件以供你在應用中使用,封裝了MongoDB對文件的的一些增刪改查等常用方法,讓NodeJS操作Mongodb資料庫變得更加靈活簡單。

Robomongo

一個視覺化的mongoDB操作軟體,類似於mysql的navicat視覺化工具。

捋一捋它們的關係,mongoDB是一個數據庫,mongoose是你在自己程式碼中操作mongo資料庫的介面,而robomongo是mongo資料庫的視覺化工具,通過它的介面方便直接操作資料庫內容。

工具安裝

MongoDB安裝

1.安裝mongoDB

2.建立MongoDB環境

需要自己建立db目錄作為資料庫環境,在命令列視窗中輸入

$ md \data\db

建立db資料夾後,在命令視窗中進入安裝目錄的bin資料夾執行mongod.exe,把資料庫安裝在\data\db中。mongoDB會檢測你的根目錄是否有data\db資料夾,如果有會預設安裝到這個資料夾裡面。

 $ cd C:\Program Files\MongoDB\Server\3.2\bin

 $ mongod.exe

當然也可以直接在系統根目錄下建立data\db資料夾,然後在mongoDB安裝資料夾中雙擊執行mongod.exe。

3.啟動MongoDB

命令列工具中輸入:

 $ cd C:\Program Files\MongoDB\Server\3.2\bin

 $ mongod.exe

為了避免每次都要輸入目錄,所以在系統變數裡面配置一下path變數,把“;C:\Program Files\MongoDB\Server\3.2\bin”放到path後面(記得加;隔開),以後可以直接在命令列視窗輸入mongod.exe回車即可。

在瀏覽器中輸入網址:http://localhost:27017/ 。如果服務啟動成功會看到以下一段話:It looks like you are trying to access MongoDB over HTTP on the native driver port.

4.連線MongoDB(這一步基本沒有用,只有在命令列工具中使用mongo原生方法時需要,而在mongoose裡面會有連線的程式碼,Robomongo執行也會有連線)

命令列工具中輸入mongo.exe,回車。

如果出現這個警告:2016-07-16T14:49:02.827+0800 I CONTROL [main] Hotfix KB2731284 or later update is not installed, will zero-out data files那是因為Windows缺少一個補丁,從這個連結下週補丁451413_intl_x64_zip,然後解壓安裝包,在你解壓的目錄下找到Windows6.1-KB2731284-v3-x64.mus安裝檔案。安裝重啟即可。

Robomongo安裝以及使用

直接到官網https://robomongo.org/下載安裝,安裝成功後執行,第一次執行,需要新建立一個連線,如圖建立test,點選save儲存連線。

這裡寫圖片描述

選擇test,點選connect連線資料庫。robomongo會自己搜尋你係統裡面安裝的mongodb並與其連線。如圖

這裡寫圖片描述

連線成功後,顯示你的資料庫,在這個節目可以對資料庫進行操作。如圖:

這裡寫圖片描述

Mongoose安裝與載入

首先假定你已經安裝了 Node.js,命令列工具輸入:

$ npm install mongoose -g

在使用的檔案中require(“mongoose”);即可。

使用Mongoose進行CRUD操作

使用基本步驟

Mongose基於mongodb的原生方法,自己定義了一套操作MongoDB資料庫的介面,比原生方法更加簡單方便。為了更加直觀,下面的步驟結合例子來講。假如我需要做一個教務系統,需要儲存學生Student的資訊,學生資訊通常包含姓名name,學號id,電話phone,登入日期date等。我把學生的資訊存在mongodb的myDB資料庫中,集合的名字叫students。如圖:

這裡寫圖片描述

_id這個域你可以自己定義,但如果你沒有定義,系統會自動給你加上。下面先介紹在node中通過mongoose對mongodb進行操作的必須前提步驟:

1.node連線資料庫

mongoose.connect('mongodb://user:[email protected]:port/database');

這只是最基本的連線,我們一般還會加一些設定,是否開啟除錯模式,連線提示等。通常我會這麼寫:

var mongoose = require("mongoose");
mongoose.Promise = global.Promise;

/*除錯模式是mongoose提供的一個非常實用的功能,用於檢視mongoose模組對mongodb操作的日誌,一般開發時會開啟此功能,以便更好的瞭解和優化對mongodb的操作。*/
mongoose.set('debug', true);

/*一般預設沒有user和password*/
var db=mongoose.connect('mongodb://localhost/myDB');

db.connection.on("error", function (error) {  
  console.log("資料庫連線失敗:" + error); 
}); 

db.connection.on("open", function () {  
  console.log("資料庫連線成功"); 
});

沒有mongoose.Promise = global.Promise會出現如下錯誤(這個錯誤沒有什麼影響):

這裡寫圖片描述

意思是mongoose自帶的promise過期了,然後需要使用v8引擎的promise。

2.定義模式(Schema)

每個模式對映mongoDB的一個集合(注意對映這個詞,下面會講為什麼),它定義(只是定義,不是實現)這個集合裡面文件的結構,就是定義這個文件有什麼欄位,欄位型別是什麼,欄位預設值是什麼等。除了定義結構外,還定義文件的例項方法,靜態模型方法,複合索引,中介軟體等。詳情自己檢視mongoose官方文件

var mongoose = require('mongoose');
var Schema = mongoose.Schema;

/*定義模式Student_Schema*/
var Student_Schema = new Schema({
  name: String,
  id: Number,
  phone: String,
  date: Date
}, {
  versionKey: false
});

/*定義模型Student,注意資料庫存的是students*/
mongoose.model("Student", Student_Schema);

{versionKey: false}是幹嘛用?如果不加這個設定,我們通過mongoose第一次建立某個集合時,它會給這個集合設定一個versionKey屬性值,這個屬性值包含這個文件的內部版本,資料庫中顯示為_v,如圖:

這裡寫圖片描述

通過{versionKey: false}可以配置這個引數,讓資料庫不再新增這個屬性,格式是:new Schema({..}, { versionKey: false });

3.定義模型(Model)

模型用來實現我們定義的模式,呼叫mongoose.model來編譯Schema得到Model。

/*定義模型Student,資料庫存的是students*/
mongoose.model("Student", Student_Schema);

為什麼上面我強調模式的對映,那是因為模式僅僅是和db中集合文件的結構相對應(對映),它並不直接在資料庫中操作這個結構,模型才是直接與資料庫打交道的存在,可以這麼說:模式是定義結構,模型是實現操作。當我們使用mongoose.model(“Student”, Student_Schema)建立Student模型對資料進行操作時,資料庫會尋找一個名字叫students集合接受Student模型的操作,特別需要注意的是:1.如果是增加(instance.save)操作時,資料庫中沒有這個集合,資料庫會自動建立這個集合儲存資料,這個集合產生規則為:把Model名字字母全部變小寫和在後面加複數s。2.如果是刪改查三個操作資料庫中沒有這個集合,那就是沒有,刪除空修改空返回空。

4.訪問模型

var MyStudent = mongoose.model("Student");

到這裡,已經基本完成了使用mongoose前提操作了。有沒有覺得有點繁瑣,其實我也覺得挺繁瑣,幸運的是234可以一步建立:

var MyStudent = mongoose.model('Student',{
  name: String,
  id: Number,
  phone: String,
  date: Date
});

5.建立例項(instance)

var sam = new MyStudent({
    name: "sam976",
    id: 123,
    phone: "18706888888",
    date: Date.now()
});

一般只在save(增加)操作中需要。

模型的例項是集合中真實的資料,就是collection中的document,用mysql中的術語來說就是一條記錄。模型在資料庫中建好了集合和文件結構後,通過例項往裡面新增真實的document。

捋一捋模式、模型、例項的關係:模式定義了操作和屬性,這些操作和屬性包括mongoose自帶和自定義,而模型和例項可以對模式裡面定義的屬性和方法進行引用。模型是mongoose用來和資料庫直接打交道的中介,例項是往資料庫存的真實資料。模式並非必須,那為什麼要分開模式和模型呢?我覺得是遵循了軟體設計中“定義和實現分開”這個原則。有的文章說模式沒有操作資料庫的能力,模型才有,對這個觀點,我覺得部分對,雖說模式不能直接操作資料庫,但模式定義的方法可以被模型用來操作資料庫。官方文件是這麼說的:

Schemas not only define the structure of your document and casting of properties, they also define document instance methods, static Model methods, compound indexes and document lifecycle hooks called middleware.

以上是使用mongoose進行增刪查改操作都需要經過的前提步驟,下面正式介紹對資料庫的增刪查改(CRUD)操作。

CRUD操作

1.CRUD之create

使用模型建立sam例項,sam例項呼叫save方法把document存入資料庫的students集合中,程式碼如下

    var MyStudent = mongoose.model("Student");
    var sam = new MyStudent({
        name: "sam976",
        id: 123,
        phone: "18706888888",
        date: Date.now()
    });
    sam.save(function(err) {});

通過robomongo檢視資料庫,可以看到資料已經存放成功,如圖

這裡寫圖片描述

2.CRUD之read

使用MyStudent模型呼叫find()方法返回students集合的所有內容,第一個引數定義條件,第二個引數是回撥函式,回撥函式中的docs是返回的是查詢結果,結果形式為一個json資料陣列[{},{}]。

    var MyStudent = mongoose.model("Student");
    MyStudent.find({}, function(err, docs) {});

比如資料庫students集合中,有如下資料:

這裡寫圖片描述

執行上面程式碼,結果console.log輸出顯示如下:

這裡寫圖片描述

模型還可以呼叫其他很多查詢的函式,比如

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

篇幅較多,這裡不攤開來講(以後會專門出一篇介紹),可以自己檢視官方文件關於Querying介紹

3.CRUD之update

使用MyStudent模型呼叫update()方法完成更新,第一個引數是條件(也就是where name=”sam976”),第二個引數修改的內容。

    var MyStudent = mongoose.model("Student");
    MyStudent.update({name:"sam976"},{id:456,phone:"12345678910"}, function(error){});

執行如上程式碼前,如圖

這裡寫圖片描述

執行如上程式碼後,如圖

這裡寫圖片描述

4.CRUD之delete

使用MyStudent模型呼叫remove()方法刪除文件。

var MyStudent = mongoose.model("Student");
MyStudent.remove({ name: 'sam976' }, function (err) {});

原始碼結構

使用mongoose的時候,通常會在專案中建立三個檔案:connect.js,mongoose-db.js,app.js。

其中connect.js存放的是連線資料庫的操作,我們只需要載入一次即可在程式執行期間一直連線資料庫。

mongoose-db.js檔案存放模式和模型的生成的程式碼,沒有連線資訊,也沒有其他額外不相干程式碼,可以在在mongoose-db.js中把模型exports公開:

var MyStudent = mongoose.model("Student", Student_Schema);
exports.MyStudent=MyStudent;

/*定義其他模型和模式*/
var MyTeacher = mongoose.model("Teacher", Teacher_Schema);
exports.MyTeacher=MyTeacher;

然後在app.js中引用:

var MyStudent = require("./mongoose-db").MyStudent;
var MyTeacher = require("./mongoose-db").MyTeacher;

app.js存放對資料庫的操作,比如CRUD。通過這樣的方式,結構比較清晰,程式碼可讀性大大增強。

下面放原始碼(目的是給自己備份,笑臉…)

connect.js

var mongoose = require("mongoose");
mongoose.Promise = global.Promise;//為了解決過期的問題
/*除錯模式是mongoose提供的一個非常實用的功能,用於檢視mongoose模組對mongodb操作的日誌,一般開發時會開啟此功能,以便更好的瞭解和優化對mongodb的操作。*/
mongoose.set('debug', true);
/*mongoose會快取命令,只要connect成功,處於其前其後的命令都會被執行,connect命令也就無所謂放哪裡*/
var db=mongoose.connect('mongodb://localhost/myDB');

db.connection.on("error", function (error) {  
  console.log("資料庫連線失敗:" + error); 
});

db.connection.on("open", function () {  
  console.log("資料庫連線成功"); 

mongoose-db.js

require('./connect');
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
/*定義模式Student_Schema*/
var Student_Schema = new Schema({
  name: String,
  id: Number,
  phone: String,
  date: Date

}, {
  versionKey: false
});

/*定義模型Student,資料庫存的是students*/
var MyStudent = mongoose.model("Student", Student_Schema);
exports.MyStudent=MyStudent;

/*mongoose.Schema({
  username: {// 真實姓名
    type: String,
    required: true
  },
  password: { // 密碼
    type: String,
    required: true
  }
});*/

app.js

require("./mongoose-db");
var express = require("express");
var mongoose = require("mongoose");
var MyStudent = require("./mongoose-db").MyStudent;
var app = express();

app.use(express.static("./"));
app.get("/create", function(req, res) {
    console.log("create 函式")
    var beta = new MyStudent({
        name: "beta",
        id: 124,
        phone: "1871111111",
        date: Date.now()
    });
    beta.save(function(err) {
        if (err) {
            console.log(err);
        } else {
            console.log('存入成功');
        }
    });
    res.send("存入成功!!");

});

app.get("/read", function(req, res) {
    console.log("讀取函式");
    MyStudent.find({}, function(err, docs) {
        console.log(docs);
        /*對docs進行操作*/
    });

    res.send("讀取成功!!");

});

app.get("/readOne", function(req, res) {
    console.log("讀取單值函式");
    MyStudent.findOne({
        name: req.query.student_name
    }, {
        "id": 1,
        "_id": 0
    }, function(err, docs) {
        if (docs.id === req.query.student_id) {
            res.send('登入成功');
            console.log(docs.password);
        } else {
            console.log(docs.password);
            res.send('登入失敗');
        }
    });
    /*過濾查詢,引數2: {'name':1, 'password':0} 查詢文件的返回結果包含name , 不包含password.(_id預設是1)*/
    /*model.find({},null,{limit:20});過濾查詢,引數3: 遊標操作 limit限制返回結果數量為20個,如不足20個則返回所有*/

});

app.get("/update", function(req, res) {
    console.log("更新函式");
    MyStudent.update({
        name: "sam976"
    }, {
        id: 456,
        phone: "12345678910"
    }, function(error) {});
    res.send("更新成功!!");

});

app.get("/delete", function(req, res) {
    console.log("刪除函式");
    MyStudent.remove({
        name: 'sam976'
    }, function(err) {
        if (err) return handleError(err);
        // removed!
    });
    res.send("刪除成功!!");

});

app.listen(3001, function() {
    console.log("start server")
});

為了測試,我還寫了個html。data-operate.html

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <title>data-operate</title>
</head>

<body>
    <form action="./create">
        <input type="submit" value="建立">
    </form>
    <br/>
    <form action="./read">
        <input type="submit" value="讀取">
    </form>
    <br/>
    <form action="./update">
        <input type="submit" value="更新">
    </form>
    <br/>
    <form action="./delete">
        <input type="submit" value="刪除">
    </form>
    <br/>
    <form action="./readOne">
        <input type="text" name="student_name">
        <input type="text" name="student_id">
        <input type="submit" value="單值讀取">
    </form>
</body>

</html>

上文是在Node中基於Mongoose對MongoDB進行增刪查改(CRUD)操作的簡單介紹,以後會有進階的文章。