Trong phần này chúng ta sẽ tìm hiểu cách lưu trữ dữ liệu trong cơ sở dữ liệu MongoDB
MongoDB
MongoDB là cơ sở dữ liệu dạng NoSQL mã nguồn mở, có khả năng mở rộng và hiệu suất cao, sử dụng cú pháp JSON, không sử dụng khái niệm lược đồ cơ sở dữ liệu… và còn nhiều tính năng khác nữa.
Để cài đặt MongoDB thì bạn tải MongoDB tại địa chỉ: https://www.mongodb.org/downloads
Bạn tải và cài cho đúng với kiến trúc của hệ điều hành của mình (ví dụ máy bạn chạy hệ điều hành 32 bit thì cài bản 32 bit).
Sau khi cài xong thì bạn đưa đường dẫn đến thư mục bin trong thư mục cài đặt MongoDB vào biến môi trường PATH để tiện sử dụng sau này (nếu bạn không biết cách thiết lập biến này thì tham khảo bài trước hoặc tìm trên Google).
Sau đó bạn mở Command Prompt (cmd) lên và chạy lệnh mongod --version
để xem phiên bản MongoDB của mình cũng như xác nhận xem đường dẫn đến thư mục bin
đã nằm trong biến PATH đúng chưa:
C:\Users\PhoCode>mongod --version db version v3.2.10 git version: 79d9b3ab5ce20f51c272b4411202710a082d0317 OpenSSL version: OpenSSL 1.0.1t-fips 3 May 2016 allocator: tcmalloc modules: none build environemnt: distmod: 2008plus-ssl distarch: x86_64 target_arch: x86_64
Phiên bản MongoDB mình dùng ở đây là phiên bản 3.2.10. Mặc định các cơ sở dữ liệu sẽ được lưu trong thư mục C:\data\db trên Windows.
Tạo cơ sở dữ liệu
Chúng ta sẽ tạo một cơ sở dữ liệu trong MongoDB để lưu trữ các ghi chú trong ứng dụng Notes.
Đầu tiên chúng ta mở một Command Prompt lên rồi gõ lệnh mongod
để chạy server:
C:\Users\PhoCode>mongod ...
Tiếp theo chúng ta mở một Command Prompt khác lên rồi chạy các lệnh sau:
C:\Users\PhoCode>mongo MongoDB shell version: 3.2.10 connection to: test > use notes switched to db notes
Lệnh mongo
sẽ chạy trình shell của MongoDB, trình shell cho phép chúng ta thao tác với server của MongoDB, mặc định trong server có sẵn một cơ sở dữ liệu có tên là test,
bất cứ thao tác ghi/đọc nào cũng sẽ được thực hiện trên CSDL này.
Lệnh use <tên CSDL>
sẽ tạo một cơ sở dữ liệu với tên tương ứng và thiết lập trình shell chuyển các thao tác đọc/ghi lên CSDL đó, nếu CSDL đó đã có rồi thì chuyển sang sử dụng luôn.
Tạo model
Giống như các phần trước, chúng ta sẽ tạo model chứa các hàm để đọc/ghi dữ liệu.
Trên npm
có module mongoose
là module dùng để kết nối với cơ sở dữ liệu Mongoose rất mạnh. Đầu tiên chúng ta tạo một thư mục có tên models-mongoose
trong thư mục gốc của project. Trong thư mục này chúng ta tạo một file có tên
notes.js
với nội dung như sau:
var mongoose = require('mongoose'); var Schema = mongoose.Schema; var dburl = undefined; exports.connect = function(thedburl, callback) { dburl = thedburl; mongoose.connect(dburl); } exports.disconnect = function(callback) { mongoose.disconnect(callback); } var NoteSchema = new Schema({ notekey: String, title: String, body: String }); mongoose.model('Note', NoteSchema); var Note = mongoose.model('Note'); exports.create = function(key, title, body, callback) { var newNote = new Note(); newNote.notekey = key; newNote.title = title; newNote.body = body; newNote.save(function(err) { if(err) callback(err); else callback(); }); } exports.update = function(key, title, body, callback) { exports.read(key, function(err, doc) { if(err) callback(err); else { doc.notekey = key; doc.title = title; doc.body = body; doc.save(function(err) { if(err) callback(err); else callback(); }); } }); } exports.read = function(key, callback) { Note.findOne({ notekey: key }, function(err, doc) { if(err) callback(err); else callback(null, doc); }); } exports.destroy = function(key, callback) { exports.read(key, function(err, doc) { if(err) callback(err); else { doc.remove(); callback(); } }); } exports.titles = function(callback) { Note.find().exec(function(err, docs) { if(err) callback(err); else { if(docs) { var noteList = []; docs.forEach(function(note) { noteList.push({ key: note.notekey, title: note.title }); }); callback(null, noteList); } else { callback(); } } }); }
File này cũng giống như các file model khác là chứa các hàm dùng để kết nối, ngắt kết nối, thực hiện các chức năng CRUD.
var mongoose = require('mongoose'); var Schema = mongoose.Schema; var dburl = undefined; exports.connect = function(thedburl, callback) { dburl = thedburl; mongoose.connect(dburl); } exports.disconnect = function(callback) { mongoose.disconnect(callback); }
Chúng ta sẽ cần đến module mongoose
để tương tác với CSDL. Schema
là một lớp trong mongoose
định nghĩa các trường của một dòng trong một collection. Biến dburl
được dùng để lưu đường dẫn kết nối đến CSDL. Hai hàm connect()
và disconnect()
được dùng để kết nối và ngắt kết nối đến CSDL. Để thực hiện kết nối thì chúng ta dùng hàm mongoose.connect()
và truyền vào đường dẫn, ngắt kết nối thì chúng ta gọi hàm mongoose.disconnect()
và truyền vào một hàm callback.
var NoteSchema = new Schema({ notekey: String, title: String, body: String }); mongoose.model('Note', NoteSchema); var Note = mongoose.model('Note');
Chúng ta định nghĩa đối tượng NoteSchema
kế thừa từ Schema
chứa các trường notekey, title, body
tương ứng. Sau đó dùng hàm model()
để tạo bảng tương ứng trong CSDL.
exports.create = function(key, title, body, callback) { var newNote = new Note(); newNote.notekey = key; newNote.title = title; newNote.body = body; newNote.save(function(err) { if(err) callback(err); else callback(); }); }
Hàm create()
được dùng để tạo các ghi chú, để lưu vào CSDL thì chúng ta gọi hàm save(),
hàm này nhận một hàm callback làm tham số.
exports.update = function(key, title, body, callback) { exports.read(key, function(err, doc) { if(err) callback(err); else { doc.title = title; doc.body = body; doc.save(function(err) { if(err) callback(err); else callback(); }); } }); }
Hàm update()
được dùng để cập nhật các ghi chú. Ở đây trước khi cập nhật chúng ta kiểm tra xem ghi chú có tồn tại không bằng cách dùng hàm read()
được định nghĩa ở dưới, hàm này nhận vào khóa, nếu không tồn tại thì trả về lỗi, nếu có tồn tại thì chúng ta cập nhật cũng bằng hàm save().
exports.read = function(key, callback) { Note.findOne({ notekey: key }, function(err, doc) { if(err) callback(err); else callback(null, doc); }); }
Hàm read()
được dùng để đọc các ghi chú, hàm này nhận vào khóa key
. Hàm findOne()
sẽ tìm bản ghi đầu tiên khớp với từ khóa, nếu không tìm thấy thì sẽ báo lỗi, nếu tìm thấy thì dữ liệu tìm được sẽ nằm trong tham số doc
trong hàm callback. Nếu bạn muốn tìm toàn bộ thay vì chỉ tìm một bản ghi thì dùng hàm find()
.
exports.destroy = function(key, callback) { exports.read(key, function(err, doc) { if(err) callback(err); else { doc.remove(); callback(); } }); }
Hàm destroy()
được dùng để xóa một ghi chú, và cũng tương tự như các hàm trên, hàm này nhận vào khóa key,
để xóa một bản ghi trong CSDL thì chúng ta gọi hàm remove().
exports.titles = function(callback) { Note.find().exec(function(err, docs) { if(err) callback(err); else { if(docs) { var noteList = []; docs.forEach(function(note) { noteList.push({ key: note.notekey, title: note.title }); }); callback(null, noteList); } else { callback(); } } }); }
Hàm titles()
sẽ có chức năng tìm toàn bộ ghi chú. Như đã nói ở trên, để lấy toàn bộ bản ghi thì chúng ta gọi hàm find().
Cấu hình project
Chúng ta sửa lại file app.js
như sau:
var express = require('express'); var path = require('path'); var favicon = require('serve-favicon'); var logger = require('morgan'); var cookieParser = require('cookie-parser'); var bodyParser = require('body-parser'); var routes = require('./routes/index'); var users = require('./routes/users'); var notes = require('./routes/notes'); //var models = require('./models-fs/notes'); //var models = require('./models-sqlite3/notes'); var models = require('./models-mongoose/notes'); models.connect("mongodb://localhost/notes", function(err) { if(err) throw err; }); notes.configure(models); routes.configure(models); var app = express(); // view engine setup app.set('views', path.join(__dirname, 'views')); app.set('view engine', 'ejs'); // uncomment after placing your favicon in /public //app.use(favicon(path.join(__dirname, 'public', 'favicon.ico'))); app.use(logger('dev')); app.use(bodyParser.json()); app.use(bodyParser.urlencoded({ extended: false })); app.use(cookieParser()); app.use(express.static(path.join(__dirname, 'public'))); app.use('/', routes.index); app.use('/users', users); app.get('/noteadd', notes.add); app.post('/notesave', notes.save); app.use('/noteview', notes.view); app.use('/noteedit', notes.edit); app.use('/notedestroy', notes.destroy); app.post('/notedodestroy', notes.dodestroy); // catch 404 and forward to error handler app.use(function(req, res, next) { var err = new Error('Not Found'); err.status = 404; next(err); }); // error handlers // development error handler // will print stacktrace if (app.get('env') === 'development') { app.use(function(err, req, res, next) { res.status(err.status || 500); res.render('error', { message: err.message, error: err }); }); } // production error handler // no stacktraces leaked to user app.use(function(err, req, res, next) { res.status(err.status || 500); res.render('error', { message: err.message, error: {} }); }); module.exports = app;
Ở đây chúng ta khai báo module notes
trong thư mục models-mongoose.
Sau đó để kết nối đến CSDL thì chúng ta gọi hàm connect()
đã định nghĩa trong file notes.js
ở trên và truyền vào đường dẫn dạng như sau:
mongodb://localhost/notes
Tiếp theo chúng ta khai báo module mongoose
trong file package.json
như sau:
{ "name": "notes", "version": "0.0.0", "private": true, "scripts": { "start": "node ./bin/www" }, "dependencies": { "body-parser": "~1.15.1", "cookie-parser": "~1.4.3", "debug": "~2.2.0", "ejs": "~2.4.1", "express": "~4.13.4", "morgan": "~1.7.0", "serve-favicon": "~2.3.0", "async": "*", "sqlite3": "*", "mongoose": "*" } }
Cuối cùng bạn chạy lệnh npm install
để cài đặt module.
Bây giờ bạn có thể chạy server để thực hiện các thao tác CRUD như bình thường, và dữ liệu sẽ được lưu trong CSDL MongoDB.
Để kiểm tra, bạn có thể mở lại trình shell của MongoDB và chạy lệnh db.notes.find()
như sau:
C:\Users\PhoCode>mongo MongoDB shell version: 3.2.10 connecting to: test > use notes switched to db notes > db.notes.find() { "_id" : ObjectId("57faf19e77a44c30b858315a"), "body" : "Open source is the future", "title" : "Phở Code", "notekey" : "phocode", "__v" : 0 }