web-dev-qa-db-ja.com

GQLで結果をカウントする最良の方法は何ですか?

カウントを行う1つの方法は次のようになります。

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

私が気に入らないのは、カウントが最大1000に制限され、クエリがおそらく遅くなることです。回避策を持っている人はいますか?私はそれを念頭に置いていますが、それはきれいに感じません。 GQLだけが本当のCOUNT関数を持っていたら...

29
barneytron

エヒアの反応に+1。

GAEでオブジェクトカウンターを取得するための公式で祝福された方法は、 シャードカウンター をビルドすることです。非常に響きのある名前にもかかわらず、これはかなり簡単です。

17
zgoda

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
20
Jehiah

すべてのデータベースのカウント関数は低速です(例:O(n))-GAEデータストアはそれをより明確にします。Jehiahが示唆しているように、計算されたカウントをエンティティに格納して参照する必要がありますスケーラビリティが必要な場合は、それに。

これはAppEngineに固有のものではありません。他のデータベースは、リクエストごとに数万のレコードをカウントしようとし、ページのレンダリング時間が指数関数的に増加し始めるまで、それをより適切に非表示にします...

7
Nick Johnson

GqlQuery.count()ドキュメント によると、limitを1000より大きい数値に設定できます。

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

シャードカウンターは、人々が言っ​​ているように、このような数を追跡する正しい方法ですが、ゲームの後半(私のように)でこれを理解する場合は、実際のオブジェクト数からカウンターを初期化する必要があります。しかし、これはDatastore Small Operationsの無料クォータ(50,000だと思います)を使い切るための優れた方法です。コードを実行するたびに、モデルオブジェクトと同じ数の操作が使用されます。

2
rescdsk

これで、エンティティ数やその他のデータのクエリに使用できるデータストア統計ができました。これらの値は24〜48時間に1回更新されるため、常に最新の変更を反映しているとは限りません。詳細については、ドキュメント(以下のリンクを参照)を確認してください。

データストア統計

0
Dimu Designs

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
0
dfichter

私はそれを試していません、そしてこれは完全なリソースの浪費です、しかしおそらく.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
0
orip

@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);
}
0
Jonathan B.