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

jos*_*osh 124 database count firebase

你可以通过孩子来计算

firebase_node.once('value', function(snapshot) { alert('Count: ' + snapshot.numChildren()); });
Run Code Online (Sandbox Code Playgroud)

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

And*_*Lee 93

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

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;
});
Run Code Online (Sandbox Code Playgroud)

有关详细信息,请参阅https://www.firebase.com/docs/transactions.html

更新: Firebase最近发布了云功能.使用云功能,您无需创建自己的服务器.您只需编写JavaScript函数并将其上传到Firebase即可.Firebase将负责在事件发生时触发功能.

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

{
  "posts" : {
    "-JRHTHaIs-jNPLXOQivY" : {
      "upvotes_count":5,
      "upvotes" : {
      "userX" : true,
      "userY" : true,
      "userZ" : true,
      ...
    }
    }
  }
}
Run Code Online (Sandbox Code Playgroud)

然后编写一个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());
});
Run Code Online (Sandbox Code Playgroud)

您可以阅读文档以了解如何开始使用云功能.

另外,另一个计算帖子的例子是: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);
});
Run Code Online (Sandbox Code Playgroud)

```

  • 你有没有为此添加支持? (69认同)
  • 这有没有添加? (25认同)
  • 有关此功能路线图的任何新闻?谢谢 (24认同)
  • 虽然交易中的客户端计数器安全吗?它似乎很容易被黑客入侵,人为地增加数量.这可能对投票系统不利. (19认同)
  • ++在没有产生转移成本的情况下获得计数真的很不错 (15认同)
  • Bump +1用于添加此功能的更新 (7认同)
  • 任何新闻?这个功能非常有用. (4认同)
  • 你计划在什么时候添加它? (4认同)
  • 他们的REST api有一个名为shallow = true的参数,它只提取直接的子节点而不是子节点,如果你通过说curl使用REST api它会很好.但是js api没有类似的东西 (3认同)
  • 这是一个必须具备的功能,这里的答案是无用的,因为它确实下载了整个节点。 (3认同)
  • 我们还没有添加它。 (2认同)
  • @Dustin显然在数据库中正在更新这个值,它会影响到之后的所有用户,因为计数需要是全局可写的.想象一下,实施一个投票系统或用这个代码限制用户配额的东西 - 结果可能是灾难性的. (2认同)
  • 我们现在有云功能,官方指南提供了子节点计数示例。我建议检查它,因为它涵盖了一些边缘情况。https://github.com/firebase/functions-samples/tree/master/child-count (2认同)
  • 我还注意到,如果您/人员一次添加大量数据,则数据写入(或类似操作)不一定按正确的顺序进行,因此您可能会突然发现较低的值。我在客户端上显示此值,并通过仅在该值高于上一个值时才向用户显示新值来解决该问题。 (2认同)
  • 它们永远不会添加... firebase赚钱,迫使您下载大软件包(即使在不必要时)...这就是为什么firebase使用纯HTTP(而不是gzip或其他内容),对他们的商业模式无所谓的原因让您省钱的简单方法 (2认同)
  • 已经7年了,FireBase添加了实时数据库计数器吗? (2认同)

Ale*_*isz 34

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

这取决于Firebase REST API提供shallow=true参数的事实.

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

{
 "posts": {
  "$postKey": {
   "comments": {
     ...  
   }
  }
 }
}
Run Code Online (Sandbox Code Playgroud)

你显然不想获取所有评论,只需要获取评论数量.

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

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

{
 "comment1key": true,
 "comment2key": true,
 ...,
 "comment9999key": true
}
Run Code Online (Sandbox Code Playgroud)

此响应的大小远小于请求等效数据,现在您可以计算响应中的键数以找到您的值(例如commentCount = Object.keys(result).length).

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

  • 只是要指出,您必须在URL末尾附加.json,例如:https://yourapp.firebaseio.com/posts/comments.json?shallow=true (3认同)

ppe*_*rin 22

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

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

数据是:

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()" 
Run Code Online (Sandbox Code Playgroud)

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

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

count(真正的最后一个选民) - 投票必须存在并且其计数等于newcount,&& newcount(优先级)只能增加一个.

  }
}
Run Code Online (Sandbox Code Playgroud)

测试脚本以添加不同用户的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>
Run Code Online (Sandbox Code Playgroud)

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

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