Pregunta

Me imagino que una forma de hacer un conteo es así:

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

Lo que no me gusta es que mi recuento se limitará a 1000 máximo y mi consulta probablemente será lenta. ¿Alguien por ahí con una solución? Tengo uno en mente, pero no se siente limpio. Si solo GQL tuviera una función COUNT real ...

¿Fue útil?

Solución

+1 a la respuesta de Jehiah.

El método oficial y bendecido para obtener contadores de objetos en GAE es crear contador de fragmentos . A pesar del nombre que suena fuertemente, este es bastante sencillo.

Otros consejos

Tienes que cambiar tu forma de pensar cuando trabajas con un almacén de datos escalable como GAE para hacer tus cálculos por adelantado. En este caso, eso significa que necesita mantener contadores para cada baz e incrementarlos cada vez que agregue una nueva barra , en lugar de contar en el momento de la visualización.

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

luego, al crear un objeto Bar, incremente el contador

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

ahora tiene una manera fácil de obtener el recuento de cualquier categoría específica

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

Las funciones de conteo en todas las bases de datos son lentas (p. ej., O (n)): el almacén de datos de GAE lo hace más obvio. Como sugiere Jehiah, debe almacenar el recuento calculado en una entidad y referirse a eso si desea escalabilidad.

Esto no es exclusivo de App Engine: otras bases de datos simplemente lo ocultan mejor, hasta el punto en que intentas contar decenas de miles de registros con cada solicitud, y el tiempo de representación de tu página comienza a aumentar exponencialmente. .

De acuerdo con GqlQuery.count () documentación , puede establecer el limit para que sea un número mayor que 1000:

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

Los contadores fragmentados son la forma correcta de realizar un seguimiento de números como este, como la gente ha dicho, pero si descubres esto al final del juego (como yo), entonces deberás inicializar los contadores a partir de un conteo real de objetos. Pero esta es una excelente manera de quemar su cuota gratuita de Datastore Small Operations (50,000, creo). Cada vez que ejecute el código, usará tantas operaciones como objetos de modelo.

No lo he intentado, y este es un recurso de recursos total, pero ¿tal vez iterar con .fetch () y especificar el desplazamiento funcionaría?

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 solución de orip funciona con un pequeño ajuste:

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

Ahora tenemos estadísticas del almacén de datos que se pueden usar para consultar los recuentos de entidades y otros datos. Estos valores no siempre reflejan los cambios más recientes, ya que se actualizan una vez cada 24-48 horas. Consulte la documentación (vea el enlace a continuación) para obtener más detalles:

Estadísticas del almacén de datos

Como señaló @Dimu, las estadísticas calculadas por Google periódicamente son un recurso decente cuando no se necesitan conteos precisos y el% de registros NO cambia drásticamente durante un día determinado.

Para consultar las estadísticas de un tipo determinado, puede usar la siguiente estructura GQL:

select * from __Stat_Kind__ where kind_name = 'Person'

Hay varias propiedades devueltas por esto que son útiles:

  • count : el número de entidades de este tipo
  • bytes : tamaño total de todas las entidades almacenadas de este tipo
  • timestamp : una fecha / hora a partir de para el momento en que se calcularon las estadísticas por última vez

Código de ejemplo

Para responder una pregunta de seguimiento publicada como un comentario a mi respuesta, ahora proporciono un código de C # de muestra que estoy usando, que ciertamente puede no ser tan robusto como debería ser, pero parece funcionar bien para mí:

/// <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 mejor solución puede parecer poco intuitiva, pero funciona muy bien en todas las aplicaciones de mi appengine. En lugar de confiar en los métodos KEY y count () enteros, agregue un campo entero propio al tipo de datos. Puede parecer un desperdicio hasta que realmente tengas más de 1000 registros, y de repente descubres que fetch () y limit () NO TRABAJAN PASANDO EL LÍMITE DE REGISTRO DE 1000.

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

Cuando creas un nuevo objeto, debes recuperar manualmente la clave más alta:

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

Esto puede parecer un desperdicio de una consulta, pero get () devuelve un solo registro de la parte superior del índice. Es muy rápido.

Luego, cuando desee obtener más allá del límite del objeto número 1000, simplemente haga lo siguiente:

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

Ya había hecho esto cuando leí la crítica mordaz de Aral Balkan: http://aralbalkan.com/1504. Es frustrante, pero cuando te acostumbras y te das cuenta de que esto es más rápido que contar () en una base de datos relacional, no te importará ...

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top