由 CouchDB 提供的移动应用程序





0/5 (0投票)
一个从 CouchDB 提供的移动应用程序。
“一个将 DBMS 作为应用程序服务器?是 Lotus Notes 吗?”
CouchDB
CouchDB 在 2009 年对我来说显得很奇怪,因为它……没有 SQL!这促使我尝试它,然后又尝试了其他的 NoSQL 产品。CouchDB 的特性包括:
- 每个数据库是键值实体的集合
- 任何数据库都可以随时或持续地复制到任何其他数据库
- 每个值都是一个 JSON 文档
- 沙箱化的 map/reduce、list 和 show 函数用于转换数据
- CouchDB 为附件提供 HTTP 请求,并作为 HTTP 响应返回
最后两点引起了我的技术兴趣。CouchDB 可以托管所有三个 MVC 层吗?
它包含 Futon,一个用公共 API 编写的基于 Web 的管理控制台。数据 map/reduce 视图查询和 UI show 和 list 函数框架强制实现了 MVC 的分离。开发者编写它们,但它们是无状态、沙箱化的 JavaScript 函数,没有副作用。 MVC 控制器是任何程序员提供的脚本,它通过 REST API 调用 list 或 show 函数来响应事件。我设想了一个仅用于查询的移动 Web 应用程序——一个 Couch App——它通过复制来扩展。
我使用 Futon 学习创建数据库并添加一个用于保存 index.html、map/reduce、list 和 show 函数的设计文档。插入一些测试文档、一些函数,然后运行这些函数很容易。我发现必须编写一个 map 函数来创建一个数据视图来执行一个我可以在 Oracle、MySQL 或 MongoDB 中在几秒钟内完成的查询,这让我感到沮丧。我克服了这个障碍。 CouchDB 视图对我来说就像一个 SQL 视图。
Opto3 数据库
我希望使用最简单的工具,尽量不依赖 CouchDB 提供的功能以外的任何东西。那条路行不通。
我使用一个不透明的数据库作为我的工作区,并使用 Futon 作为我的编辑器来创建和编辑 view、show 和 list 函数。所有 JavaScript 函数都必须放入 JSON 记录中。转义引号和管理嵌入 JSON 的 JavaScript 花括号是无法忍受的。我想要一个本地工作区,我可以在每次代码更改后将其推送到 CouchDB——一个我可以在 GitHub 上分发的东西。此外,我需要 CommonJS 模块,以便我可以 require 像 handlebars 这样的包。如何将浅层目录树插入到设计文档中?
我爱上了 Kanso。 它在 kanso.json 文件中描述了依赖项。Java Maven 用户会看到与 POM 的相似之处。想要一个依赖项?在 kanso.json 中命名它。Kanso 正确地推送了由该文件描述的工件。
演示数据?我下载了一个 Maxmind 免费演示 CSV 文件,其中包含 200 个验光师的位置。应用程序将是一个验光师查找器。Kanso 命令使您可以为 CSV 文件添加 CouchDB 唯一 ID 列并转换为 JSON。我让 CSV 列名成为 CSV 列值的键。我通过 Futon 创建了一个 opto3 数据库。在命令窗口中,我发出一个 Kanso 命令将演示 JSON 上传到我的 opto3 数据库。
立即,_all_docs REST 查询就能够列出我的整个数据库。参见图 1。每个文档都有一个 CouchDB 生成的 ID,以及一个以顺序修订号为前缀的修订 ID。键可以是任何 JSON 对象,允许重复。如果您不提供显式键,CouchDB 会将 ID 值存储到键中。
在编码过程中,我反复使用 Kanso 将更新推送到我的设计文档。工件被放置在目标位置。本地目录是推送源。
图 1
如果在 Futon 中显示数据库,我可以单击一行以查看文档数据,如图 2 所示。
图 2:单击 ID 后的文档详细信息
以 ID 结尾的 REST 路径将在浏览器或 curl 命令行中返回该 ID 的文档 JSON。一个验光师的原始文档是
curl -X GET https://:5984/opto3/3770717789a226c91f8ce4808e31cddd {“_id”:”3770717789a226c91f8ce4808e31cddd”,”_rev”:”1-c9818488639a0326aefc0624aba5bde2″,”npi”:”1003868795″,”full_name”:”David L Kjelland”,”first_name”:”David”,”middle_name”:”L”,”last_name”:”Kjelland”,”title”:”OD”,”mailing_address1″:”Po Box 211″,”mailing_city”:”Mineral Point”,”mailing_state”:”WI”,”mailing_zip”:”53565-0211″,”mailing_phone”:”608-987-3301″,”mailing_fax”:”608-987-3045″,”street_address1″:”318 High St”,”street_city”:”Mineral Point”,”street_state”:”WI”,”street_zip”:”53565-1219″,”street_county”:”Iowa”,”street_msa”:”0″,”street_phone”:”608-987-3301″,”street_fax”:”608-987-3045″,”gender”:”M”,”specialty_code”:”152W00000X”,”specialty”:”Optometrist”,”license_number”:”1588-035″,”license_state”:”WI”}
一个移动应用程序
我创建了一个单页 jQuery Mobile index.html,由四个渐进式钻取逻辑页面组成
- 启动页
- 显示各州验光师数量的州列表
- 给定州的验光师城市地址列表
- 一位验光师的详细信息
数据库有一个或多个设计文档,用_design/something. 的记录 ID 表示。我选择了_design/opto。我插入了 map 函数(模型)来为两个 list 函数(视图)提供数据。在 Futon 下拉菜单中,我可以选择一个视图,查看其代码,并切换任何可选的 reduce 函数。
图 3 显示了视图 map 代码以及 count_by_state 视图的结果。CouchDB 对每个文档(行)调用一次 map 函数。该函数使用键和值调用 CouchDB 的 emit 函数,将其输入到一个新的内部 B-Tree 集合中。只有在创建或更新视图时才会发生这种开销。参见列表中的“View Code”。请注意,CouchDB 按键对文档进行排序。一组以排序的州缩写为键的“1”有什么用? 稍等。
图 3: 不带 reduce 的按州计数 map
在图 4 中,我设置了“Reduce”选项。显示转换为一个已排序的州键列表,每个键都有一个值,该值是该州验光师的数量。reduce 函数为每个州键生成值的总和(即每个州实例中每个 1 值的总和)。 太棒了!我需要这些数据用于一个顶层列表页面,该页面将显示可点击的美国地图,每个地图上显示验光师数量。我会将视图引用传递给一个 list 函数,该函数将为缩减集合的每一行生成一个 HTML 行。例如,演示数据库中有两个阿肯色州的验光师记录。图 3 中有两个“AR”记录。缩减后,图 4 显示“AR”的数量为两个。好。
图 4: 带 reduce 的按州计数 map
给定美国州的钻取面板需要一个仅由 map 函数组成的数据视图。参见图 5。该函数将州-城市复合键发出到验光师地址的值。如果我们仅在一个消费 list 函数中为此州过滤该视图,则城市将按自然升序排序,每个城市都有一个验光师地址。
这对于填充单击州后显示的城市列表非常完美。 CouchDB 始终为每个文档设置记录 ID。详细信息面板页面更改只需要访问 ID 即可检索和显示该验光师记录的详细信息。
图 5: 按州、带有地址值的城市排序列表
在处理 UI 之前,我使用 RESTful 的方式测试了 view、show 和 list 函数,尝试了 curl 和浏览器。
CouchDB 视图函数
视图是数据,而不是渲染的 HTML。我将 map/reduce 函数导出到一个 CommonJS 模块。
[__strong id=internal-source-marker_0.9778130722697824">// Used for panel two
exports.count_by_state = {
map: function(doc) { emit([doc.street_state], 1); },
reduce: function (key, values, rereduce) { return sum(values); }
};
// Used by panel three
exports.sorted_states_cities = {
map: function(doc) { emit([doc.street_state, doc.street_city], doc.street_address1); }
};
CouchDB List 函数
两个 list 函数通过 CouchDB provides() 函数和 getRow() 迭代器返回 HTML 行项集合。Handlebars 生成动态 HTML。
[__strong id=internal-source-marker_0.9778130722697824">// Populates list in panel two
exports.list_states = function(doc, req) {
provides('html', function() {
var Handlebars = require('handlebars');
var template = Handlebars.templates['stateName.html'];
var html = '';
while (row = getRow()) {
var context = { key: row.key, value: row.value};
html += template(context);
}
return html;
}
)
};
// Populates list in panel three
exports.list_cities = function(doc, req) {
provides('html', function() {
var Handlebars = require('handlebars');
var template = Handlebars.templates['cityName.html'];
var html = '';
while (row = getRow()) {
var context = { id: row.id, state: row.key[0], city: row.key[1], address: row.value};
html += template(context);
}
return html;
}
)
};
CouchDB Show 函数
单个 show 函数使用 handlebars 模板,根据文档参数和由 onclick 设置的文档 ID 生成详细 HTML。
internal-source-marker_0.9778130722697824">exports.detail = function(doc, req) {
[__strong id=internal-source-marker_0.9778130722697824"> var Handlebars = require('handlebars'); [___strong_>
var context = {
_id: doc._id,
full_name: doc.full_name,
title: doc.title,
street_address1: doc.street_address1,
street_address2: doc.street_address2,
street_city: doc.street_city,
street_state: doc.street_state,
street_zip: doc.street_zip,
street_county: doc.street_county,
street_phone: doc.street_phone,
gender: doc.gender,
specialty: doc.specialty
};
var template = Handlebars.templates['detail.html'];
var html = template(context);
return html;
};
Index.html
我使用 jQuery Mobile 创建了一个四页的“单 HTML 页”移动演示。您可以从 http://mauget.cloudant.com/opto3 复制该应用程序到您自己的 CouchDB。
控制器 (Controller)
我通过 jquery-mobile-routerlite 的页面更改事件触发的处理程序,在 scripts/controller.js 中将视图与 list 或 show 函数关联起来。jQuery-mobile 按钮是一个带样式的 HTML 超链接。单击按钮会触发页面更改事件。按钮的 on-click 操作会存储一个应用程序范围的密钥,处理程序会使用该密钥来查找和渲染结果。
Handlebars 模板
Handlebars 模板支持 show 函数和两个 list 函数。回想一下,面板二和面板三的 onclick 操作设置了一个模板使用的变量。请参见以下两个模板中的斜体部分。
由 list_states 使用的面板二列表项按钮模板
<li><a href="#three" onclick="APP.key='{{key}}';"> {{key}} <span> {{value}} </span></a></li>
由 list_cities 使用的面板三列表项按钮模板
<li><a href="#four" onclick="APP.id='{{id}}';"> {{address}}, {{city}}, {{state}} </a></li>
由“detail”使用的面板四 show 模板
internal-source-marker_0.9778130722697824"><dl title="Detail"> <dt><strong>Name</strong></dt> <dd>{{full_name}}, {{title}}</dd> <dt><strong>Specialty</strong></dt> <dd>{{specialty}}</dd> <dt><strong>Gender</strong></dt> <dd>{{gender}}</dd> <dt><strong>Address</strong></dt> <dd>{{street_address1}}</dd> <dd>{{street_address2}}</dd> <dt><strong>City, State</strong></dt> <dd>{{street_city}}, {{street_state}}</dd> <dt><strong>Zip</strong></dt> <dd>{{street_zip}}</dd> <dt><strong>County</strong></dt> <dd>{{street_county}}</dd> <dt><strong>Phone</strong></dt> <dd>{{street_phone}}</dd> </dl>[___strong_>
来自 iPhone 的屏幕截图
我将工作区推送到 GitHub。
- 参见: https://github.com/mauget/opto3-couchapp。
- 在您的 PC 或移动浏览器中尝试 Cloudant 中的 Opto3。缩短的 URL: http://bit.ly/UsMO0z。
以下屏幕截图按钻取顺序从 iPhone 5 截取
图 6: 动画 GIF 启动屏幕
-
图 7: 显示验光师数量的美国各州列表
-
图 8: 钻取到密苏里州验光师
-
图 9: 一位验光师的详细信息
CouchApp 的 URL 很丑。生产环境可以将一个“漂亮的”URL 重定向到一个可以驻留在许多 CouchDB 实例中的 Couch App。
结论
我的技术兴趣告诉我,一个完全托管在 DBMS 中的移动应用程序,并且能很好地保持 MVC 分离,这很酷。需要学习新知识,但编码量不大。通过将 Opto3 部署到多个复制实例,它可以横向扩展。我不知道它作为一个应用程序服务器的实际性能如何。这以及细粒度安全是未来的研究课题。
CouchDB 除了视图之外,还具有结构化的更新和删除功能。我的应用程序是一个仅用于查询的应用程序,但如果数据发生变化,应用程序将呈现这些变化。我通过一个 curl 命令添加了一个假的阿拉斯加验光师。当我再次返回到州列表页面时,应用程序就识别出了新的州、城市和验光师。
我多疑的一面有些担忧。我有一个学习曲线,其他人也会有。我会建议我的客户使用这种一体化架构吗?现在还不能用于生产。作为他们移动应用程序的广告技术概念验证呢?当然可以!我还会添加 CRUD 并利用基于角色的安全。
– Lou Mauget, asktheteam@keyholesoftware.com
参考文献
- CouchDB, https://couchdb.apache.ac.cn/
- CouchDB 文档 API, http://wiki.apache.org/couchdb/HTTP_Document_API
- jquery-mobile-routerlite, https://github.com/1Marc/jquery-mobile-routerlite
- jQuery mobile , http://jquerymobile.com/
- handlerbars, https://handlebars.node.org.cn/
- CommonJS, http://www.commonjs.org/
- JSON, http://www.json.org/
- Kanso, http://kan.so/
- GeoLite 可下载免费数据库, http://dev.maxmind.com/geoip/geolite
- CouchAPP, http://couchapp.org/page/index
- jquery Mobile + CouchDB, http://custardbelly.com/blog/2010/12/08/jquery-mobile-couchdb-part-1-getting-started/
- NoSQL 是 SQL 的续集吗? http://keyholesoftware.wordpress.com/2012/10/01/is-nosql-the-sql-sequel/
- 用于 Web CRUD 的 NoSQL 数据库(CouchDB)– Shows/Views, http://ilyasterin.com/blog/2010/02/nosql-databases-for-web-crud-couchdb-showsviews.html
- 仅使用 CouchDB 和客户端 JavaScript 开发 Web 应用程序,而不使用中间件,这有多可行?, http://www.quora.com/CouchDB/How-feasible-it-is-to-really-develop-web-applications-just-using-CouchDB-and-client-side-JavaScript-with-no-middle-tier
标签:CouchDB, jQuery Mobile, Kanso, Opto3 Database
