使用Couchbase,Angular和Node.js轻松进行应用程序开发

我的一个朋友想建立一个简单的系统来收集想法和投票。 即使您可以找到许多在线服务来做到这一点,我也认为这是一个很好的机会,展示了使用Couchbase和Node.js开发新应用程序有多么容易。

那么如何开始呢?

我们中的一些人将从UI入手,其他人将从数据入手,在本示例中,我将从模型入手。 基本步骤为:

  1. 为文件建模
  2. 创建视图
  3. 创建服务
  4. 创建UI
  5. 通过迭代改进您的应用程序

Gihub中提供了此示例应用程序的源: https : //github.com/tgrall/couchbase-node-ideas

使用以下命令在本地克隆项目:

git clone https://github.com/tgrall/couchbase-node-ideas.git

注意:我的目标不是提供完整的应用程序,而是描述开发应用程序的关键步骤。

为文件建模

对于此应用程序,您需要3种类型的文档:

  • 想法:用作者,标题和描述来描述想法
  • 投票:作者和评论–请注意,不选择投票价值是一种选择,在第一个版本中,如果存在投票,则意味着用户喜欢这个想法。
  • 用户:包含有关用户的所有信息(在此应用程序的第一个版本中未使用)

您可以争辩说,可以将选票作为想法文档中元素的列表放置。 在这种情况下,由于我们不知道会有多少票/评论,我倾向于使用其他文档并在投票中引用该想法。 由于以下原因,在这种情况下使用不同的文档也很有趣:

  • 没有“并发”访问权限,当用户想要投票时,他不会更改想法文档本身,因此无需进行乐观锁定。
  • 文档的大小将更小,更易于缓存在内存中。

因此文档如下所示:

{
  "type" : "idea",
  "id" : "idea:4324",
  "title" : "Free beer during bug hunt",
  "description" : "It will be great to have free beer during our test campaign!",
  "user_id" : "user:234"
}
{
"type" : "user",
"id" : "user:434",
"name" : "John Doe",
"email" : "jdoe@myideas.com"
}
{
"type" : "vote",
"id" : "vote:usr:434-idea:4324",
"idea_id" : "idea:4324",
"user_id" : "user:434",
"comment" : "This is a great idea, beer is excellent to find bugs!"
}

我真正喜欢的是可以快速创建一个小的数据集以验证其正确性并帮助我设计视图的事实。 执行此操作的方式是,启动服务器,启动Couchbase管理控制台,创建存储桶,最后手动插入文档并验证模型和视图。

创建视图

现在,我已经创建了一些文档,我可以考虑想从数据库中获取信息的方式。 对于此应用程序,我需要:

  • 想法清单
  • 想法投票

第一个版本的想法清单非常简单,我们只需要发出标题即可:

function (doc, meta) {
if (doc.type == "idea") {
emit(doc.title);
}
}

对于想法投票,我选择创建一个整理视图,当我将它们公开到API / View层时,这将给我一些有趣的选择。 对于此视图,我也使用sum()reduce函数以确保获得票数。

function (doc, meta) {
switch (doc.type){
case "idea" :
emit([meta.id,0, doc.title],0);
break;
case "vote" :
emit([doc.idea_id,1],1);
break;
}
}

我有我的文档,我有一些视图,这些视图使我能够检索想法列表,按想法投票的数目并计算投票的数目……因此,我准备使用一个简单的API层将所有这些信息公开给应用程序。

创建服务

最近,我一直在使用Node.js玩很多东西,这是因为学习新知识很不错,而且因为Couchbase真的很容易使用。 想想看,Couchbase喜欢JSON,而Node.js对象格式是JSON,这意味着我没有任何编组/拆组的工作。

我的API层非常简单,我只需要创建一组REST端点即可处理:

  • 对每种类型的文档进行CRUD操作
  • 列出不同的文件

服务的代码在分支01-simple-services中可用:

您可以使用以下命令通过简单的服务运行该应用程序:

> git checkout -f 01-simple-services
  > node app.js

然后使用http://127.0.0.1:3000转到浏览器

关于该项目

对于此项目,我仅使用2个节点模块ExpressCouchbase 。 package.json文件如下所示:

