web-dev-qa-db-fra.com

Dans Firebase, existe-t-il un moyen d'obtenir le nombre d'enfants d'un nœud sans charger toutes les données du nœud?

Vous pouvez obtenir le nombre d'enfants via

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

Mais je crois que cela récupère le sous-arbre entier de ce nœud sur le serveur. Pour les listes énormes, cela semble RAM et demande beaucoup de temps de latence. Existe-t-il un moyen d'obtenir le nombre (et/ou une liste de noms enfants) sans tout récupérer?

119
josh

L'extrait de code que vous avez donné charge effectivement l'ensemble des données, puis le compte côté client, ce qui peut être très lent pour de grandes quantités de données.

À l'heure actuelle, Firebase ne dispose pas d'un moyen de compter les enfants sans charger de données, mais nous prévoyons de l'ajouter.

Pour l'instant, une solution serait de conserver un compteur du nombre d'enfants et de le mettre à jour chaque fois que vous ajoutez un nouvel enfant. Vous pouvez utiliser une transaction pour compter les éléments, comme dans ce code de suivi des 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;
});

Pour plus d'informations, voir https://www.firebase.com/docs/transactions.html

UPDATE: Firebase a récemment publié Cloud Functions. Avec les fonctions de nuage, vous n'avez pas besoin de créer votre propre serveur. Vous pouvez simplement écrire des fonctions JavaScript et les télécharger sur Firebase. Firebase sera responsable du déclenchement des fonctions chaque fois qu'un événement se produit.

Si vous souhaitez compter les votes positifs par exemple, vous devez créer une structure similaire à celle-ci:

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

Et écrivez ensuite une fonction javascript pour augmenter le upvotes_count quand il y a une nouvelle écriture sur le noeud 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());
});

Vous pouvez lire la Documentation pour savoir comment Démarrer avec les fonctions du nuage .

Un autre exemple de comptage de messages est également disponible: https://github.com/firebase/functions-samples/blob/master/child-count/functions/index.js

Mise à jour janvier 2018

Les documents Firebase ont changé et donc au lieu de event, nous avons maintenant change et context.

L'exemple donné génère une erreur en se plaignant que event.data n'est pas défini. Ce modèle semble mieux fonctionner:

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

`` `

89
Andrew Lee

C'est un peu tard dans le jeu car plusieurs autres ont déjà bien répondu, mais je vais vous dire comment je pourrais le mettre en œuvre.

Cela dépend du fait que l'API Firebase REST API = offre un paramètre shallow=true].

Supposons que vous avez un objet post et que chacun d'entre eux peut avoir un nombre de comments:

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

Vous ne voulez évidemment pas récupérer tous les commentaires, mais simplement le nombre de commentaires.

En supposant que vous ayez la clé pour un article, vous pouvez envoyer une demande GET à https://yourapp.firebaseio.com/posts/[the post key]/comments?shallow=true.

Cela retournera un objet de paires clé-valeur, où chaque clé est la clé d'un commentaire et sa valeur est true:

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

La taille de cette réponse est beaucoup plus petite que celle de la demande de données équivalentes. Vous pouvez maintenant calculer le nombre de clés dans la réponse pour trouver votre valeur (par exemple, commentCount = Object.keys(result).length).

Cela ne résoudra peut-être pas complètement votre problème, car vous calculez toujours le nombre de clés renvoyées et vous ne pouvez pas nécessairement vous abonner à la valeur car elle change, mais cela réduit considérablement la taille des données renvoyées sans nécessiter de modification de votre schéma.

28
Alex Klibisz

Enregistrez le nombre au fur et à mesure et utilisez la validation pour le renforcer. J'ai piraté cela ensemble - pour garder un nombre de votes uniques et compte qui continue à monter !. Mais cette fois j'ai testé ma suggestion! (nonobstant les erreurs de couper/coller!).

Le "truc" consiste ici à utiliser le noeud priorité comme nombre de votes ...

Les données sont:

vote/$ issueBeingVotedOn/utilisateur/$ uniqueIdOfVoter = thisVotesCount, priorité = thisVotesCount vote/$ issueBeingVotedOn/count = 'utilisateur /' + $ idOfLastVoter, priorité = 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'utilisateur ne peut voter qu'une seule fois et le nombre doit être supérieur au nombre actuel. La valeur des données doit être identique à la priorité.

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

compte (dernier électeur vraiment) - le vote doit exister et son compte est égal à newcount, && newcount (priorité) ne peut être augmenté que d'un.

  }
}

Script de test pour ajouter 10 votes de différents utilisateurs (pour cet exemple, id est falsifié, doit-on utiliser auth.uid en production). Compte à rebours de (i--) 10 pour voir la validation échouer.

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

Le "risque" est qu’un vote est exprimé mais que le décompte n’a pas été mis à jour (haking ou échec du script). C’est la raison pour laquelle les votes ont une "priorité" unique - le script doit réellement commencer par s’assurer qu’il n’ya pas de vote dont la priorité est supérieure au nombre actuel, le cas échéant, elle devrait effectuer cette transaction avant de faire la sienne - obligez vos clients à nettoyer pour toi :)

Le nombre doit être initialisé avec une priorité avant de commencer - forge ne vous permet pas de le faire. Un script de raccord est donc nécessaire (avant que la validation ne soit active!).

22
pperrin

écrire une fonction cloud et mettre à jour le nombre de nœuds.

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

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

root-- | | -users (ce noeud contient la liste de tous les utilisateurs) |
| -count | -userscount: (ce nœud ajouté dynamiquement par la fonction cloud avec le nombre d'utilisateurs)

4
indvin