Domanda

Immagino che un modo per fare un conteggio sia così:

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

Quello che non mi piace è il mio conteggio sarà limitato a 1000 max e la mia query sarà probabilmente lenta. Qualcuno là fuori con una soluzione alternativa? Ne ho uno in mente, ma non sembra pulito. Se solo GQL avesse una vera funzione COUNT ...

È stato utile?

Soluzione

+1 alla risposta di Jehiah.

Il metodo ufficiale e benedetto per ottenere contatori di oggetti su GAE è quello di creare contatore con divisione . Nonostante il nome pesantemente suonante, questo è piuttosto semplice.

Altri suggerimenti

Devi girare il tuo pensiero quando lavori con un archivio dati scalabile come GAE per eseguire i tuoi calcoli in anticipo. In questo caso ciò significa che devi mantenere i contatori per ogni baz e incrementarli ogni volta che aggiungi una nuova barra , invece di contare al momento della visualizzazione.

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

quindi quando si crea un oggetto Bar, incrementare il contatore

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

ora hai un modo semplice per ottenere il conteggio per qualsiasi categoria specifica

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

Le funzioni di conteggio in tutti i database sono lente (ad es. O (n)) - l'archivio dati GAE lo rende più ovvio. Come suggerisce Jehiah, è necessario archiviare il conteggio calcolato in un'entità e fare riferimento a questo se si desidera la scalabilità.

Questo non è univoco per App Engine: altri database lo nascondono meglio, fino al punto in cui stai provando a contare decine di migliaia di record con ogni richiesta e il tempo di rendering della pagina inizia ad aumentare in modo esponenziale. .

Secondo GqlQuery.count () documentazione , puoi impostare il limite su un numero maggiore di 1000:

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

I segnalini frammentati sono il modo giusto per tenere traccia di numeri come questo, come hanno detto la gente, ma se lo capisci alla fine del gioco (come me), dovrai inizializzare i contatori da un conteggio effettivo di oggetti. Ma questo è un ottimo modo per bruciare la tua quota gratuita di Datastore Small Operations (50.000 credo). Ogni volta che esegui il codice, utilizzerà tutte le operazioni quante sono gli oggetti modello.

Non l'ho provato, e questo è un vero e proprio hog di risorse, ma forse iterare con .fetch () e specificare l'offset funzionerebbe?

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 soluzione di orip funziona con un piccolo ritocco:

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

Ora disponiamo di statistiche del datastore che possono essere utilizzate per interrogare i conteggi delle entità e altri dati. Questi valori non riflettono sempre le modifiche più recenti in quanto vengono aggiornati una volta ogni 24-48 ore. Consulta la documentazione (vedi link sotto) per maggiori dettagli:

Statistiche del datastore

Come sottolineato da @Dimu, le statistiche calcolate da Google su base periodica sono una risorsa accettabile quando non sono necessari conteggi precisi e la% dei record NON cambia drasticamente in un dato giorno.

Per interrogare le statistiche per un determinato tipo, puoi usare la seguente struttura GQL:

select * from __Stat_Kind__ where kind_name = 'Person'

Ci sono un certo numero di proprietà restituite da questo che sono utili:

  • count - il numero di Entità di questo tipo
  • byte - dimensione totale di tutte le entità memorizzate di questo tipo
  • timestamp - un a partire da data / ora dell'ultimo calcolo delle statistiche

Codice di esempio

Per rispondere a una domanda di follow-up pubblicata come commento alla mia risposta, ora sto fornendo alcuni esempi di codice C # che sto usando, che è vero che potrebbe non essere robusto come dovrebbe essere, ma sembra funzionare bene per me:

/// <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 soluzione migliore potrebbe sembrare un po 'controintuitiva, ma funziona benissimo in tutte le mie app appengine. Invece di fare affidamento sui metodi intero KEY e count (), aggiungi un campo intero tuo al tipo di dati. Potrebbe sembrare inutile fino a quando non avrai effettivamente più di 1000 record e scoprirai improvvisamente che fetch () e limit () NON FUNZIONANO PASSANDO IL CONFINE DEI RECORD 1000.

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

Quando si crea un nuovo oggetto, è necessario recuperare manualmente la chiave più alta:

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

Questo può sembrare uno spreco di una query, ma get () restituisce un singolo record dalla parte superiore dell'indice. È molto veloce.

Quindi, quando vuoi andare oltre il 1000 ° limite dell'oggetto, devi semplicemente:

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

L'avevo già fatto quando ho letto la recensione di Aral Balkan: http://aralbalkan.com/1504. È frustrante, ma quando ti abitui e ti rendi conto di quanto è più veloce di contare () su un db relazionale, non ti dispiacerà ...

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top