{
    'name': 'couchbase-ideas-management',
    'version': '0.0.1',
    'private': true,
    'dependencies':
    {
        'express': '3.x',
        'couchbase': '0.0.11'
    }
}

运行安装后,让我们编写新的API接口的代码,就像在我使用迭代方法之前所说的那样,所以现在我不涉及安全性,我只想让基本操作起作用。

我从端点开始获取和设置文档。 我正在创建一个通用端点,该端点将类型用作URI参数,从而允许用户/应用程序对/ api / vote,/ api / idea进行获取/发布。 以下代码捕获了这一点:

// get document
 app.get('/api/:type/:id', function(req, res) {
  	if (type == 'idea' || type == 'vote' || type == 'user') {
		get(req, res, type);
	} else {
		res.send(400);
	}
});

// create new document
app.post('/api/:type', function(req, res) {
	if (type == 'idea' || type == 'vote' || type == 'user') {
		upsert(req, res, type);
	} else {
		res.send(400);
	}
});

在每种情况下,我都开始测试URI是否为受支持的类型之一(想法,投票,用户),如果是这种情况,我将调用get()或upsert()方法来调用Couchbase。

get()和upsert()方法或多或少使用相同的方法。 我测试文档是否存在,类型是否正确,然后对Couchbase进行操作。 让我们关注upsert()方法。 我将其称为upsert(),因为使用了相同的操作来创建和更新文档。

function upsert(req, res, docType) {
	// check if the body contains a know type, if not error
	if (req.body != null && req.body.type == docType) {
		var id = req.body.id;
		if (id == null) {
			// increment the sequence and save the doc
			cb.incr("counter:"+req.body.type, function(err, value, meta) {
				id = req.body.type + ":" + value;
				req.body.id = id;
				cb.add(id, req.body, function(err, meta) {
					res.send(200);
				});
			});
		} else {
			cb.replace(id, req.body, function(err, meta) {
				res.send(200);
			});
		}
	} else {
		res.send(403);
	}
}

在此功能中,我首先测试文档是否包含一种类型,以及该类型是否为预期的类型(第3行)。 然后,我检查是否存在文档ID,以查看是否需要创建它。 这就是为什么我喜欢将id / key保留在文档中的原因之一,是的,我复制了它,但这确实使开发变得容易。 因此,如果必须创建一个新文档,则必须生成一个新的ID。 我选择为每种类型创建一个计数器。 这就是为什么我调用incr函数(第7行),然后使用返回的值创建文档(第10行)的原因。

注意:如您所见,我的文档包含ID作为属性的一部分。 此ID与用于设置文档的ID(“键”)的ID相同。 复制这些信息并不一定是一种好习惯,并且在许多情况下,应用程序仅使用文档密钥本身。 我个人也喜欢将ID放在文档本身中,因为它大大简化了开发过程。

如果存在该ID,我只需调用更新操作即可保存文档。 (第15行)

delete操作与使用delete HTTP操作的get等效。

现在,我可以获取,插入和更新文档。 我仍然需要做一些工作来处理列表。 如您所料,在这里我需要调用视图。 我不会在简单的想法列表中详细介绍。 让我们集中于显示投票结果的视图。

app.get('/api/results/:id?', function(req, res) {
	var queryParams = {
		stale: false,
		group_level : 3
	};
	if (req.params.id != null) {
		queryParams.startkey = [req.params.id,0];
		queryParams.endkey = [req.params.id,2];
	}
 
	cb.view("ideas", "votes_by_idea", queryParams, function(err, view) {
		var result = new Array();
		var idx = -1;
		var currentKey = null;
		for (var i = 0; i < view.length; i++) {
			key = view[i].key[0];
			if (currentKey == null || currentKey != key ) {
				idx = idx +1;
				currentKey = key;
				result[idx] = { id : key, title : view[i].key[2], value : 0 };
			} else {
				result[idx].value = view[i].value;
			}
		}
		res.send(result);
	});		
});

对于应用程序的这一部分,我将使用一个小技巧来使用整理后的视图。 / api / results /调用返回想法列表及其标题和投票总数。 结果如下所示:

