我认为进行计数的一种方法是这样的:
foo = db.GqlQuery("SELECT * FROM bar WHERE baz = 'baz')
my_count = foo.count()
我不喜欢的是我的计数将被限制为最大 1000 并且我的查询可能会很慢。有人有解决方法吗?我有一个想法,但感觉不干净。如果只有 GQL 有一个真正的 COUNT 函数......
我认为进行计数的一种方法是这样的:
foo = db.GqlQuery("SELECT * FROM bar WHERE baz = 'baz')
my_count = foo.count()
我不喜欢的是我的计数将被限制为最大 1000 并且我的查询可能会很慢。有人有解决方法吗?我有一个想法,但感觉不干净。如果只有 GQL 有一个真正的 COUNT 函数......
在使用像 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
+1 对耶希亚的回应。
在 GAE 上获取对象计数器的官方和祝福方法是构建分片计数器。尽管名字听起来很重,但这很简单。
所有数据库中的计数函数都很慢(例如,O(n))——GAE 数据存储只是让这一点更加明显。正如 Jehiah 建议的那样,您需要将计算出的计数存储在一个实体中,如果您想要可扩展性,请参考它。
这不是 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
我们现在拥有可用于查询实体计数和其他数据的 Datastore Statistics。这些值并不总是反映最近的更改,因为它们每 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);
}
最好的解决方法可能看起来有点违反直觉,但它在我所有的 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)
当我阅读 Aral Balkan 的严厉评论时,我已经这样做了:http: //aralbalkan.com/1504。这很令人沮丧,但是当您习惯它并意识到这比关系数据库上的 count() 快得多时,您不会介意...