Firebase'de, tüm düğüm verilerini yüklemeden bir düğümün çocuk sayısını almanın bir yolu var mı?


132

Çocuk sayısını şu yolla alabilirsiniz:

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

Ancak bunun sunucudan o düğümün tüm alt ağacını getireceğine inanıyorum. Büyük listeler için bu, RAM ve yoğun gecikme gerektiriyor. Her şeyi getirmeden sayımı (ve / veya çocuk isimlerinin bir listesini) almanın bir yolu var mı?


çok teşekkürler deniyorum ve benim için işe yaradı
Ahmed Mahmoud

kodunuz büyük veri kümesini işleyemez. Java yığın alanı nedeniyle hata aldım. Hala bazı özellikler bekliyorum.
Panupong Kongarn

Yanıtlar:


98

Verdiğiniz kod parçacığı gerçekten de tüm veri kümesini yükler ve daha sonra istemci tarafında sayar, bu da büyük miktarda veri için çok yavaş olabilir.

Firebase'in şu anda veri yüklemeden çocukları saymanın bir yolu yoktur, ancak eklemeyi planlıyoruz.

Şimdilik bir çözüm, çocuk sayısının bir sayacını tutmak ve her yeni çocuk eklediğinizde bunu güncellemektir. Bu kod izleme yükseltmelerinde olduğu gibi, öğeleri saymak için bir işlem kullanabilirsiniz:

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

Daha fazla bilgi için bkz. Https://www.firebase.com/docs/transactions.html

GÜNCELLEME: Firebase kısa süre önce Cloud Functions'ı piyasaya sürdü. Cloud Functions ile kendi Sunucunuzu oluşturmanız gerekmez. JavaScript işlevlerini yazabilir ve Firebase'e yükleyebilirsiniz. Firebase, bir olay meydana geldiğinde işlevleri tetiklemekten sorumlu olacaktır.

Örneğin, olumlu oyları saymak istiyorsanız, buna benzer bir yapı oluşturmalısınız:

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

Ve sonra düğüme upvotes_countyeni bir yazma olduğunda artırmak için bir javascript işlevi yazın 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());
});

Cloud Functions'ı Kullanmaya Nasıl Başlayacağınızı öğrenmek için Belgeleri okuyabilirsiniz .

Ayrıca, gönderileri saymanın başka bir örneği de burada: https://github.com/firebase/functions-samples/blob/master/child-count/functions/index.js

Ocak 2018 Güncellemesi

Firebase dokümanlar böylece yerine değişti eventşimdi var changeve context.

Verilen örnek event.data, tanımsız olan bir şikayet hatası verir . Bu model daha iyi çalışıyor gibi görünüyor:

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
Bunun için hiç destek eklediniz mi?
Jim Cooper

20
Yine de bir işlemde müşteri tarafı sayacı güvenli midir? Sayımları yapay olarak artırmak için kolayca hacklenebilecek gibi görünüyor. Bu, oylama sistemleri için kötü olabilir.
Soviut

16
++ Transfer masraflarına neden olmadan sayımı almak gerçekten güzel olurdu
Jared Forsyth

27
Bu hiç eklendi mi?
Eliezer

25
Bu özellik yol haritasıyla ilgili herhangi bir haber var mı? Teşekkürler
Pandaiolo

38

Diğerlerinin zaten güzelce yanıt vermiş olması nedeniyle bu oyunda biraz geç kaldı, ancak bunu nasıl uygulayabileceğimi paylaşacağım.

Bu, Firebase REST API'nin bir shallow=trueparametre sunmasına bağlıdır .

Bir postnesneye sahip olduğunuzu ve her birinin birkaç tane olabileceğini varsayalım comments:

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

Belli ki tüm yorumları, sadece yorum sayısını getirmek istemiyorsunuz.

Bir gönderi için anahtara sahip olduğunuzu varsayarak, adresine bir GETistek gönderebilirsiniz https://yourapp.firebaseio.com/posts/[the post key]/comments?shallow=true.

Bu, her anahtarın bir yorumun anahtarı olduğu ve değerinin olduğu anahtar-değer çiftlerinden oluşan bir nesne döndürür true:

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

Bu yanıtın boyutu, eşdeğer veriyi istemekten çok daha küçüktür ve artık değerinizi bulmak için yanıttaki anahtarların sayısını hesaplayabilirsiniz (örneğin commentCount = Object.keys(result).length).

Hâlâ iade edilen anahtarların sayısını hesapladığınız için bu, sorununuzu tam olarak çözmeyebilir ve değiştikçe değere abone olamayabilirsiniz, ancak cihazınızda herhangi bir değişiklik yapılmasına gerek kalmadan döndürülen verilerin boyutunu büyük ölçüde azaltır. şema.


