在Firebase中,有没有办法在不加载所有节点数据的情况下获取节点的子节点数?

时间:2013-03-01 01:19:07

标签: database firebase count

您可以通过

获取子女数
firebase_node.once('value', function(snapshot) { alert('Count: ' + snapshot.numChildren()); });

但我相信这会从服务器中获取该节点的整个子树。对于巨大的列表,这似乎是RAM和延迟密集型。有没有办法获取计数(和/或子名称列表)而不取出整个事物?

5 个答案:

答案 0 :(得分:88)

您提供的代码段确实会加载整个数据集,然后将其计入客户端,这对于大量数据来说可能非常慢。

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。使用云功能,您无需创建自己的服务器。您只需编写JavaScript函数并将其上传到Firebase即可。 Firebase将负责在事件发生时触发功能。

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

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

您可以阅读Documentation了解如何Get Started with Cloud Functions

此外,另一个计算帖子的例子如下: https://github.com/firebase/functions-samples/blob/master/child-count/functions/index.js

2018年1月更新

firebase docs已更改,而不是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);
});

```

答案 1 :(得分:24)

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

这取决于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)。

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

答案 2 :(得分:21)

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

此处的'技巧'是使用节点 priority 作为投票计数......

数据是:

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()" 

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

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

计数(真正的最后一个选民) - 投票必须存在并且其计数等于newcount,&amp;&amp; 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>

此处的'风险'是投票,但计数未更新(黑客或脚本失败)。这就是为什么投票具有独特的“优先级” - 脚本应该首先确保没有优先级高于当前计数的投票,如果它应该在完成该事务之前完成该事务 - 让客户清理为你服务:))

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

答案 3 :(得分: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

root-- |       | -users(此节点包含所有用户列表)       |
      | -count           | -userscount: (此节点由云功能和用户计数动态添加)

答案 4 :(得分:-6)

我很欣赏这是从开放问题开始的2年,但是作为它的第一个谷歌结果,我认为我会更新,因为我正在寻找一种方法来获取对象的计数。这可以通过

完成

提供了DataSnapshot.numChildren()方法,可在此处找到更多详细信息 - Firebase Website