[
    {
        "id": "idea:0",
        "title": "Add new electric company cars",
        "value": 0
    },
    {
        "id": "idea:1",
        "title": "Develop new blog on Jekyll",
        "value": 3
    },
    {
        "id": "idea:2",
        "title": "Bring your own device project",
        "value": 1
    },
    {
        "id": "idea:3",
        "title": "Test the new Rasperry Pi",
        "value": 1
    }
]

请注意,也可以只选择一个想法,例如,您只需要将ID传递给通话即可。 如果您更详细地查看该函数,不仅会调用该视图,而且还会构建一个数组,在其中放置想法ID,标签,然后在下一个循环中添加投票数。 这是可能的,因为该视图是思想及其投票的整理视图。 我现在有了REST服务,包括高级查询功能。 现在是时候使用这些服务并构建用户界面了。

创建UI

对于我正在使用AngularJS的视图,出于简化原因,我将其打包在同一node.js应用程序中

简单的用户界面,无需登录/安全

没有登录的应用程序代码可在02-simple-ui-no-login中找到

您可以使用以下命令通过简单的服务运行该应用程序:

> git checkout -f 02-simple-ui-no-login
  > node app.js

该应用程序基于AngularJS和Twitter Boostrap。

我正在使用Angular的基本功能和包装:

  • /public/js/app.js包含模块声明和指向不同视图/控制器的所有路由
  • /public/js/controllers.js包含所有控制器。 我将展示其中的一些,但是基本上,这就是我在上面创建的服务的调用位置。
  • / views / partials /包含应用程序使用的不同页面/屏幕。

因为应用程序非常简单,所以我没有完成指令或其他函数的任何打包。 对于AngularJS和Node.js部件,这是正确的。

虚拟用户管理

在用户界面的第一个版本中,我尚未集成任何登录名/安全性,因此我使用全局范围变量$ scope.user伪造了用户登录名,您可以在控制器AppCtrl()中看到该变量。 由于尚未实现登录/安全性,因此在页面底部添加了一个文本字段,您可以在其中输入“虚拟”用户名来测试应用程序。 该字段插入/views/index.html页面。

列表视图和投票数

该应用程序的主页包含想法列表和投票数。

查看EntriesListCtrl控制器和view / index.html文件。 可以猜到,这是基于Couchbase排序视图的,该视图返回了想法列表和投票数。

创建/编辑想法

当用户单击导航中的“新建”链接时,应用程序将调用view / view / partials / idea-form.html。 使用“ /#/ idea / new” URL调用此表单。

只需查看IdeaFormCtrl控制器即可了解正在发生的情况:

function IdeaFormCtrl($rootScope, $scope, $routeParams, $http, $location) {
  $scope.idea = null;
	if ($routeParams.id ) {
		$http({method: 'GET', url: '/api/idea/'+ $routeParams.id }).success(function(data, status, headers, config) {			
				$scope.idea = data;
			});
	}

	$scope.save = function() {			
		$scope.idea.type = "idea"; // set the type
		$scope.idea.user_id = $scope.user;
		$http.post('/api/idea',$scope.idea).success(function(data) {
			$location.path('/');
		});
	}
	$scope.cancel = function() {
		$location.path('/');
	}

}
IdeaFormCtrl.$inject = ['$rootScope', '$scope', '$routeParams','$http', '$location'];

首先,我测试是否使用URL($ routeParams.id –第3行)中的提示标识符调用控制器。 如果存在ID,我将调用REST API来获取想法并将其设置为
$ scope.idea变量。 然后在第9行,您可以看到$ scope.save()函数,该函数调用REST API将想法保存/更新到Couchbase。 我使用第10行和第11行来设置用户和想法的数据类型。

注意:通过添加两个属性(用户和类型)来修改数据的“模式”,以查看这些行很有趣。 我正在向文档添加新字段,该字段将按原样存储在Couchbase中。 再一次,您在这里看到我从我的应用程序驱动数据类型。 我可以采取另一种方法,并在服务层中强制使用该类型。 对于此示例,我选择将其放置在应用程序层中,以发送适当的数据类型。

其他互动

正如您在VoteFormCtrl控制器中看到的那样,使用相同的方法来创建与用户/想法相关联的投票。 我不会介绍所有操作的所有细节,我只是邀请您查看应用程序的代码,如果需要澄清应用程序的其他部分,可以随时在此博文中添加评论。

