139

您可以通过以下方式获取孩子人数

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

但我相信这会从服务器获取该节点的整个子树。对于庞大的列表,这似乎是 RAM 和延迟密集型的。有没有办法在不获取整个内容的情况下获取计数(和/或子名列表)?

4

4 回答 4

104

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

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 最近发布了 Cloud Functions。使用 Cloud Functions,您无需创建自己的服务器。您可以简单地编写 JavaScript 函数并将其上传到 Firebase。Firebase 将负责在事件发生时触发函数。

例如,如果你想计算赞成票,你应该创建一个类似于这个的结构:

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

然后编写一个javascript函数来增加upvotes_count当有新的写入upvotes节点时。

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());
});

您可以阅读文档以了解如何开始使用 Cloud Functions

此外,另一个计数帖子的示例在这里: https ://github.com/firebase/functions-samples/blob/master/child-count/functions/index.js

2018 年 1 月更新

firebase文档已更改,因此event我们现在拥有changecontext.

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);
});

```

于 2013-03-01T02:13:02.170 回答
39

这在游戏中有点晚了,因为其他几个人已经很好地回答了,但我会分享我如何实现它。

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

假设您有一个post对象,并且每个对象可以有多个comments

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

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

假设您有帖子的密钥,您可以GEThttps://yourapp.firebaseio.com/posts/[the post key]/comments?shallow=true.

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

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

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

这可能无法完全解决您的问题,因为您仍在计算返回的键数,并且您不一定要在更改时订阅该值,但它确实大大减少了返回数据的大小,而无需对您的架构。

于 2016-01-22T22:46:49.043 回答
23

随时保存计数 - 并使用验证来强制执行。我一起破解了这个 - 为了保持不断出现的独特选票和计数!但是这次我已经测试了我的建议!(尽管有剪切/粘贴错误!)。

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

数据是:

vote/$issueBeingVotedOn/user/$uniqueIdOfVoter = thisVotesCount, priority=thisVotesCount vote/$issueBeingVotedOn/count = 'user/'+$idOfLastVoter, priority=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 "
    }

count (最后一个投票者) - 投票必须存在并且它的 count 等于 newcount, && newcount (priority) 只能增加一个。

  }
}

测试脚本以添加不同用户的 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 或脚本失败)。这就是为什么投票有一个独特的“优先级”——脚本应该首先确保没有优先级高于当前计数的投票,如果有的话,它应该在自己做之前完成该事务——让你的客户清理为你准备:)

计数需要在开始之前以优先级初始化 - forge 不允许您这样做,因此需要一个存根脚本(在验证激活之前!)。

于 2014-04-17T12:00:55.920 回答
4

将云函数写入并更新节点数。

// 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(此节点包含所有用户列表)|
|-count |-userscount :(此节点由云功能与用户计数动态添加)

于 2017-09-05T08:33:27.993 回答