Как лучше всего посчитать результаты в GQL? - PullRequest
29 голосов
/ 07 января 2009

Я считаю, что один из способов подсчета выглядит так:

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

Что мне не нравится, так это то, что мой счет будет ограничен до 1000 макс, и мой запрос, вероятно, будет медленным. У кого-нибудь есть обходной путь? Я имею в виду один, но он не чувствует себя чистым. Если бы только GQL имел реальную функцию COUNT ...

Ответы [ 9 ]

20 голосов
/ 08 января 2009

При работе с масштабируемым хранилищем данных, таким как GAE, вы должны изменить свое мышление, чтобы выполнить ваши расчеты заранее. В этом случае это означает, что вам нужно сохранять счетчики для каждого baz и увеличивать их всякий раз, когда вы добавляете новый bar, вместо того, чтобы считать во время отображения.

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

затем при создании объекта Bar увеличьте счетчик

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

теперь у вас есть простой способ получить счет для любой конкретной категории

CategoryCounter.filter('category =',category_name).get().count
17 голосов
/ 26 февраля 2009

+ 1 к ответу Иехиа.

Официальный и благословенный метод получения счетчиков объектов в GAE - это создание осколочного счетчика . Несмотря на громкое название, это довольно просто.

7 голосов
/ 08 января 2009

Функции подсчета во всех базах данных работают медленно (например, O (n)) - хранилище данных GAE только делает это более очевидным. Как предполагает Джехия, вам нужно хранить вычисленное количество в сущности и ссылаться на него, если вы хотите масштабируемости.

Это не уникально для App Engine - другие базы данных просто скрывают это лучше, вплоть до того момента, когда вы пытаетесь считать десятки тысяч записей с каждым запросом, и время отображения вашей страницы начинает экспоненциально увеличиваться. .

2 голосов
/ 31 января 2013

В соответствии с документацией GqlQuery.count() вы можете установить для limit значение, большее 1000:

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

Заштрихованные счетчики - это правильный способ отслеживать числа, подобные этому, как говорили люди, но если вы поймете это в конце игры (как я), то вам нужно будет инициализировать счетчики из фактического количества объекты. Но это отличный способ прожить вашу бесплатную квоту Datastore Small Operations (я думаю, 50 000). Каждый раз, когда вы запускаете код, он использует столько операций, сколько имеется объектов модели.

0 голосов
/ 11 марта 2018

Как отмечает @Dimu, статистические данные, рассчитываемые Google на периодической основе, являются хорошим ресурсом для перехода, когда нет необходимости в точных подсчетах и% записей НЕ кардинально меняется в течение любого дня.

Чтобы запросить статистику для данного вида, вы можете использовать следующую структуру GQL:

select * from __Stat_Kind__ where kind_name = 'Person'

Существует несколько свойств, которые могут быть возвращены этим:

  • count - количество сущностей этого вида
  • bytes - общий размер всех сущностей, хранящихся в этом виде
  • timestamp - по состоянию на дата / время, когда статистика в последний раз вычислялась

Пример кода

Чтобы ответить на последующий вопрос, опубликованный в качестве комментария к моему ответу, я сейчас предоставляю пример используемого C# кода, который, по общему признанию, может быть не таким надежным, как должен, но, похоже, работает нормально. для меня:

/// <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);
}
0 голосов
/ 07 февраля 2017

Теперь у нас есть статистика хранилища данных, которую можно использовать для запроса количества объектов и других данных. Эти значения не всегда отражают самые последние изменения, так как они обновляются каждые 24-48 часов. Ознакомьтесь с документацией (см. Ссылку ниже) для получения более подробной информации:

Статистика хранилища данных

0 голосов
/ 07 февраля 2011

решение orip работает с небольшой настройкой:

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
0 голосов
/ 19 января 2010

Лучший обходной путь может показаться немного нелогичным, но он прекрасно работает во всех моих приложениях appengine. Вместо того чтобы полагаться на целочисленные методы KEY и count (), вы добавляете собственное целочисленное поле к типу данных. Это может показаться расточительным, пока у вас на самом деле не будет более 1000 записей, и вы вдруг обнаружите, что fetch () и limit () НЕ РАБОТАЮТ ЗА ГРАНИЦУ 1000 ЗАПИСЕЙ.

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

Когда вы создаете новый объект, вы должны вручную получить самый высокий ключ:

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

Это может показаться пустой тратой на запрос, но get () возвращает одну запись в верхней части индекса. Это очень быстро.

Затем, когда вы хотите извлечь предел 1000-го объекта, вы просто делаете:

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

Я уже сделал это, когда прочитал скандальный обзор Арала Балкана: http://aralbalkan.com/1504. Это разочаровывает, но когда вы привыкнете к этому и поймете, насколько это быстрее, чем count () для реляционных БД, вы не будете возражать ...

0 голосов
/ 08 января 2009

Я не пробовал, и это полная проблема с ресурсами, но, возможно, итерация с .fetch() и указание смещения будет работать?

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