迭代开发:为投票增值!

服务的代码在分支01-simple-services中可用:

您可以使用以下命令通过简单的服务运行该应用程序:

> git checkout -f 03-vote-with-value 
  > node app.js
在表单中添加字段

我真正喜欢使用AngularJS,Node和Couchbase的事情是开发人员使用从数据库到浏览器的JSON。

因此,让我们实现一个新功能,用户可以只给出1到5的投票,而不仅仅是发表评论。这样做很简单,请按以下步骤操作:

  • 修改用户界面:添加新字段
  • 修改Couchabe视图以使用新字段

就是这个! AngularJS处理新字段的绑定,因此我只需要编辑/views/partials/idea-form.html即可。 为此,我需要在控制器中添加值列表,并将其公开到窗体的选择框中。

位于$ scope.ratings变量中的值列表:

$scope.ratings = [
  {
  	"id": "0",
  	"label": "0 - No Interest",
	},
  	{
    	"id": "1",
    	"label": "1 - Low Interest",
  	},
  	{
    	"id": "2",
    	"label": "2 - Medium",
  	},
  	{
    	"id": "3",
    	"label": "3 - Good",
  	},
  	{
    	"id": "4",
    	"label": "4 - Outstanding",
  	}, 
  	{
    	"id": "5",
    	"label": "5 - Must be done. Now!",
  	}];

完成此操作后,您可以使用以下代码将选择框添加到视图中:

<div class="control-group">  
	<label class="control-label" >Rate</label>  
	<div class="controls">  
      		<select required ng-model="vote.rating" ng-options="value.id as value.label group by value.group for value in ratings">
      		</select>
	</div>
</div>

要将选择框添加到表单中,我仅使用AngularJS功能:

  • 我的控制器中使用ng-options属性描述的值列表
  • 使用ng-model属性绑定到vote.rating字段对象。

我在表单中添加该字段,将该字段绑定到我的Javascript对象; 还有……没别的! 由于我的REST API只是按原样使用JSON对象,因此AngularJS将发送具有新属性的投票对象。

更新视图以使用评分

现在,我的数据库正在处理表决中的新属性,我需要更新视图以在sum函数中使用它。 (我也可以计算平均值,但在这里我想要所有投票/评分的总和)。

function (doc, meta) {
  switch (doc.type){
    case "idea" :
      emit([meta.id,0, doc.title],0);
      break;
    case "vote" :
      emit([doc.idea_id,1], (doc.rating)?doc.rating:2 );
      break;
  }  
}

我唯一更改的行是行号7。逻辑很简单,如果存在等级,我发出它,如果没有等级,我发出2,这是一个想法的中等等级。

这是一个小技巧,可以让我拥有一个有效的视图/系统,而不必更新所有现有文档(如果有的话)。 我现在将在此处停止,稍后再添加新功能,例如使用Passport进行用户身份验证和用户管理。

版本和升级管理

如果您仔细查看应用程序的代码,则在应用程序启动时,视图将从app.js文件自动导入。 实际上,我添加了一个小功能,可以检查当前安装的版本,并在需要时使用正确的版本更新视图。 您可以看一下函数initApplication()

  • 从Couchbase加载版本号(ID为'app.version'的文档)
  • 检查版本是否不同
    • 更新/创建视图(我在这里是在生产模式下完成的,在实际应用中,最好使用dev模式–只需在设计文档ID前面加上'dev_'即可)

结论

在本文中,我们了解了如何快速开发应用程序/原型并为开发人员利用NoSQL的灵活性。 为此,请执行以下步骤:

  1. 设计文档模型和API(REST)
  2. 创建使用API​​的UI
  3. 只需在UI中添加字段即可修改模型
  4. 更新视图以使您的列表适应新模型

除此之外,我还将快速说明如何通过代码控制应用程序的版本并自动部署新视图(及其他内容)。

参考:来自Tug博客博客的JCG合作伙伴 Tugdual Grall 使用Couchbase,Angular和Node.js轻松进行应用程序开发

翻译自: https://www.javacodegeeks.com/2013/03/easy-application-development-with-couchbase-angular-and-node-js.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值