Ответ 1
+1 к ответу Иехии.
Официальный и благословенный метод получения счетчиков объектов в GAE заключается в создании sharded counter. Несмотря на сильно звучащее имя, это довольно просто.
Я считаю, что один способ сделать подсчет выглядит следующим образом:
foo = db.GqlQuery("SELECT * FROM bar WHERE baz = 'baz')
my_count = foo.count()
Что мне не нравится, мой счет будет ограничен 1000 макс, и мой запрос, вероятно, будет медленным. Кто-нибудь там с обходным решением? Я имею в виду, но он не чувствует себя чистым. Если только GQL имел реальную функцию COUNT...
+1 к ответу Иехии.
Официальный и благословенный метод получения счетчиков объектов в GAE заключается в создании sharded counter. Несмотря на сильно звучащее имя, это довольно просто.
Вы должны перевернуть свое мышление при работе с масштабируемым хранилищем данных, например 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)
Зашаренные счетчики - это правильный способ отслеживать такие числа, как это было сказано людьми, но если вы выясните это в конце игры (например, я), вам нужно будет инициализировать счетчики с фактического количества объекты. Но это отличный способ прорваться через вашу свободную квоту на Small Small Operations (50 000, я думаю). Каждый раз, когда вы запускаете код, он будет использовать столько операционных систем, сколько есть объектов модели.
Я не пробовал, и это полный ресурс hog, но, возможно, повторение с помощью .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
Теперь мы имеем статистику Datastore, которая может использоваться для запроса количества объектов и других данных. Эти значения не всегда отражают самые последние изменения, поскольку они обновляются каждые 24-48 часов. Ознакомьтесь с документацией (см. Ссылку ниже) для более подробной информации:
Лучшее обходное решение может показаться немного контр-интуитивным, но оно отлично работает во всех приложениях 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() на реляционном db, вы не будете возражать...