Question

Je pense que l’une des façons de compter est comme ceci:

foo = db.GqlQuery("SELECT * FROM bar WHERE baz = 'baz')
my_count = foo.count()

Ce que je n’aime pas, c’est que mon compte sera limité à 1 000 max et que ma requête sera probablement lente. Quelqu'un avec une solution de contournement? J'en ai une en tête, mais ça ne se sent pas propre. Si seulement GQL avait une vraie fonction COUNT ...

Était-ce utile?

La solution

+1 à la réponse de Jéhiah.

La méthode officielle et bénéfique pour obtenir des compteurs d’objets sur GAE consiste à construire le compteur fragmenté . Malgré le nom qui sonne fort, c'est assez simple.

Autres conseils

Lorsque vous travaillez avec un magasin de données évolutif tel que GAE, vous devez réfléchir avant de faire vos calculs. Dans ce cas, cela signifie que vous devez conserver des compteurs pour chaque baz et les incrémenter chaque fois que vous ajoutez une nouvelle barre , au lieu de compter au moment de l'affichage.

class CategoryCounter(db.Model):
    category = db.StringProperty()
    count = db.IntegerProperty(default=0)

puis lors de la création d'un objet Bar, incrémentez le compteur

def createNewBar(category_name):
  bar = Bar(...,baz=category_name)

  counter = CategoryCounter.filter('category =',category_name).get()
  if not counter:
    counter = CategoryCounter(category=category_name)
  else:
    counter.count += 1
  bar.put()
  counter.put()

db.run_in_transaction(createNewBar,'asdf')

vous avez maintenant un moyen facile d'obtenir le nombre pour une catégorie spécifique

CategoryCounter.filter('category =',category_name).get().count

Les fonctions de comptage dans toutes les bases de données sont lentes (par exemple, O (n)) - le magasin de données GAE rend cela plus évident. Comme le suggère Jehiah, vous devez stocker le nombre calculé dans une entité et vous y référer si vous souhaitez une évolutivité.

Cela ne concerne pas uniquement App Engine: d'autres bases de données la cachent mieux, jusqu'au point où vous essayez de compter des dizaines de milliers d'enregistrements à chaque demande, et le temps de rendu de votre page commence à augmenter de façon exponentielle. .

Selon le GqlQuery.count () documentation , vous pouvez définir la limite sur un nombre supérieur à 1000:

from models import Troll
troll_count = Troll.all(keys_only=True).count(limit=31337)

Les compteurs fragmentés sont le bon moyen de garder une trace de tels chiffres, comme l’ont dit les gens, mais si vous vous en rendez compte tard dans le jeu (comme moi), vous devrez initialiser les compteurs à partir du nombre réel de objets. Mais c’est un excellent moyen de graver votre quota gratuit de Datastore Small Operations (50 000, je pense). Chaque fois que vous exécutez le code, il utilisera autant d'opérations qu'il y a d'objets de modèle.

Je ne l’ai pas essayé et c’est une ressource absolue, mais peut-être qu’itérer avec .fetch () et spécifier le décalage fonctionnerait?

LIMIT=1000
def count(query):
   result = offset = 0
   gql_query = db.GqlQuery(query)
   while True:
     count = gql_query.fetch(LIMIT, offset)
     if count < LIMIT:
       return result
     result += count
     offset += LIMIT
La solution de

orip fonctionne avec un petit ajustement:

LIMIT=1000
def count(query):
    result = offset = 0
    gql_query = db.GqlQuery(query)
    while True:
        count = len(gql_query.fetch(LIMIT, offset))
        result += count
        offset += LIMIT
        if count < LIMIT:
            return result

Nous disposons maintenant de statistiques de magasin de données pouvant être utilisées pour interroger le nombre d'entités et d'autres données. Ces valeurs ne reflètent pas toujours les modifications les plus récentes car elles sont mises à jour toutes les 24 à 48 heures. Consultez la documentation (voir lien ci-dessous) pour plus de détails:

Statistiques sur le magasin de données

Comme l'a souligné @Dimu, les statistiques calculées périodiquement par Google sont une ressource fiable lorsque des comptes précis ne sont pas nécessaires et que le% d'enregistrements NE change PAS de manière drastique au cours d'une journée donnée.

Pour interroger les statistiques d'un type donné, vous pouvez utiliser la structure GQL suivante:

select * from __Stat_Kind__ where kind_name = 'Person'

Ceci retourne plusieurs propriétés utiles:

  • count - le nombre d'entités de ce type
  • octets - taille totale de toutes les entités stockées de ce type
  • horodatage - une date / heure à partir de pour le dernier calcul des statistiques

Exemple de code

Pour répondre à une question de suivi postée en tant que commentaire sur ma réponse, je fournis maintenant un exemple de code C # que j'utilise, qui peut ne pas être aussi robuste qu'il devrait l'être, mais semble fonctionner correctement pour moi:

/// <summary>Returns an *estimated* number of entities of a given kind</summary>
public static long GetEstimatedEntityCount(this DatastoreDb database, string kind)
{
    var query = new GqlQuery
    {
        QueryString = <*>quot;select * from __Stat_Kind__ where kind_name = '{kind}'",
        AllowLiterals = true
    };
    var result = database.RunQuery(query);
    return (long) (result?.Entities?[0]?["count"] ?? 0L);
}

La meilleure solution de contournement peut sembler un peu contre-intuitive, mais elle fonctionne très bien dans toutes mes applications d'applications. Plutôt que de compter sur les méthodes integer KEY et count (), vous ajoutez un champ entier de votre choix au type de données. Cela peut sembler inutile jusqu’à ce que vous ayez réellement plus de 1000 enregistrements et que vous découvriez soudain que fetch () et limit () NE TRAVAILLENT PAS AU-DELA DE LA LIMITE DES RECORD 1000.

def MyObj(db.Model):
  num = db.IntegerProperty()

Lorsque vous créez un nouvel objet, vous devez récupérer manuellement la clé la plus élevée:

max = MyObj.all().order('-num').get()
if max : max = max.num+1
else : max = 0
newObj = MyObj(num = max)
newObj.put()

Cela peut sembler un gaspillage de requête, mais get () renvoie un seul enregistrement en haut de l'index. C'est très rapide.

Ensuite, lorsque vous voulez aller au-delà de la 1000e limite d'objet, vous faites simplement:

MyObj.all().filter('num > ' , 2345).fetch(67)

Je l'avais déjà fait lorsque j'ai lu la critique cinglante d'Aral Balkan: http://aralbalkan.com/1504. C'est frustrant, mais quand vous vous y habituez et que vous vous rendez compte à quel point c'est plus rapide que count () sur une base de données relationnelle, cela ne vous dérange pas ...

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top