In Firebase, c'è un modo per ottenere il numero di figli di un nodo senza caricare tutti i dati del nodo?


132

Puoi ottenere il conteggio dei bambini tramite

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

Ma credo che questo recuperi l'intero sottoalbero di quel nodo dal server. Per elenchi di grandi dimensioni, ciò sembra intensivo di RAM e latenza. Esiste un modo per ottenere il conteggio (e / o un elenco di nomi figlio) senza recuperare il tutto?


grazie mille lo provo e ha funzionato per me
Ahmed Mahmoud,

il tuo codice non è in grado di gestire un set di dati di grandi dimensioni. Ho ricevuto la causa dell'errore dallo spazio heap di Java. Sto ancora aspettando alcune funzionalità.
Panupong Kongarn,

Risposte:


98

Lo snippet di codice fornito in effetti carica l'intero set di dati e quindi lo conta sul lato client, che può essere molto lento per grandi quantità di dati.

Firebase al momento non ha modo di contare i bambini senza caricare i dati, ma prevediamo di aggiungerli.

Per ora, una soluzione sarebbe quella di mantenere un contatore del numero di figli e aggiornarlo ogni volta che aggiungi un nuovo figlio. Puoi utilizzare una transazione per contare gli articoli, come in questo codice che tiene traccia degli upvode:

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

Per ulteriori informazioni, consultare https://www.firebase.com/docs/transactions.html

AGGIORNAMENTO: Firebase ha recentemente rilasciato funzioni cloud. Con le funzioni cloud, non è necessario creare il proprio server. Puoi semplicemente scrivere funzioni JavaScript e caricarlo su Firebase. Firebase sarà responsabile dell'attivazione delle funzioni ogni volta che si verifica un evento.

Se si desidera contare i voti positivi, ad esempio, è necessario creare una struttura simile a questa:

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

E quindi scrivere una funzione JavaScript per aumentare la upvotes_countpresenza di una nuova scrittura sul upvotesnodo.

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

Puoi leggere la documentazione per sapere come iniziare con le funzioni cloud .

Inoltre, un altro esempio di conteggio dei post è qui: https://github.com/firebase/functions-samples/blob/master/child-count/functions/index.js

Aggiornamento gennaio 2018

I documenti Firebase sono cambiati, quindi invece di eventora abbiamo changee context.

L'esempio fornito genera un errore di reclamo event.datanon definito. Questo modello sembra funzionare meglio:

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
Hai mai aggiunto supporto per questo?
Jim Cooper,

20
Tuttavia, un contatore sul lato client in una transazione è sicuro? Sembra che potrebbe essere facilmente hackerato per aumentare artificialmente i conteggi. Questo potrebbe essere negativo per i sistemi di voto.
Soviet

16
++ sarebbe davvero bello ottenere il conteggio senza incorrere in costi di trasferimento
Jared Forsyth

27
Questo è mai stato aggiunto?
Eliezer,

25
Qualche novità su questa roadmap delle funzionalità? Grazie
Pandaiolo,

38

Questo è un po 'tardi nel gioco poiché molti altri hanno già risposto bene, ma condividerò come potrei implementarlo.

Ciò dipende dal fatto che l' API REST Firebase offre un shallow=trueparametro.

Supponi di avere un postoggetto e ognuno può avere un numero di comments:

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

Ovviamente non vuoi recuperare tutti i commenti, ma solo il numero di commenti.

Supponendo di avere la chiave per un post, è possibile inviare una GETrichiesta a https://yourapp.firebaseio.com/posts/[the post key]/comments?shallow=true.

Ciò restituirà un oggetto di coppie chiave-valore, dove ogni chiave è la chiave di un commento e il suo valore è true:

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

La dimensione di questa risposta è molto più piccola rispetto alla richiesta di dati equivalenti e ora puoi calcolare il numero di chiavi nella risposta per trovare il tuo valore (es. CommentCount = Object.keys(result).length).

Questo potrebbe non risolvere completamente il tuo problema, poiché stai ancora calcolando il numero di chiavi restituite e non puoi necessariamente iscriverti al valore mentre cambia, ma riduce notevolmente la dimensione dei dati restituiti senza richiedere alcuna modifica al tuo schema.


