Firebaseで、すべてのノードデータを読み込まずにノードの子の数を取得する方法はありますか?


132

あなたは経由で子供の数を得ることができます

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

しかし、これはそのノードのサブツリー全体をサーバーからフェッチすると思います。巨大なリストの場合、RAMとレイテンシが集中するようです。全部をフェッチせずにカウント(および/または子の名前のリスト)を取得する方法はありますか?


おかげで私はそれを試してみました、そしてそれは私のために働きました
Ahmed Mahmoud

コードは大きなデータセットを処理できません。Javaヒープ領域によりエラーが発生しました。私はまだいくつかの機能を待っています。
Panupong Kongarn 2017年

回答:


98

あなたが与えたコードスニペットは実際にデータのセット全体をロードし、それをクライアント側でカウントします。これは大量のデータに対して非常に遅くなる可能性があります。

Firebaseには現在、データを読み込まずに子をカウントする方法はありませんが、追加する予定です。

今のところ、1つの解決策は、子の数のカウンターを維持し、新しい子を追加するたびにそれを更新することです。次のコードのように、トランザクションを使用してアイテムをカウントできます。

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,
      ...
    }
    }
  }
}

そしてupvotes_countupvotesノードへの新しい書き込みがあるときに増加するJavaScript関数を記述します。

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

「」


74
これに対するサポートを追加したことがありますか?
ジムクーパー

20
トランザクションのクライアント側のカウンターは安全ですか?人為的に数を増やすために簡単にハッキングされる可能性があります。これは投票システムにとって悪いことかもしれません。
ソビエト2014

16
++転送コストを発生させずにカウントを取得することは本当に良いことです
Jared Forsyth '

27
これは追加されたことがありますか?
Eliezer、2015年

25
この機能ロードマップに関するニュースはありますか?ありがとう
Pandaiolo 2015

37

他のいくつかの人がすでにうまく答えているので、これはゲームの少し遅いですが、私はそれを実装する方法を共有します。

これは、Firebase REST APIshallow=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

1
Shallowはおそらく今のところ最良のオプションですが、圧縮では返されず、非常に遅くなり、大規模なデータセットの場合はエクスペリエンスが低下する可能性があります
Mbrevda

コメントキーにブール値がなく、子がある場合でも、キーのキーと値のペアを返しますか?
alltej 2017

1
REST APIの使用には注意が必要な場合があります
。startupsventurecapital.com

3
ただ、あなたが追加する必要があることを指摘して.json、たとえば、URLの最後に:https://yourapp.firebaseio.com/posts/comments.json?shallow=true
オサマXäwãñz

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

ユーザーは一度だけ投票できます。&&カウントは現在のカウントよりも1つ大きくなければなりません。&&データ値は優先度と同じでなければなりません。

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

count(最後の有権者は本当に)-投票が存在しなければならず、その数はnewcountに等しく、&& newcount(優先度)は1つだけ上がることができます。

  }
}

異なるユーザーによる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ではこれを行うことができないため、(検証がアクティブになる前に)スタブスクリプトが必要です。


これはすごい!しかし、紛争ではどうなりますか?つまり、2人が同時に投票しますか?理想的には、それらの投票の1つを単に破棄するのではなく、それを自動的に解決する必要があります...おそらくトランザクションで投票を行いますか?
josh 14

こんにちはジョシュ、論理的には本物の投票は前の投票が行われた場合にのみ失敗する可能性がありますが、合計は(まだ)更新されていません。私の最後から2番目のパラグラフはそれをカバーしています-とにかく(毎回)以前の有権者の投票の合計を更新するだけです-必要がなければ、どうですか?そして、これは更新を投票します。投票がうまくいく限り。「合計」の更新が失敗した場合は、次の有権者が修正するので、もう一度-それで何ですか?
pperrin 14

私は本当に「カウント」ノードは「最後の前回の投票」ノードであると言いたくてたまらない-だから、各有権者/クライアントはそのノード/値を更新/修正/修復してから、独自の投票を追加する-(次の有権者に更新を任せる「これ」の投票を含む合計)。-あなたが私のドリフトを手に入れたら...
pperrin 14

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:(このノードは、ユーザー数とともにクラウド機能によって動的に追加されます)

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.