Di Firebase, apakah ada cara untuk mendapatkan jumlah anak dari suatu simpul tanpa memuat semua data simpul?

132

Anda bisa mendapatkan jumlah anak melalui

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

Tapi saya percaya ini mengambil seluruh sub-pohon dari simpul itu dari server. Untuk daftar besar, itu sepertinya RAM dan latensi intensif. Apakah ada cara untuk mendapatkan hitungan (dan / atau daftar nama anak) tanpa mengambil semuanya?

Astaga
sumber
terima kasih banyak saya mencobanya dan itu berhasil untuk saya
Ahmed Mahmoud
kode Anda tidak dapat menangani kumpulan data besar. Saya mendapat kesalahan yang disebabkan oleh ruang heap Java. Saya masih menunggu beberapa fitur.
Panupong Kongarn

Jawaban:

98

Cuplikan kode yang Anda berikan memang memuat seluruh rangkaian data dan kemudian menghitungnya di sisi klien, yang bisa sangat lambat untuk sejumlah besar data.

Firebase saat ini tidak memiliki cara untuk menghitung anak-anak tanpa memuat data, tetapi kami berencana untuk menambahkannya.

Untuk saat ini, salah satu solusinya adalah dengan mempertahankan penghitung jumlah anak dan memperbaruinya setiap kali Anda menambahkan anak baru. Anda dapat menggunakan transaksi untuk menghitung item, seperti dalam upvode pelacakan kode ini:

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

Untuk info lebih lanjut, lihat https://www.firebase.com/docs/transactions.html

UPDATE: Firebase baru-baru ini merilis Cloud Functions. Dengan Cloud Functions, Anda tidak perlu membuat Server sendiri. Anda cukup menulis fungsi JavaScript dan mengunggahnya ke Firebase. Firebase akan bertanggung jawab untuk memicu fungsi setiap kali suatu peristiwa terjadi.

Misalnya, jika Anda ingin menghitung jumlah suara, Anda harus membuat struktur yang mirip dengan ini:

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

Dan kemudian menulis fungsi javascript untuk meningkatkan upvotes_countketika ada tulis baru ke upvotesnode.

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

Anda dapat membaca Dokumentasi untuk mengetahui cara Memulai dengan Fungsi Cloud .

Juga, contoh penghitungan posting lainnya ada di sini: https://github.com/firebase/functions-samples/blob/master/child-count/functions/index.js

Perbarui Januari 2018

Dokumen firebase telah berubah jadi alih-alih eventsekarang kita memiliki changedan context.

Contoh yang diberikan melempar kesalahan yang mengeluh yang event.datatidak terdefinisi. Pola ini tampaknya bekerja lebih baik:

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