Potrebbe rendere questa la risposta accettata poiché shallow = true è una nuova aggiunta rispetto alle risposte precedenti. Non ho avuto il tempo di esaminarlo da solo, quindi aspetterò qualche giorno per vedere cosa pensa la gente ...
josh

1
Shallow è probabilmente l'opzione migliore per ora, ma non viene restituito con la compressione e può diventare piuttosto lento ed esperienza per grandi set di dati
Mbrevda

Se le chiavi di commento non hanno valori booleani ma hanno invece figli, restituisce comunque le coppie chiave-valore di chiavi?
alltej,

1
Potresti voler stare attento usando l'API REST: startupsventurecapital.com/…
Remi Sture

3
Solo per sottolineare che è necessario aggiungere .jsonalla fine dell'URL, ad esempio:https://yourapp.firebaseio.com/posts/comments.json?shallow=true
Osama Xäwãñz,

22

Salva il conteggio mentre procedi e utilizza la convalida per applicarlo. L'ho hackerato insieme - per tenere un numero di voti e conteggi unici che continuano a salire !. Ma questa volta ho testato il mio suggerimento! (nonostante gli errori taglia / incolla!).

Il "trucco" qui è usare la priorità del nodo come numero di voti ...

I dati sono:

voto / $ issueBeingVotedOn / user / $ uniqueIdOfVoter = thisVotesCount, priority = thisVotesCount voto / $ 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()" 

l'utente può votare solo una volta che il conteggio di&& deve essere uno superiore al conteggio corrente.

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

count (ultimo elettore in realtà) - il voto deve esistere e il suo conteggio equivale a newcount, && newcount (priorità) può aumentare solo di uno.

  }
}

Script di prova per aggiungere 10 voti di utenti diversi (per questo esempio, id falso, nel caso l'utente debba utilizzare il prodotto in produzione). Conto alla rovescia di (i--) 10 per vedere la validazione fallita.

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

Il "rischio" qui è che viene espresso un voto, ma il conteggio non viene aggiornato (haking o errore dello script). Questo è il motivo per cui i voti hanno una 'priorità' unica - lo script dovrebbe davvero iniziare assicurandosi che non vi sia alcun voto con priorità superiore al conteggio corrente, se esiste dovrebbe completare quella transazione prima di fare la propria - convincere i vostri clienti a pulire per te :)

Il conteggio deve essere inizializzato con una priorità prima di iniziare: forge non ti consente di farlo, quindi è necessario uno script stub (prima che la convalida sia attiva!).


Questo e spettacolare!!! Cosa succede sui conflitti, però? Cioè, due persone votano allo stesso tempo? Idealmente, vorresti risolverlo automaticamente, invece di scartare uno dei loro voti ... magari votare in una transazione?
josh,

Ciao Josh, logicamente un voto genuino può fallire solo se è stato espresso un voto precedente, ma il totale non è stato aggiornato (ancora). Il mio penultimo paragrafo copre che - farei solo l'aggiornamento totale per i votanti precedenti, comunque (ogni volta) - se non fosse necessario, e allora? e quindi questo voto si aggiorna. Finché il voto funziona bene. Se il tuo aggiornamento "totale" fallisce, il prossimo elettore lo risolverà, quindi di nuovo - e allora?
pperrin,

Sono davvero tentato di dire che il nodo "conta" dovrebbe essere un nodo "ultimo voto precedente", quindi ogni elettore / client aggiorna / corregge / ripara quel nodo / valore e quindi aggiunge il proprio voto - (lasciando che il prossimo elettore aggiorni il totale per includere "questo" voto). - Se ottieni la mia deriva ...
Pperrin,

4

scrivere una funzione cloud e aggiornare il conteggio dei nodi.

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

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

root-- | | -users (questo nodo contiene l'elenco di tutti gli utenti) |
| -count | -userscount: (questo nodo aggiunto dinamicamente dalla funzione cloud con il conteggio degli utenti)

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.