在 Firebase 中,有没有一种方法可以在不加载所有节点数据的情况下获得一个节点的子节点数?

你可以通过

firebase_node.once('value', function(snapshot) { alert('Count: ' + snapshot.numChildren()); });

但是我相信这将从服务器获取该节点的整个子树。对于巨大的列表,这似乎是内存和延迟密集型的。有没有一种方法可以获取计数(和/或子名列表)而不需要获取全部内容?

114331 次浏览

您给出的代码片段确实加载了整个数据集,然后在客户端对其进行计数,这对于大量数据来说非常慢。

Firebase 目前还没有一种不加载数据就可以计数子节点的方法,但是我们计划添加它。

目前,一种解决方案是维护一个子级数计数器,并在每次添加新子级时更新它。您可以使用事务来计数项目,如下面的代码跟踪 upvodes:

var upvotesRef = new Firebase('https://docs-examples.firebaseio.com/android/saving-data/fireblog/posts/-JRHTHaIs-jNPLXOQivY/upvotes');
upvotesRef.transaction(function (current_value) {
return (current_value || 0) + 1;
});

有关更多信息,请参见 https://www.firebase.com/docs/transactions.html

更新: Firebase 最近发布了云函数。使用云函数,您不需要创建自己的服务器。您可以简单地编写 JavaScript 函数并将其上传到 Firebase。每当事件发生时,Firebase 将负责触发功能。

例如,如果你想计算升级选票,你应该创建一个类似于下面这个结构:

{
"posts" : {
"-JRHTHaIs-jNPLXOQivY" : {
"upvotes_count":5,
"upvotes" : {
"userX" : true,
"userY" : true,
"userZ" : true,
...
}
}
}
}

然后编写一个 javascript 函数,在对 upvotes节点有新的写操作时增加 upvotes_count

const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp(functions.config().firebase);


exports.countlikes = functions.database.ref('/posts/$postid/upvotes').onWrite(event => {
return event.data.ref.parent.child('upvotes_count').set(event.data.numChildren());
});

你可以通过阅读 文件来了解如何阅读 开始使用云函数

此外,这里还有一个计算职位的例子: Https://github.com/firebase/functions-samples/blob/master/child-count/functions/index.js

2018年1月更新

火库文件已经改变,所以我们现在有 changecontext而不是 event

给定的示例抛出一个错误,抱怨 event.data未定义。这个模式似乎更好:

exports.countPrescriptions = functions.database.ref(`/prescriptions`).onWrite((change, context) => {
const data = change.after.val();
const count = Object.keys(data).length;
return change.after.ref.child('_count').set(count);
});

```

随时保存计数,并使用验证来强制执行。我把它们拼凑在一起——为了保持一个独特的投票计数和不断出现的计数!.但这次我已经测试了我的建议!(尽管存在剪切/粘贴错误!).

这里的“技巧”是使用节点 优先权作为投票计数..。

数据如下:

VotesCount (投票计数) ,优先级 = this VotesCount (投票计数) 统计数字 = ‘ user/’+ $idOfLastVoter 优先级 = CountofLastVote

,"vote": {
".read" : true
,".write" : true
,"$issue" : {
"user" : {
"$user" : {
".validate" : "!data.exists() &&
newData.val()==data.parent().parent().child('count').getPriority()+1 &&
newData.val()==newData.GetPriority()"

用户只能投票一次 & & 计数必须高于当前计数 & & 数据值必须与优先级相同。

      }
}
,"count" : {
".validate" : "data.parent().child(newData.val()).val()==newData.getPriority() &&
newData.getPriority()==data.getPriority()+1 "
}

计数(实际上是最后一个投票者)-投票必须存在,其计数等于新计数,& & newcount (优先级)只能上升一个。

  }
}

测试脚本通过不同的用户添加10个投票(在本例中,id 是伪造的,如果用户 auth.uid 在生产环境中)。倒计时(i ——)10,验证失败。

<script src='https://cdn.firebase.com/v0/firebase.js'></script>
<script>
window.fb = new Firebase('https:...vote/iss1/');
window.fb.child('count').once('value', function (dss) {
votes = dss.getPriority();
for (var i=1;i<10;i++) vote(dss,i+votes);
} );


function vote(dss,count)
{
var user='user/zz' + count; // replace with auth.id or whatever
window.fb.child(user).setWithPriority(count,count);
window.fb.child('count').setWithPriority(user,count);
}
</script>

这里的“风险”是投票,但计数没有更新(haking 或脚本失败)。这就是为什么投票有一个独特的“优先级”——脚本应该确保没有优先级高于当前计数的投票,如果有,应该在完成自己的交易之前完成交易——让你的客户为你清理:)

计数需要在开始之前用优先级进行初始化——锻造不允许这样做,所以需要一个存根脚本(在验证激活之前!).

这在游戏中有点晚了,因为其他一些人已经很好地回答了这个问题,但是我将分享我可能如何实现它。

这取决于这样一个事实,即 Firebase REST API提供了一个 shallow=true参数。

假设你有一个 post对象,每个对象可以有一个 comments的数字:

{
"posts": {
"$postKey": {
"comments": {
...
}
}
}
}

显然,您不想获取所有的评论,只是获取评论的数量。

假设您有一个帖子的密钥,您可以将 GET请求发送到 https://yourapp.firebaseio.com/posts/[the post key]/comments?shallow=true.

这将返回一个键-值对的对象,其中每个键是一个注释的键,其值为 true:

{
"comment1key": true,
"comment2key": true,
...,
"comment9999key": true
}

这个响应的大小比请求等效数据要小得多,现在您可以计算响应中键的数量来找到您的值(例如 commentCount = Object.keys(result).length)。

这可能不能完全解决您的问题,因为您仍然在计算返回的键的数量,并且您不能在值发生变化时订阅它,但是它确实大大减少了返回数据的大小,而不需要对模式进行任何更改。

写一个云函数并更新节点计数。

// below function to get the given node count.
const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp(functions.config().firebase);


exports.userscount = functions.database.ref('/users/')
.onWrite(event => {


console.log('users number : ', event.data.numChildren());




return event.data.ref.parent.child('count/users').set(event.data.numChildren());
});

参考: https://firebase.google.com/docs/functions/database-events

根 |-users (此节点包含所有用户列表) |
数数 |-用户数量: (这个节点通过云函数动态添加用户数)