`` `

Andrew Lee
sumber
74
Apakah Anda pernah menambahkan dukungan untuk ini?
Jim Cooper
20
Apakah penghitung sisi klien dalam transaksi aman? Tampaknya bisa dengan mudah diretas untuk meningkatkan jumlah secara buatan. Ini bisa berdampak buruk bagi sistem pemungutan suara.
Soviut
16
++ akan sangat menyenangkan untuk mendapatkan hitungan tanpa menimbulkan biaya transfer
Jared Forsyth
27
Apakah ini pernah ditambahkan?
Eliezer
25
Ada berita tentang peta jalan fitur ini? Terima kasih
Pandaiolo
37

Ini sedikit terlambat dalam permainan karena beberapa orang lain sudah menjawab dengan baik, tetapi saya akan membagikan bagaimana saya bisa menerapkannya.

Ini bergantung pada fakta bahwa Firebase REST API menawarkan shallow=trueparameter.

Asumsikan Anda memiliki postobjek dan masing-masing dapat memiliki sejumlah comments:

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

Anda jelas tidak ingin mengambil semua komentar, hanya jumlah komentar.

Dengan asumsi Anda memiliki kunci untuk sebuah posting, Anda dapat mengirim GETpermintaan ke https://yourapp.firebaseio.com/posts/[the post key]/comments?shallow=true.

Ini akan mengembalikan objek pasangan nilai kunci, di mana setiap kunci adalah kunci dari komentar dan nilainya adalah true:

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

Ukuran respons ini jauh lebih kecil daripada meminta data yang setara, dan sekarang Anda dapat menghitung jumlah kunci dalam respons untuk menemukan nilai Anda (mis. CommentCount = Object.keys(result).length).

Ini mungkin tidak sepenuhnya menyelesaikan masalah Anda, karena Anda masih menghitung jumlah kunci yang dikembalikan, dan Anda tidak dapat selalu berlangganan nilai saat itu berubah, tetapi hal itu sangat mengurangi ukuran data yang dikembalikan tanpa memerlukan perubahan apa pun pada Anda skema.

Alex Klibisz
sumber
Mungkin menjadikan ini sebagai jawaban yang diterima karena dangkal = benar adalah tambahan baru sejak jawaban sebelumnya. Belum sempat melihat sendiri, jadi akan menunggu beberapa hari untuk melihat apa yang dipikirkan orang ...
josh
1
Dangkal mungkin merupakan pilihan terbaik untuk saat ini, tetapi itu tidak dikembalikan dengan kompresi dan dapat menjadi sangat lambat dan pengalaman untuk set data besar
Mbrevda
Jika kunci komentar tidak memiliki nilai boolean tetapi memiliki anak, apakah masih mengembalikan pasangan kunci-nilai kunci?
alltej
1
Anda mungkin ingin berhati-hati menggunakan REST API: startupsventurecapital.com/...
Remi Sture
3
Hanya untuk menunjukkan bahwa Anda harus menambahkan .jsonke akhir URL, misalnya:https://yourapp.firebaseio.com/posts/comments.json?shallow=true
Osama Xäwãñz
22

Simpan hitungan saat Anda pergi - dan gunakan validasi untuk menegakkannya. Saya meretas ini bersama - untuk menjaga jumlah suara unik dan jumlah yang terus muncul !. Tapi kali ini saya sudah menguji saran saya! (terlepas dari kesalahan cut / paste!).

'Trik' di sini adalah menggunakan prioritas simpul sebagai penghitungan suara ...

Datanya adalah:

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

pengguna hanya dapat memberikan suara satu kali && hitung harus lebih tinggi dari jumlah saat ini && nilai data harus sama dengan prioritas.

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

hitung (pemilih terakhir benar-benar) - suara harus ada dan hitungnya sama dengan jumlah baru, && jumlah baru (prioritas) hanya dapat naik satu.

  }
}

Script uji untuk menambahkan 10 suara oleh pengguna yang berbeda (untuk contoh ini, dipalsukan id, harus pengguna auth.uid dalam produksi). Hitung mundur dengan (i--) 10 untuk melihat validasi gagal.

<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>

'Risiko' di sini adalah pemilihan diberikan, tetapi penghitungan tidak diperbarui (haking atau kegagalan skrip). Inilah sebabnya mengapa suara memiliki 'prioritas' yang unik - skrip harus benar-benar dimulai dengan memastikan bahwa tidak ada suara dengan prioritas lebih tinggi dari jumlah saat ini, jika ada harus menyelesaikan transaksi itu sebelum melakukan sendiri - buat klien Anda untuk membersihkan untukmu :)

Hitungan perlu diinisialisasi dengan prioritas sebelum Anda mulai - menempa tidak membiarkan Anda melakukan ini, sehingga skrip rintisan diperlukan (sebelum validasi aktif!).

pperrin
sumber
Ini luar biasa !!! Apa yang terjadi pada konflik? Yaitu, dua orang memberikan suara pada saat yang sama? Idealnya Anda ingin menyelesaikannya secara otomatis, alih-alih hanya membuang salah satu suara mereka ... mungkin memilih dalam suatu transaksi?
Astaga
Hai Josh, secara logis pemungutan suara asli hanya dapat gagal jika pemungutan suara sebelumnya telah dilakukan, tetapi totalnya belum diperbarui (belum). Para 2 saya sampai terakhir meliputi bahwa - Saya hanya akan melakukan pembaruan total untuk pemilih pemilih sebelumnya (setiap kali) - jika tidak diperlukan, lalu apa? dan kemudian suara ini diperbarui. Selama pemungutan suara bekerja dengan baik. Jika pembaruan 'total' Anda gagal, pemilih berikutnya akan memperbaikinya, jadi sekali lagi - lalu bagaimana?
pperrin
Saya benar-benar tergoda untuk mengatakan bahwa simpul 'hitung' harus menjadi simpul 'suara terakhir sebelumnya' - sehingga setiap pemilih / klien memperbarui / memperbaiki / memperbaiki simpul / nilai dan kemudian menambahkan suara sendiri - (membiarkan pemilih berikutnya memperbarui total untuk memasukkan suara 'ini'). - Jika kau mengerti maksudku ...
pperrin
4

tulis fungsi cloud untuk dan perbarui jumlah simpul.

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

Rujuk: https://firebase.google.com/docs/functions/database-events

root-- | | -user (simpul ini berisi daftar semua pengguna) |
| -count | -userscount: (simpul ini ditambahkan secara dinamis oleh fungsi cloud dengan jumlah pengguna)

indvin
sumber