在这个三个部分的教程中,我们教大家使用Node.js和Geddy来创建了一个todo的管理应用,上一篇我们将数据保存在内存中,在这个系列最后一篇文章中,我们将介绍如何将todo项目保存到mongodb中。
MongoDB是一个由10gen开发的Nosql类型的数据库。这是node应用可以使用的超棒的数据库,它将数据保存为JSON格式。并且所有的查询都是使用javascript开发的。如果你想深入了解Mongdb,请参考这两篇文章:Nosql数据库教程之初探MongoDB - 第一部分和Nosql数据库教程之初探MongoDB - 第二部分。相信大家会有个基本的使用概念。
到如下地址下载mongodb:
http://www.mongodb.org/downloads
安装很简单,你可以参考上面文章内容。
对于我们的应用来说,我们使用一个模块来包装mongdb-native数据驱动。这很大的简化了代码,我们安装后进入你的应用并且执行如下:
npm install mongodb-wrapper
如果没有错误的话,你可以看到一个mongdb-wrapper目录出现在你的node_modules目录。
Mongdb非常简单,你不需要担心设置表,列或者数据库,简单的连接到数据库。就创建了一个!只需要添加到collection。接下来我们设置我们的应用。
我们需要应用范围中访问DB ,所以我们添加代码到config/init.js。你可以看到如下:
// Add uncaught-exception handler in prod-like environments if (geddy.config.environment != 'development') { process.addListener('uncaughtException', function (err) { geddy.log.error(JSON.stringify(err)); }); } geddy.todos = []; geddy.model.adapter = {}; geddy.model.adapter.Todo = require(process.cwd() + '/lib/model_adapters/todo').Todo; Let’s add in our db code at the very top (and remove the geddy.todos array while we’re at it): var mongo = require('mongodb-wrapper'); geddy.db = mongo.db('localhost', 27017, 'todo'); geddy.db.collection('todos'); // Add uncaught-exception handler in prod-like environments if (geddy.config.environment != 'development') { process.addListener('uncaughtException', function (err) { geddy.log.error(JSON.stringify(err)); }); } geddy.model.adapter = {}; geddy.model.adapter.Todo = require(process.cwd() + '/lib/model_adapters/todo').Todo;
首先我们要求mongdb-wrapper模块。然后我们设置我们的数据库,添加collection到数据库中,几乎不需要做太多。
Geddy并不在乎你使用的后台,只要是使用MVC架构书写就好。这意味着你需要修改数据写入的部分都在model-adapter中。同时也注意这是一个adapter的完全重写,所以如果你需要保持使用内存保存数据的话,你需要将它备份到其它目录中。
打开model-adapter(lib/model_adapters/todo.js),找到save方法。应该如下:
this.save = function (todo, opts, callback) { if (typeof callback != 'function') { callback = function(){}; } var todoErrors = null; for (var i in geddy.todos) { // if it's already there, save it if (geddy.todos[i].id == todo.id) { geddy.todos[i] = todo; todoErrors = geddy.model.Todo.create(todo).errors; return callback(todoErrors, todo); } } todo.saved = true; geddy.todos.push(todo); return callback(null, todo); }
修改成如下:
this.save = function (todo, opts, callback) { // sometimes we won't need to pass a callback if (typeof callback != 'function') { callback = function(){}; } // Mongo doesn't like it when you send functions to it // so let's make sure we're only using the properties cleanTodo = { id: todo.id , saved: todo.saved , title: todo.title , status: todo.status }; // Double check to see if this thing is valid todo = geddy.model.Todo.create(cleanTodo); if (!todo.isValid()) { return callback(todo.errors, null); } // Check to see if we have this to do item already geddy.db.todos.findOne({id: todo.id}, function(err, doc){ if (err) { return callback(err, null); } // if we already have the to do item, update it with the new values if (doc) { geddy.db.todos.update({id: todo.id}, cleanTodo, function(err, docs){ return callback(todo.errors, todo); }); } // if we don't already have the to do item, save a new one else { todo.saved = true; geddy.db.todos.save(todo, function(err, docs){ return callback(err, docs); }); } }); }
不要被这些代码弄晕了,这是最复杂的一个。记住我们的save方法必须同时处理新的todo和更新todo俩个操作。接下来我们详细介绍代码。
我们使用同样的callback方法,如果我们没有callback方法,那么使用一个空的方法。
然后我们处理todo项目。我们必须这样做的原因在于我们的todo对象拥有javascript的方法(类似save),但是Mongo不希望你们传递对象的时候包含方法。因此我们需要创建一个新的对象,只包含我们需要的属性。
然后,我们判断todo是否合法。如果不的话,我们调用带有验证错误的callback 。如果合法,就继续。
为 了避免我们在数据库中已经包含了todo数据,我们检查是否todo存在。这里开始我们使用mongodb-wrapper模块。它提供了我们一个干净的 API来和数据库交互。这里我们使用db.todos.findOne()方法来找到一个简单document来满足我们的查询。我们的查询是一个简单 js对象 - 我们查找一个id和todo的id一样的documen。如果我们找到一个并且没有报错,我们使用db.todos.update()方法来更新 document中的数据。如果没有找到,我们使用db.todos.save()方法来保存一个新的包含todo项目的document。
在所有的情况中,完成后我们都调用一个callback,所有我们得到的错误和db返回的docs都将传递到这个callback中。
首先我们看看all方法,应该类似下面:
this.all = function (callback) { callback(null, geddy.todos); }
我们修改成这样:
this.all = function (callback) { var todos = []; geddy.db.todos.find().sort({status: -1, title: 1}).toArray(function(err, docs){ // if there's an error, return early if (err) { return callback(err, null); } // iterate through the docs and create models out of them for (var i in docs) { todos.push( geddy.model.Todo.create(docs[i]) ) } return callback(null, todos); }); }
比 save方法简单多了,对吧?我们使用db.todos.find()方法得到所有todos集合中的项目。我们使用mongdb-wrapper的 API通过status和标题来排序结果(降序排列)。然后我们传递到array,这个数组将触发查询。一旦我们得到数据,我们看看是否有错误,如果有, 我们调用错误的callback,如果没有,则继续。
然后,我们循环所有的docs(mongo返回的所有document内容),创建新的todo model实例,将他们推送到todos数组中。完毕后,我们调用callback,传递到todos。
看看load方法,如下:
this.load = function (id, callback) { for (var i in geddy.todos) { if (geddy.todos[i].id == id) { return callback(null, geddy.todos[i]); } } callback({message: "To Do not found"}, null); };
修改成:
this.load = function (id, callback) { var todo; // find a todo in the db geddy.db.todos.findOne({id: id}, function(err, doc){ // if there's an error, return early if (err) { return callback(err, null); } // if there's a doc, create a model out of it if (doc) { todo = geddy.model.Todo.create(doc); } return callback(null, todo); }); };
这个更加简单。我们再次使用db.todos.findOne()方法。这一次,我们就这么用。如果遇到错误,我们调用callback,如果没有继续。如果我们得到了一个doc,我们创建一个新的todo model实例并且调用callback。
看看remove方法如下:
this.remove = function(id, callback) { if (typeof callback != 'function') { callback = function(){}; } for (var i in geddy.todos) { if (geddy.todos[i].id == id) { geddy.todos.splice(i, 1); return callback(null); } } return callback({message: "To Do not found"}); };
修改为:
this.remove = function(id, callback) { if (typeof callback != 'function') { callback = function(){}; } geddy.db.todos.remove({id: id}, function(err, res){ callback(err); }); }
这个remove方法更加简单。使用db.todos.remove()方法来删除所有指定id的document并且调用一个带有error的callback。
最后让我们测试一下,cd到你的项目目录,使用geddy启动服务器。创建一个todo,编辑,使用错误的输入让它报错,删除,是不是都可以正常工作吧!
我们希望通过这篇文章大家能够学习到Node.js,MongoDB和Geddy。如果你有什么好的建议或者意见,请给我们留言。谢谢!