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


132

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

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

但是我相信这可以从服务器获取该节点的整个子树。对于庞大的列表,这似乎需要占用大量RAM和延迟。有没有一种方法可以获取计数(和/或子名称列表)而无需获取整个信息?


非常感谢我尝试过,它对我
艾哈迈德·马哈茂德

您的代码无法处理大型数据集。我由Java堆空间引起错误。我仍在等待一些功能。
Panupong Kongarn

Answers:


98

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

Firebase当前无法在不加载数据的情况下对儿童进行计数,但是我们确实计划添加它。

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

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_countupvotes节点的新写操作。

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月更新

火力文档已经改变所以不是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);
});

```


74
您是否为此添加过支持?
Jim Cooper

20
事务中的客户端计数器是否安全?似乎很容易有人为增加计数而被黑。这可能对投票系统不利。
苏联2014年

16
++能够在不产生转帐费用的情况下获得计数非常好
Jared Forsyth

27
是否添加过?
Eliezer

25
有关此功能路线图的任何新闻?谢谢
Pandaiolo 2015年

37

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

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

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


可能使它成为可接受的答案,因为shallow = true是自先前答案以来的新增内容。我自己没有时间研究它,因此将等待几天才能看到人们的想法……
josh 2016年

1
浅可能是目前最好的选择,但它不会通过压缩返回,并且会变得非常缓慢,并且无法体验大型数据集
Mbrevda

如果注释键没有布尔值而是有子级,它是否仍返回键的键值对?
alltej

1
您可能需要使用REST API要小心:startupsventurecapital.com/...
雷米Sture专门

3
只是指出您必须.json在URL末尾附加例如:https://yourapp.firebaseio.com/posts/comments.json?shallow=true
OsamaXäwãñz17年

22

随手保存计数-并使用验证强制执行。我一起砍了这个-为了保留唯一的票数和不断增加的票数!但是这次我测试了我的建议!(尽管剪切/粘贴错误!)。

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

数据为:

投票/ $ issueBeingVotedOn /用户/ $ uniqueIdOfVoter = thisVotesCount,优先级= thisVotesCount投票/ $ issueBeingVotedOn / count ='用户/'+ $ 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,&& 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>

这里的“风险”是投了票,但未更新计数(摇晃或脚本失败)。这就是为什么投票具有唯一的“优先级”的原因-脚本应该首先确保没有优先级高于当前计数的投票开始,如果有的话应该在完成交易之前完成该交易-让客户清理为您服务:)

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


这太棒了!!!但是,在冲突中会发生什么?即,两个人同时投票?理想情况下,您希望自动解决该问题,而不仅仅是丢弃他们的一票...也许是在交易中投票?
2014年

乔什,您好,从逻辑上讲,只有进行了前一次投票,真正的投票才能失败,但总票数尚未更新(尚未)。我的倒数第二段涵盖了这一点-我只想(每次)对以前的选民进行全部更新-如果不需要的话,那又如何呢?然后此票更新。只要投票工作正常。如果您的“全部”更新失败,则下一个选民将对其进行修复,因此,再次如此-那么呢?
pperrin

我真的很想说“计数”节点应该是“最后一次投票”节点-因此每个投票者/客户端都会更新/修复/修复该节点/值,然后添加自己的投票-(让下一个投票者更新总计包括“此”投票)。-如果你能
流连忘返

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 :(此节点由云功能根据用户数量动态添加)

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.