Sığ = doğru, önceki yanıtlardan bu yana yeni bir ekleme olduğundan, bunu kabul edilen yanıt yapabilir. Kendime bakacak zamanım olmadı, bu yüzden insanların ne düşündüğünü görmek için birkaç gün bekleyeceğim ...
josh

1
Sığ muhtemelen şimdi için en iyi seçenek, ama sıkıştırma ile döndü değil ve büyük veri kümeleri için oldukça yavaş ve deneyim haline gelebilir
Mbrevda

Açıklama anahtarlarının boole değerleri yoksa, ancak bunun yerine alt öğeleri varsa, anahtarların anahtar / değer çiftlerini yine de döndürüyor mu?
alltej

1
REST API'yi kullanırken dikkatli olmak isteyebilirsiniz: startupsventurecapital.com/…
Remi Sture

3
Sadece .jsonURL'nin sonuna eklemeniz gerektiğini belirtmek için, örneğin:https://yourapp.firebaseio.com/posts/comments.json?shallow=true
Osama Xäwãñz

22

Sayacı ilerledikçe kaydedin ve bunu uygulamak için doğrulamayı kullanın. Bunu birlikte hackledim - benzersiz oyları ve sayıları artmaya devam etmek için !. Ama bu sefer önerimi test ettim! (kesme / yapıştırma hatalarına rağmen!).

Buradaki 'numara', düğüm önceliğini oy sayımı olarak kullanmaktır ...

Veriler:

oy / $ issueBeingVotedOn / kullanıcı / $ uniqueIdOfVoter = thisVotesCount, öncelik = thisVotesCount oy / $ issueBeingVotedOn / count = 'kullanıcı /' + $ idOfLastVoter, öncelik = 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()" 

kullanıcı yalnızca bir kez oy kullanabilir && sayı, mevcut sayıdan bir yüksek olmalıdır ve& veri değeri, öncelik ile aynı olmalıdır.

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

say (gerçekten son seçmen) - oy mevcut olmalı ve sayısı newcount'a eşit olmalıdır, && newcount (öncelik) yalnızca bir artabilir.

  }
}

Farklı kullanıcılar tarafından 10 oy eklemek için test komut dosyası (bu örnek için, kimlik sahte, üretimde kullanıcı kimlik doğrulamalı olmalıdır). Doğrulamanın başarısız olduğunu görmek için (i--) 10 kadar geri sayın.

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

Buradaki 'risk', bir oylamanın atılması, ancak sayının güncellenmemesidir (haking veya komut dosyası hatası). Oyların benzersiz bir 'önceliğe' sahip olmasının nedeni budur - senaryo gerçekten mevcut sayıdan daha yüksek önceliğe sahip bir oylama olmadığından emin olarak başlamalıdır, eğer bu işlemi kendi işlemlerini yapmadan önce tamamlaması gerekiyorsa - müşterilerinizi temizlemelerini sağlayın senin için :)

Başlamadan önce sayımın bir öncelik ile başlatılması gerekir - forge bunu yapmanıza izin vermez, bu nedenle bir saplama betiğine ihtiyaç vardır (doğrulama etkin olmadan önce!).


Bu harika !!! Yine de çatışmalarda ne olur? Yani, iki kişi aynı anda mı oy veriyor? İdeal olarak, sadece oylarından birini atmak yerine, bunu otomatik olarak çözmek istersiniz ... belki bir işlemde oylama yapabilir?
josh

Merhaba Josh, mantıksal olarak gerçek bir oylama ancak önceki bir oylama yapılmışsa, ancak toplam (henüz) güncellenmemişse başarısız olabilir. 2.'den sonuncuya kadar olan paragraflar şunları kapsıyor - önceki seçmenler için toplam güncellemeyi yine de (her seferinde) yapardım - gerek yoksa ne olacak? ve sonra bu güncellemeleri oylar. Oylama iyi çalıştığı sürece. Eğer 'toplam' güncellemeniz başarısız olursa, bir sonraki seçmen bunu düzeltir, yani tekrar - peki ne?
pperrin

'Sayma' düğümünün 'önceki oy' düğümü olması gerektiğini söylemek gerçekten cazip geliyor - bu nedenle her seçmen / müşteri bu düğümü / değeri günceller / düzeltir / onarır ve ardından kendi oyunu ekler - (bir sonraki seçmenin 'bu' oyu içerecek şekilde toplam). - Eğer beni
anlarsan

4

düğüm sayısına bir bulut işlevi yazın ve güncelleyin.

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

Bakın: https://firebase.google.com/docs/functions/database-events

root-- | | -kullanıcılar (bu düğüm tüm kullanıcı listesini içerir) |
| -count | -userscount: (bu düğüm, kullanıcı sayısıyla bulut işlevi tarafından dinamik olarak eklenir)

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.