如何在野狗中实现 Count 和 自增字段

野狗并没有提供一个类似于count()的API。当我们需要知道某个数据节点下有多少个子节点的时候,有两种方式来实现:


  • 当数据量不大的时候,可以在客户端进行计数

  • 当数据量较大的时候,在野狗云端记录一个计数器


由于JavaScript语言比较容易运行和调试,我们以JavaScript SDK为例来解决这个问题,Android和iOS端可以实现相同的效果。


在客户端计数


假设一个这样的场景,我们有一个/books数据节点,通过push()方法在其下存储了许多书籍的信息,我们将每本书的信息称之为一条“记录”,数据结构如下:



我们先假设记录条数量很少,采用客户端计数的方案。需要注意的是,使用child_added事件是无法实现这个计数需求的:


var ref = new Wilddog("https://<appId>.wilddogio.com/books");

var counter = 0;

ref.on('child_added', function(snapshot) {

console.log(snapshot.val().name);

counter ++;

});


在上面的代码中,由于child_added事件会为每个子节点触发一次,因此我们试图用一个counter字段作为计数器来计算总记录的条数。这个方案看起来还可以,但是问题出在异步上,每此回调函数被触发执行,我们都无法知道这是否是最后一条记录。


正确的做法是使用value事件:


var ref = new Wilddog("https://<appId>.wilddogio.com/books");


ref.on('value', function(snapshot) {

    var count = 0;

    snapshot.forEach(function() {

        count++;

    });

    console.log(count);  // 此时的count是可靠的

});


上面的代码可以准确的统计到记录的条数,其实还有一个更简单的API,不需要使用forEach循环计数,直接调用snapshot的numChildren()方法就可以了。这是小数据量的时候最好的方法了,非常简单。


在云端计数


遗憾的是实际的应用开发中,大部分时候我们都会考虑数据量比较大,全部加载到客户端进行计数是效率非常低,甚至完全不可行的。我们可以在云端记录一个计数器的方式来实现。我们在books节点下增加一个count节点,代表记录的条数,数据结构如下:



接下来,在每次push()一条新的记录的时候,我们都对counter字段进行+1:


var ref = new Wilddog("https://<appId>.wilddogio.com/books");

ref.push({"name":'new book'});

ref.child('counter').once('value', function(snapshot) {

var count = snapshot.val();

counter ++;

ref.child('counter').set(count);

});


这是最简单最直观的实现。但是它只适用于没有并发访问,或者对count数据的精确度要求不高的场景下。因为这里的push一条数据和修改counter的值是两个操作,而且修改counter的值是一种“读出来,加1,写回去”的模式,在多个终端用户同时操作的情况下会出现并发问题导致数据错误。


要解决并发修改数据的问题,需要使用野狗的transaction API。这里的transaction和关系型数据库中事务不是一回事。关系型数据库中的事务是为了保证ACID四个特性,而野狗的transaction API只是用来保证在多个客户端同时修改一个数据时不会产生冲突,可以实现对数据更改的原子性操作。使用transaction后的代码实现如下:


ref.child('counter').transaction(function(currentValue) {

    var newValue = (currentValue||0) + 1;

    return newValue;

}, function(err, committed, snapshot) {

    if (err) {

    console.log("err: " + err);

    } else if (committed) {

    ref.push({"name":'a new book'});

    }

});


transaction() 方法接受2个回调函数作为参数。第一个函数将会获取到当前的值,return的是即将被写入的值。这里我们对counter进行+1操作。第二个回调函数中的committed为true时,代表这个transaction已经成功,此时我们push一条新的记录。


transaction()的原理是,在向云端写入newValue的时候,会判断云端的数据仍然是旧的currentValue。如果云端数据已被其他客户端修改掉了,那么这次请求就会失败并重试。重试的过程是,重新从云端获取数据的值,记为currentValue,重新计算新值(+1),重新向云端写入。这个过程一直重复直至写入成功,或者超过最大重试次数。这些工作都是野狗SDK内部完成了的,我们无需关心其原理,只要根据文档,知道API的用法就可以了。


实现自增字段


有了计数器的实现,稍加修改就可以实现自增字段了。我们可以将counter字段作为一个id的发号器,用它来生成自增的id。假如我们不再使用push()方法存储每本书的数据(push会为每条记录生成唯一的key,就是数据中以-K开头的字符串),而是自行维护一个自增的“主键”。我们将写入数据的程序修改如下:


ref.child('counter').transaction(function(currentValue) {

    return (currentValue||0) + 1;

}, function(err, committed, snapshot) {

    if (err) {

    console.log("err: " + err);

    } else if (committed) {

    var id = snapshot.val();

    ref.child(id).set({"name":'a new book'});

    }

});


执行的过程是,第一个回调函数将counter的值+1,成功之后进入第二个回调函数中,commited为true,snapshot为+1之后的counter的值,也就是获得了最新的id。不再使用push,而是child(id)之后去set()数据就可以了。结果如下:


这样我们就实现了自增字段。



转载自:https://sanwen8.cn/p/1a7EJbE.html

【6层】一字型框架办公楼(含建筑结构图、计算书) 1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 6、可私信博主看论文后选择购买源代码。 1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 6、可私信博主看论文后选择购买源代码。 1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 6、可私信博主看论文后选择购买源代码。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值