Pergunta

Eu acho que uma maneira de fazer uma contagem é assim:

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

O que eu não gosto é minha contagem será limitada a 1000 máximo e minha consulta provavelmente será lenta. Alguém lá fora, com uma solução alternativa? Eu tenho um em mente, mas não se sentir limpo. Se apenas GQL tinha uma verdadeira função count ...

Foi útil?

Solução

1 para a resposta de Jeías.

método oficial e abençoou em obter contadores do objeto no GAE é construir sharded counter . Apesar do nome fortemente soando, este é bastante simples.

Outras dicas

Você tem que virar seu pensamento quando se trabalha com um armazenamento de dados escalável como GAE para fazer seus cálculos na frente. Neste caso, isso significa que você precisa para manter contadores para cada baz e incrementar-los sempre que você adicionar um novo bar, em vez de contar no momento da exibição.

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

, em seguida, ao criar um objeto Bar, incrementar o 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')

Agora você tem uma maneira fácil de obter a contagem para qualquer categoria específica

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

Contagem funções em todos os bancos de dados são lentas (por exemplo, O (n)) - o armazenamento de dados GAE só faz isso mais evidente. Como Jeías sugere, você precisa armazenar a contagem computadorizada em uma entidade e referem-se a que, se quiser escalabilidade.

Isso não é exclusivo para o App Engine - outros bancos de dados apenas escondê-lo melhor, até o ponto onde você está tentando contar dezenas de milhares de registros com cada solicitação, e sua página prestar começa tempo para aumentar exponencialmente .. .

De acordo com a GqlQuery.count() documentação , você pode definir o limit ser algum número maior que 1000:

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

contadores Sharded são o caminho certo para manter o controle de números como este, pois as pessoas têm dito, mas se você descobrir isso no final do jogo (como eu), então você vai precisar para inicializar os contadores a partir de uma contagem real de objetos. Mas esta é uma ótima maneira de queimar através de sua cota livre de armazenamento de dados Operações pequeno (50.000 eu acho). Toda vez que você executar o código, ele irá usar o máximo de ops, pois há objetos de modelo.

Eu não tentei, e este é um recurso porco absoluta, mas talvez interagindo com .fetch() e especificando o deslocamento iria trabalhar?

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

A solução da orip trabalha com um pouco de ajustes:

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

Agora temos Datastore Estatísticas que pode ser usado para contagem de entidade de consulta e outros dados. Esses valores nem sempre refletem as mudanças mais recentes como eles são atualizados uma vez a cada 24-48 horas. Confira a documentação (ver link abaixo) para mais detalhes:

Datastore Estatísticas

Como apontado por @Dimu, as estatísticas calculadas pelo Google em uma base periódica são um decente go-to de recursos quando as contagens precisas não são necessários e a% de registros não estão mudando drasticamente durante um determinado dia.

Para consultar as estatísticas para um determinado tipo, você pode usar a seguinte estrutura GQL:

select * from __Stat_Kind__ where kind_name = 'Person'

Há uma série de propriedades retornados por este, que são úteis:

  • count - O número de entidades deste tipo
  • bytes - tamanho total de todas as entidades armazenados deste tipo
  • timestamp - um a partir de data / hora para quando as estatísticas foram última computadorizada

Exemplo de código

Para responder a uma pergunta de acompanhamento postado como um comentário à minha resposta, agora estou fornecendo um código de exemplo C# que estou usando, que é certo que não pode ser tão robusta como deveria ser, mas parece funcionar bem para mim :

/// <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 = $"select * from __Stat_Kind__ where kind_name = '{kind}'",
        AllowLiterals = true
    };
    var result = database.RunQuery(query);
    return (long) (result?.Entities?[0]?["count"] ?? 0L);
}

A melhor solução pode parecer um pouco contra-intuitivo, mas funciona muito bem em todos os meus aplicativos AppEngine. Ao invés de confiar nos métodos de contagem () CHAVE inteiro e, você adiciona um campo inteiro de sua preferência para o tipo de dados. Pode parecer um desperdício até que você realmente tem mais de 1000 registros, e de repente você descobre que fetch () e limite de () não funcionam PASSADO 1000 gravar o limite.

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

Quando você cria um novo objeto, você deve recuperar manualmente a chave mais alto:

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

Isto pode parecer um desperdício de uma consulta, mas get () retorna um único registro fora do topo do índice. É muito rápido.

Então, quando você deseja buscar passado o limite de 1000 objeto, você simplesmente fazer:

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

Eu já tinha feito isso quando eu li análise contundente de Aral Balkan: http://aralbalkan.com/1504. É frustrante, mas quando você se acostumar com isso e você percebe o quanto mais rápido isso é que a contagem () em um banco de dados relacional, você não vai se importar ...

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top