Вопрос

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

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

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

Это было полезно?

Решение

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

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

Другие советы

При работе с масштабируемым хранилищем данных, таким как 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

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

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

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

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

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

Я не пробовал, и это большая проблема с ресурсами, но, возможно, итерация с .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

Решение 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

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

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

Как отметил @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);
}

Лучший обходной путь может показаться немного нелогичным, но он прекрасно работает во всех моих приложениях. Вместо того чтобы полагаться на целочисленные методы 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 () для реляционных БД, вы не будете возражать ...

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top