代码之家  ›  专栏  ›  技术社区  ›  barneytron

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

  •  29
  • barneytron  · 技术社区  · 16 年前

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

    我不喜欢的是我的计数将被限制在1000最大值,我的查询可能会很慢。有人有解决办法吗?我想买一个,但感觉不干净。如果GQL有一个真正的计数函数。。。

    9 回复  |  直到 15 年前
        1
  •  20
  •   Jehiah    16 年前

    在使用像GAE这样的可伸缩数据存储进行前期计算时,您必须改变想法。在这种情况下,这意味着您需要为每个 baz 并在每次添加新的 bar

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

    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
    
        2
  •  17
  •   zgoda    15 年前

    +1对Jehiah的回答。

    在GAE上获取对象计数器的官方方法是构建 sharded counter . 尽管名字听起来很重,但这很简单。

        3
  •  7
  •   Nick Johnson    16 年前

    这不是App Engine所独有的—其他数据库只是更好地隐藏了它,直到您尝试在每个请求中计算数万条记录,并且您的页面呈现时间开始呈指数级增加。。。

        4
  •  2
  •   rescdsk    12 年前

    根据 GqlQuery.count() documentation limit 大于1000的数字:

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

    切分计数器是跟踪像这样的数字的正确方法,正如人们所说,但是如果你在游戏后期(像我一样)发现了这一点,那么你需要根据对象的实际计数初始化计数器。但是,这是一个很好的方法,可以让您的免费数据存储小操作配额(我想是50000个)。每次运行代码时,它将使用与模型对象数量相同的ops。

        5
  •  0
  •   orip    16 年前

    我还没有尝试过,这是一个彻底的资源消耗,但可能是迭代 .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
    
        6
  •  0
  •   dfichter    14 年前

    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
    
        7
  •  0
  •   TheAddonDepot    8 年前

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

    Datastore Statistics

        8
  •  0
  •   Jonathan B. Zac    6 年前

    正如@Dimu所指出的,谷歌定期计算的统计数据是一个不错的资源,因为不需要精确的计数,而且记录的百分比在任何一天都不会发生剧烈变化。

    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);
    }
    
        9
  •  -1
  •   Alyxandor    15 年前

    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()快得多时,您不会介意。。。