hbase和couchdb
在本教程中,我们将使用CouchDB作为后端并选择Angular作为前端技术来构建应用程序。 CouchDB是NoSQL数据库,而Angular是更新JavaScript MVC框架之一。 令人兴奋和令人敬畏的是,CouchDB是具有HTTP API的数据库–我们的客户端应用程序将直接与该数据库通信:CouchDB将充当我们客户端应用程序所需的唯一后端!
我们将专注于一个小型应用程序来跟踪我们的费用。 每个步骤都会有一个提交,有时该提交还包括测试。 测试不是本教程中的主题,但是如果您对此感兴趣,则应该看看! 您将在GitHub的存储库中找到本教程中使用的全部代码。
为什么选择CouchDB?
你们中有些人可能会说我们可以使用客户端替代方法。 IndexedDB或Local Storage是在客户端本地工作以持久化数据的技术。 但是使用数据库服务器有几个优点:我们可以将许多客户端连接到我们的应用程序。 当您独自在另一个超市中时,您的伴侣可以更新费用清单,这也会增加费用。
使用CouchDB有很多优点:CouchDB本机“说” HTTP,因此我们在数据库和应用程序之间不需要其他层。 我们JavaScript应用程序可以使用CouchDB提供的RESTful接口直接与CouchDB数据库通信!
而且,如果我们想对数据库使用复制,则就像切面包一样容易:因为CouchDB是为创建分布式数据库系统而设计的。
要求
对于本教程,您将需要安装最新版本的CouchDB(1.6)和最新的稳定Node.js(当前为0.10.x)版本。
安装Node.js和Yo
作为Mac用户,您可以在Node主页上获得官方安装程序。 在Linux和OSX上管理Node.js安装的另一种方法是Tim Caswell的出色nvm 。
我们将安装Yo来搭建我们的应用程序。 在创建骨架的过程中,您会问我们一些问题。 Yo询问我们是否要使用SASS,并且不确定是否要回答“否”,但是我们绝对希望包括Bootstrap和预选的Angular-Modules。
在我们的外壳中键入:
npm install -g yo generator-angular grunt-cli couchapp
mkdir expenses && cd expenses
yo angular expenses
作为我们脚手架的一部分,Yo为我们创建了一个Gruntfile(Gruntfile.js)。 Grunt是JavaScript的任务执行者,它具有许多已经编写的插件,可以自动执行任务并使您的生活更轻松。
使用grunt serve
命令启动开发服务器,并在grunt任务完成后在浏览器中打开http://127.0.0.1:9000
。 下图显示了一个示例。
安装CouchDB
有很棒的文档可以在很多平台上安装CouchDB –有适用于所有主要操作系统的软件包,在OSX上,您可以使用brew安装CouchDB。
CouchDB的第一步
让我们启动第一个CouchDB实例并创建一个数据库:
couchdb & # start a CouchDB
curl -X PUT http://127.0.0.1:5984/expenses # create the database expenses
CouchDB回答:
{"ok":true}
我们刚刚使用HTTP创建了第一个数据库!
让我们进一步研究CouchDB的HTTP API:现在我们可以插入第一个文档,假设我们要跟踪购买的爆米花(稍后我们的应用程序需要对CouchDB的这些调用)。
curl -X POST http://127.0.0.1:5984/expenses -H "Content-Type: application/json" -d '{"name": "Popcorn", "price": "0.99"}'
CouchDB的答案:
{"ok":true,"id":"39414de82e814b6e1ca754c61b000efe","rev":"1-2b0a863dc254239204aa5b132fda8f58"}``
现在,我们可以使用GET请求和CouchDB分配给我们文档的ID来访问文档,因为我们没有提供特定的ID:
curl -X GET http://127.0.0.1:5984/expenses/39414de82e814b6e1ca754c61b000efe
免费学习PHP!
全面介绍PHP和MySQL,从而实现服务器端编程的飞跃。
原价$ 11.95 您的完全免费
CouchDB的答案:
{"_id":"39414de82e814b6e1ca754c61b000efe","_rev":"1-2b0a863dc254239204aa5b132fda8f58","name":"Popcorn","price":"0.99"}
之后,我们插入另一个文档:
curl -X POST http://127.0.0.1:5984/expenses -H "Content-Type: application/json" -d '{"name": "Washing powder", "price": "2.99"}'
配置:带有CouchDB的CORS
我们的客户将通过HTTP从CouchDB本身以外的其他位置进行通信。 为了使此功能在我们的浏览器中起作用,我们必须在CouchDB中启用CORS(跨源资源共享)。
在这种情况下,我们要为本地自定义更改修改local.ini
。 可以通过HTTP修改配置。 在https
部分中,我们启用CORS,然后使用通配符配置源:
curl -X PUT http://localhost:5984/_config/httpd/enable_cors -d '"true"'
curl -X PUT http://localhost:5984/_config/cors/origins -d '"*"'
使用这两个命令,我们正在更改CouchDB的local.ini
。 您可以使用couchdb -c
找出local.ini
的位置。
重要! 请注意,如果您将应用程序部署到生产环境中,则可能需要更改“来源”部分。 此处提供的所有设置仅用于开发!
角度和依赖注入
在app/scripts/app.js
我们将找到应用程序的主要JavaScript文件,实际上这就是所谓的Angular模块。 此模块将其他一些模块作为依赖项加载(例如ngCookies
)。 在此文件中,我们还使用$routeprovider
应用程序的客户端路由。
该文件中的$routeprovider
是Angular依赖项注入(DI)的一个很好的示例。 通过定义您要使用的服务的名称,Angular将其注入给定的功能范围。 您可以在docs中找到有关Angular依赖注入的更多信息。
因为我们希望在一个中央位置拥有连接到CouchDB所需的数据,所以让我们尝试使用具有常数的DI。 我们使用链接将它们添加到我们的模块中:
.constant('appSettings', {
db: 'http://127.0.0.1:5984/expenses'
});
我们迄今为止唯一控制器,它在初始支架期间创建,是MainCtrl
位于app/scripts/controllers/main.js
。 将定义MainCtrl
并注入$scope
。 稍后我们将看到如何使用范围。
现在我们可以将appSettings
添加到函数参数中以注入它们,就像我们之前使用$routeprovider
看到的$routeprovider
:
.controller('MainCtrl', function ($scope, appSettings) {
console.log(appSettings);
});
现在,您应该能够在浏览器的调试控制台上记录输出。 恭喜你! 您已成功使用依赖项注入。 您可以在以下位置找到完整的提交: https : //github.com/robertkowalski/couchdb-workshop/commit/d6b635a182df78bc22a2e93af86162f479d8b351 。
取得结果
在下一步中,我们将注入$http
服务以从CouchDB中获取数据并更新视图。 当传统的数据库使用分解为表的数据时,CouchDB使用的是非结构化文档,可以使用地图和归约功能通过称为视图的概念来聚合,过滤和连接非结构化文档。 视图由设计文档(一种特殊的文档)定义。
您可以自行编写视图,然后通过curl将其发送到CouchDB,使用位于http://localhost:5984/_utils
的图形界面或通过诸如CouchApp之类的工具–有许多诸如CouchApp之类的工具( npm install -g couchapp
),以npm install -g couchapp
视图的开发和部署。
这是我们的视图:
{
"_id":"_design/expenses",
"views": {
"byName": {
"map": "function (doc) {
emit(doc.name, doc.price);
}"
}
}
}
_id
对我们很重要,因为它定义了以后查询视图的路径。 在创建设计文档时, _id
属性以_design
为前缀。 我们将视图byName
,它仅包含一个基本的map函数,该函数将发出数据库中每个文档的name属性作为键,并将price作为值。
让我们使用curl将其发送到CouchDB:
curl -X POST http://127.0.0.1:5984/expenses -H "Content-Type: application/json" -d '{"_id":"_design/expenses","views": {"byName": {"map": "function (doc) {emit(doc.name, doc.price);}"}}}'
CouchDB响应:
{"ok":true,"id":"_design/expenses","rev":"1-71127e7155cf2f780cae2f9fff1ef3bc"}
现在,我们可以在以下位置查询:
http://localhost:5984/expenses/_design/expenses/_view/byName
如果您对诸如CouchApp之类的工具感兴趣(提示:您稍后必须使用它),那么这里的提交显示了如何使用它(使用npm run bootstrap
来部署设计文档)。
您还记得我们一开始的卷曲要求吗? 现在,我们将用JavaScript实现它们。 Angular提供$http
服务,可以如下所示进行注入:
.controller('MainCtrl', function ($scope, $http, appSettings) {
然后,我们添加一个函数以使用$http
服务获取项目:
function getItems () {
$http.get(appSettings.db + '/_design/expenses/_view/byName')
.success(function (data) {
$scope.items = data.rows;
});
}
getItems();
$http
服务返回一个promise,它将从CouchDB视图提供给我们JSON数据。 我们正在将数据添加到$scope.items
。 使用$scope
我们可以在视图中设置和更新值。 如果模型的值发生变化,则视图将自动更新。 Angular的双向绑定使视图和模型之间的数据同步。 控制器更改模型后,它将立即更新视图,并且当视图中的值更改时,还将更新模型。
在删除大部分样板标记后,让我们添加一些带有表达式HTML以在app/views/main.html
显示我们的项目:
<div>{{ item[0].key }}</div>
<div>{{ item[0].value }}</div>
我们将在“使用CouchDB的第一步”部分中看到添加的第一项:
该部分的提交可在GitHub上获得。
使用指令: ng-repeat
现在,我们应该看到第一个项目,但是其他所有项目呢?
我们可以在此处使用ng-repeat
指令,它将为我们从更长的列表中构建标记。 通常,我们可以说Angular中的指令将行为附加到DOM元素。 Angular中还有很多其他预定义的指令,您也可以定义自己的指令。 在这种情况下,我们将ng-repeat="item in items"
到外部div
,然后它将从$scope.items
迭代数组items
。
类pull-left
和pull-right
是Bootstrap CSS的一部分,并为我们提供了浮动元素。 由于元素是浮动的,因此我们正在应用一个clearfix
,它也包含在Bootstrap中:
<div ng-repeat="item in items">
<div class="clearfix">
<div class="pull-left">{{ item.key }}</div>
<div class="pull-right">{{ item.value }}</div>
</div>
</div>
如果刷新页面,则项目将在DOM检查器中呈现为:
<!-- ngRepeat: item in items -->
<div ng-repeat="item in items" class="ng-scope">
<div class="clearfix">
<div class="pull-left ng-binding">Popcorn</div>
<div class="pull-right ng-binding">0.99</div>
</div>
</div>
<!-- end ngRepeat: item in items -->
<div ng-repeat="item in items" class="ng-scope">
<div class="clearfix">
<div class="pull-left ng-binding">Washing powder</div>
<div class="pull-right ng-binding">2.99</div>
</div>
</div>
<!-- end ngRepeat: item in items -->
我们现在有一个不错的小清单,但是除了使用curl之外,还没有办法通过我们的应用程序提交新项目。 到目前为止, 此提交中的应用程序可用,如下图所示。
创建用于提交项目的表格
我们将添加一个包含两个输入的表单:一个输入商品名称,另一个输入价格。 该表格还具有一个用于提交我们的物品的按钮。
来自Bootstrap的class="row"
的div
用于以响应方式设置应用程序的样式。 Bootstrap类(如form-control
和btn btn-primary
用于设置按钮和输入的样式。
表单还获得了novalidate
属性:它禁用了浏览器的本机表单验证,因此我们稍后可以使用Angular来验证表单:
<form class="form-inline" role="form" novalidate>
<div class="row">
<div class="form-group">
<label class="sr-only" for="item-name">Your item</label>
<input
class="form-control"
id="item-name"
name="item-name"
placeholder="Your item" />
</div>
<div class="form-group">
<label class="sr-only" for="item-price">Price</label>
<input
class="form-control"
id="item-price"
name="item-price"
placeholder="Price" />
</div>
</div>
<div class="row">
<button
class="btn btn-primary pull-right"
type="submit">Save</button>
</div>
</form>
表单的提交位于https://github.com/robertkowalski/couchdb-workshop/commit/d678c51dfff16210f1cd8843fbe55c97dc25a408 。
在CouchDB中保存数据
使用ng-model
我们可以观察并访问控制器中输入的值,然后将其发送到CouchDB。 对于我们的价格输入,我们将添加属性ng-model="price"
:
<input
class="form-control"
ng-model="price"
id="item-price"
name="item-price"
placeholder="Price" />
名称的输入将获得ng-model="name"
属性。 看起来像这样:
<input
class="form-control"
ng-model="price"
id="item-price"
name="item-price"
placeholder="Price" />
我们还在最后一项下面添加了一个小状态框。 我们将需要它来显示错误。
<div class="status">
{{ status }}
</div>
现在,我们可以使用$scope.price
和$scope.name
访问控制器中的值。 范围将视图连接到我们的控制器。 看一下Model-View-Controller(MVC)模式,范围就是我们的模型。 Angular有时也称为MVVM(模型-视图-视图-模型)框架-所有这些JavaScript MVC框架通常被称为MVW(模型-视图-任何模型),因为它们之间存在许多细微的差异。
但是,我们如何提交表格?
发送表单的常见方法是在$scope
上定义一个函数,并在视图中结合ng-submit
指令。 我们的函数将构建要发送到CouchDB的JSON。 创建JSON之后, processForm
将调用postItem
,它将把JSON发送到CouchDB:
$scope.processForm = function () {
var item = {
name: $scope.name,
price: $scope.price
};
postItem(item);
};
function postItem (item) {
// optimistic ui update
$scope.items.push({key: $scope.name, value: $scope.price});
// send post request
$http.post(appSettings.db, item)
.success(function () {
$scope.status = '';
}).error(function (res) {
$scope.status = 'Error: ' + res.reason;
// refetch items from server
getItems();
});
}
我们的函数postItem
发生了很多事情:
在将HTTP请求发送到数据库之前,我们正在对用户界面进行乐观更新,因此用户可以立即看到更新,并且我们的应用程序感觉更加敏捷。 为此,我们将项目添加到合并范围内的其他项目。 Angular将为我们更新视图。
然后,我们在后台对项目进行POST请求,成功后,我们将从状态字段中删除所有(先前的)错误消息。
如果发生错误,我们正在向视图中写入错误消息。 CouchDB将告诉我们为什么错误发生在返回的JSON的reason
属性中。 为了再次获得一致的视图,我们在收到错误后重新获取项目列表。
现在,我们可以在表单上添加指令ng-submit
,当提交表单时,它将在作用域上调用我们的函数:
<form class="form-inline" role="form" novalidate ng-submit="processForm()">
就是这样! Angular帮助我们保持了最新观点! 查看最新提交 。
添加验证
您可能已经注意到,我们可以在费用应用程序中放入各种值。 人们可以在价格中添加诸如foo
之类的无效字符串,然后将其发送到服务器。 因此,让我们添加一些服务器端验证:CouchDB能够在更新时验证文档。 我们只需要在设计文档中添加一个带有函数的validate_doc_update
字段即可。 如果数据无效,则此函数应引发异常。
该函数具有四个参数,如下所示:
validate_doc_update: function (newDoc, oldDoc, userCtx, secObj) {
// ...
}
newDoc
是将被创建或用于更新的文档。 还有参数oldDoc
, userCtx
和secObj
用于更复杂的验证,但是我们将仅使用newDoc
进行验证:
如果您还没有使用已经提到的CouchApp,那么我真的建议您现在使用,因为它使处理较大的设计文档变得更加容易。 这是CouchApp的设计文档:
var ddoc = {
_id: '_design/expenses',
views: {},
lists: {},
shows: {},
validate_doc_update: function (newDoc, oldDoc, userCtx, secObj) {
if (newDoc._deleted === true) {
return;
}
if (!newDoc.name) {
throw({forbidden: 'Document must have an item name.'});
}
if (!newDoc.price) {
throw({forbidden: 'Document must have a price.'});
}
if (!/\d+\.\d\d/.test(newDoc.price)) {
throw({forbidden: 'Price must be a number and have two decimal places after a dot.'});
}
}
};
// _design/expenses/_view/byName
ddoc.views.byName = {
map: function (doc) {
emit(doc.name, doc.price);
}
};
module.exports = ddoc;
验证中undefined
字段name
和price
。 此外,我们正在使用正则表达式测试价格格式。 如果我们只想删除文档,则不需要任何验证。 我们正在使用以下命令更新设计文档:
couchapp push couchdb/views.js http://localhost:5984/expenses
现在,当我们尝试保存无效值时,应该看到错误,如下图所示:
这是相关的提交 。
向前端添加验证
我们现在在服务器上进行了一些验证真是太棒了,但是如果我们不需要请求来验证我们的文档,那岂不是更棒了吗? 让我们使用Angular添加一些验证。
我们的两个输入都是必需的,因此它们都具有required
属性。 您还记得设计文档的validate函数中的正则表达式吗? 指令ng-pattern
使用正则表达式检查我们的输入:
<input
class="form-control"
ng-model="price"
id="item-price"
name="item-price"
placeholder="Price"
required
ng-pattern="/\d+\.\d\d$/"/>
使用name-of-the-form.$invalid
我们可以测试输入之一是否无效。 由于我们的表单具有名称-属性form
我们将使用form.$invalid
。 我们可以将此值与ng-disabled
类的指令结合使用,如果表单的值无效或缺失,它将禁用我们的提交按钮:
<button
class="btn btn-primary pull-right"
type="submit"
ng-disabled="form.$invalid">Save</button>
而已! 仅用几行HTML,我们就获得了很好的验证。 查看最新的提交 ,包括测试。
结论
我们已经学习了如何使用CouchDB和Angular构建一个小型应用程序。 Angular和CouchDB为我们做了很多繁重的工作。 我们看了看:
- CouchDB HTTP接口
- CouchDB视图和验证
- Angular的依赖注入
- Angular的双向数据绑定
- 角度指令
- 在Angular中使用验证
Angular和CouchDB是很棒的开发工具,它们可以帮助我们在走向正常应用程序的途中。 我希望您对CouchDB和Angular有一个初步的了解,如果您有兴趣,仍然可以查看许多主题:
- 在CouchDB本身上托管应用程序
- 更新文件
- 编写自己的指令
- 复写
- 在我们看来使用reduce函数
- 测试Angular应用
翻译自: https://www.sitepoint.com/tracking-expenses-couchdb-angular/
hbase和couchdb