Pure JS (4.1): 使用 MongoDB 进行数据存储和管理
程序员文章站
2022-04-26 14:17:37
...
Pure JS (4.1): 使用 MongoDB 进行数据存储和管理
MongoDB 的一大特性就是 “JavaScript Friendly”,“Scheme Free” 的主张天生就是与 JavaScript 的开发理念相契合的。
它允许我们通过 eval 进行数据的初始化,以及在查询中使用 JS 对象作为查询条件,并且查询结果也可以很容易地转为 JS 对象或数组。
最近三篇文章将重点介绍如何在服务器端用 JS 与 MongoDB 进行交互;
我将这些操作封装在 pure.db 中,可能封装得并不完善,但可以很容易地进行修改和扩展 。
第一篇文章通过测试案例来了解 pure.db 的用法;
第二篇文章说明在实际的 Web 应用中如何使用它;
第三篇文章探讨 pure.db 的实现细节。
运行测试案例
首先,我们需要到这里下载 MongoDB:http://www.mongodb.org/downloads
通过命令行运行 mongod 和 mongo(默认情况下需要先在根目录创建文件夹 \data\db )。
mongod 是一个接受命令的守护进程,而 mongo 是一个与 mongod 交互的命令行 shell 工具。
然后下载附件,这是一个 eclipse 工程(就是我们之前一直使用的 PureJS 工程),添加到eclipse中,运行 test/purejs/test/DBTest.java。(鼠标右键 -> Run As -> JUnit Test 或 Alt + Shift + X, T)
正常情况下,应该看到像这样的绿条:
引用
如果出现 Error 或 Failure,在确认不是环境问题(mongod 正常运行中,Java 7 和 Junit 4 的配置没有问题)之后,请通过留言或站内信等方式通知我。
我们可以看到 DBTest 类的结构是这样的:
package purejs.test; import org.junit.*; import purejs.core.*; public class DBTest { @Before public void setUp() throws Exception { JSEngine.excuteFile("scripts/config.js"); JSEngine.excuteFile("scripts/lib/pure.js"); JSEngine.excuteFile("scripts/test/db/before.js"); } @Test public void list() throws Exception { execute("list"); } @Test public void listWithLimit() throws Exception { execute("listWithLimit"); } @Test public void listWithEquals() throws Exception { execute("listWithEquals"); } // 其他类似的测试方法 ... private void execute(String name) throws Exception { JSEngine.excuteFile("scripts/test/db/" + name + ".js"); JSEngine.invoke("run"); } }
也就是说每个测试方法实际上就是运行一个 js 文件,我们打开文件夹 scripts/test/db ,所有用于测试的 js 文件都在这里,总共有 25 个测试文件以及一个初始化脚本 before.js ,接下来我们将选择其中的重点进行说明。
通过 eval 进行数据的初始化
数据初始化的脚本 before.js 调用 pure.db.eval(func) 进行数据的初始化,传入一个函数,函数中的代码将直接在 MongoDB 运行,可以在这里查看原生的 MongoDB js 的写法:
http://www.mongodb.org/display/DOCS/SQL+to+Mongo+Mapping+Chart
before.js 首先建立了一个测试框架:
function run() { setUp(); execute(); verify(); tearDown(); }
然后实现了 setUp 函数和 tearDown 函数,而 execute 函数和 verify 函数则分别在每个测试文件中实现。
【setUp】
在 setUp 函数中,首先调用 tearDown 函数进行数据的清理,然后通过 eval 进行数据初始化,初始化的步骤包括:
1. 获取 DB 中名为 'users' 的 collection
2. 为 collection 创建索引,索引字段为 name ,排序方式为升序
3. 在 users 中存放一组测试数据
具体实现如下:
function setUp() { tearDown(); pure.db.eval(function(){ var users = db.users; users.ensureIndex({ name: 1 }); users.insert({ name: 'user1', desc: 'desc1' }); users.insert({ name: 'user2', desc: 'desc2' }); users.insert({ name: 'user3', desc: 'desc3' }); users.insert({ name: 'xxxxx', desc: 'desc3', age: 20 }); }); }
虽然 eval 很酷,但根据官方文档说明,运行 eval 时将阻塞所有的其他操作。
因此,最好只在数据初始化、数据迁移等特殊应用场景使用它。
所有测试的前提就是 eval 可用,因此没有针对 eval 的测试。
另外,因为建立了索引,所以后续的测试中通常不会对记录进行排序;但最近被更新的数据将被放在最后,顺序也就被打乱了。因此在实际应用中,每次获取数据后还是要进行排序的。
向 users 中插入的前三个对象很有规律,而第四个对象是为了测试 dintict,exists 等函数的用法而特意增加的。
【tearDown】
tearDown 首先将 context 清空,通过 eval -> db.users.drop 删除 users。
实现如下:
function tearDown() { context = {}; pure.db.eval(function(){ db.users.drop(); }); }
context 对象用于在 excute 函数和 verify 函数之间传递数据,所以每次执行测试前后都需要重新进行初始化。
测试案例介绍
【list (find)】
list 是我根据自己的习惯为 MongoDB 原生 JS 中的 find 方法起的一个别名,仍然使用find也是可以的。
list.js 内容如下:
importPackage(org.junit); function execute() { var users = pure.db.get('users'); context.list = users.$list(); } function verify() { var list = context.list; Assert.assertEquals(4, list.length, 0); Assert.assertEquals('user1', list[0].name); Assert.assertEquals('desc1', list[0].desc); Assert.assertEquals('user3', list[2].name); Assert.assertEquals('desc3', list[2].desc); }
excute 函数中通过 pure.db.get 获取名为 'users' 的 collection,然后 list 方法获取 user 对象列表。
list 函数名前带上 “$” 表示将结果转换为 JavaScript 数组,否则将继续保留 DBCursor 形式,以便进行排序等后续操作。
verify 函数对结果的长度和特定对象进行检查。
【listWithLimit】
通过 list 方法获取 DBCursor 之后,我们还可以通过 limit 和 skip 方法限制返回对象的数量,这可以用于分页功能的实现:
importPackage(org.junit); function execute() { var users = pure.db.get('users'); context.list = users.list().limit(2).$skip(1); } function verify() { var list = context.list; Assert.assertEquals(2, list.length, 0); Assert.assertEquals('user2', list[0].name); Assert.assertEquals('desc2', list[0].desc); Assert.assertEquals('user3', list[1].name); Assert.assertEquals('desc3', list[1].desc); }
limit(1) 表示返回值长度限制为 1 , skip(1) 表示跳过第一个对象。因此最终取到的列表就只有 'user2' 和 'user3' 了。
【在 list 中增加比较条件】
list 中增加 相等、大于、小于 等比较条件进行过滤。
listWithEquals
context.list = users.$list({ name: 'user2' });
listWithGreaterThen
context.list = users.$list({ name: { $gt: 'user2' } });
listWithLesserThen
context.list = users.$list({ name: { $lt: 'user2' } });
listWithMultipleEquals
context.list = users.$list({ name: 'user2', desc: 'desc2' });
listWithMultipleCompare
context.list = users.$list({ name: { $gt: 'user1', $lt: 'user3' } });
我们还可以用 $or 来表示不同条件之间是 “或” 的关系,如 listWithOr.js :
context.list = users.$list({ $or: [{ name: 'user1' }, { desc: 'desc2' } ] });
【对结果进行排序和 distinct 及返回特定属性】
使用 sort 方法可以对结果进行排序,参数为一个对象,对象的属性名称表示被排序的字段(这里的 name 或 desc),属性值为 1 (升序) 或 -1 (降序)。
ditinct 方法可以去除重复,并且只返回特定字段的值。
也可以在 list 的第二个参数中指定需要返回的特定属性。
listWithOrder
context.list = users.list().$sort({ desc: 1 });
listWithOrderDesc
context.list = users.list().$sort({ desc: -1 });
listWithDistinct
context.list = users.distinct('desc');
listSomeField
context.list = users.$list({}, { desc : 1 });
【使用正则表达式及exists】
我们还可以在过滤条件中使用正则表达式,从而替代传统 SQL 数据库中的 like 查询。
也可以通过 $exists 检查包含某个特定属性的对象。
listWithLike
context.three = users.$list({ name: /user/ }); context.one = users.$list({ name: /user1/ });
listWithPrefix
context.three = users.$list({ name: /^u/ }); context.one = users.$list({ name: /^user1/ });
listWithPostfix
context.list = users.$list({ desc: /3$/ });
listWithExists
context.list = users.$list({ age: { $exists: true } });
【count】
count 函数可以对返回结果进行计数。
count.js
context.count = users.count();
listWithCount.js
context.count = users.list({ name: /user/ }).count();
【get (findOne)】
同样,get 是 findOne 的一个别名。
get 的用法与 list 相似,但只返回符合条件的第一个对象。
context.user = users.get({ name: 'user1' });
【insert】
insert 用于增加对象
users.insert({ name: 'user4', desc: 'desc4' });
【update】
insert 用于更新对象,可以更新一条或多条,第一个参数表示被更新的记录符合的条件,第二个参数表示更新选项,第三个参数表示不存在的情况下是否插入记录,第四个对象表示是否更新多条。
update.js
var query = { name: 'user1' }; users.update(query, { $set: { desc: 'new desc' } });
updateMultiple.js
var query = { name: /user/ }; users.update(query, { $set: { desc: 'new desc' } }, false, true);
关于 update 的测试并不全面,可以在这里找到更全面的说明:
http://www.mongodb.org/display/DOCS/Updating
【save】
save 既可以用于插入,也可以用于更新。
saveAsAdd.js
users.save({ name: 'user4', desc: 'desc4' });
saveAsUpdate.js
var user = users.get({ name: 'user1' }); user.desc = 'new desc'; users.save(user);
【remove】
remove 用于删除对象
remove.js
users.remove({ name: 'user1' });
上一篇: PureJS (6.5):配置文件
下一篇: Pure (6.7):部署