在GQL中计算结果的最佳方法是什么?

时间:2009-01-07 19:37:16

标签: google-app-engine gql gqlquery

我认为计算的一种方法是这样的:

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

我不喜欢的是我的数量将限制为最多1000次,我的查询可能会很慢。那里的人有解决方法吗?我有一个想法,但它感觉不干净。如果只有GQL有一个真正的COUNT函数......

9 个答案:

答案 0 :(得分:20)

在使用像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 :(得分:17)

+1以耶希亚的回应。

在GAE上获取对象计数器的官方和祝福方法是构建sharded counter。尽管声名鹊起,但这非常简单。

答案 2 :(得分:7)

所有数据库中的计数函数都很慢(例如,O(n)) - GAE数据存储区使这一点变得更加明显。正如Jehiah建议的那样,您需要将计算出的计数存储在实体中,如果您想要可伸缩性,请参考该数据。

这不是App Engine独有的 - 其他数据库只是更好地隐藏它,直到你试图用每个请求计算成千上万条记录,并且你的页面渲染时间开始呈指数增长。

答案 3 :(得分:2)

根据GqlQuery.count() documentation,您可以将limit设置为大于1000的某个数字:

from models import Troll
troll_count = Troll.all(keys_only=True).count(limit=31337)
正如人们所说的那样,分片计数器是跟踪这样的数字的正确方法,但是如果你在游戏的后期(比如我)弄清楚了这一点,那么你需要从实际计数中初始化计数器对象。但这是一种很好的方式来烧掉你的数据存储小型操作的免费配额(我认为是50,000)。每次运行代码时,它都将使用与模型对象一样多的操作。

答案 4 :(得分:0)

我还没有尝试过,这是一个彻头彻尾的资源困境,但也许用.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

答案 5 :(得分:0)

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

答案 6 :(得分:0)

我们现在拥有可用于查询实体计数和其他数据的数据存储统计信息。这些值并不总是反映最近的更改,因为它们每24-48小时更新一次。查看文档(参见下面的链接)了解更多详细信息:

Datastore Statistics

答案 7 :(得分:0)

正如@Dimu指出的那样,谷歌定期计算的统计数据是一个不错的首选资源,当不需要精确计数时,记录的百分比在任何特定日期都不会发生剧烈变化。

要查询给定种类的统计信息,可以使用以下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);
}

答案 8 :(得分:-1)

最好的解决方法可能看起来有点违反直觉,但它在我的所有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()快多少时,你就不会介意......