質問

カウントを行う 1 つの方法は次のようなものだと思います。

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

気に入らないのは、カウントが最大 1000 に制限され、クエリが遅くなる可能性があることです。回避策を持っている人はいますか?考えていることはあるのですが、すっきりしない気がします。GQL に実際の COUNT 関数があれば...

役に立ちましたか?

解決

+1エヒアの応答。

GAEでオブジェクトカウンターを取得する公式で祝福された方法は、シャードカウンターを構築することです。重く聞こえる名前にもかかわらず、これは非常に簡単です。

他のヒント

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データストアはそれをより明確にします。 Jehiahが示唆するように、計算されたカウントをエンティティに保存し、スケーラビリティが必要な場合はそれを参照する必要があります。

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

による GqlQuery.count() ドキュメンテーション, を設定できます。 limit 1000 より大きい数値にする:

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

人々が言っ​​ているように、シャード カウンターはこのような数値を追跡する正しい方法ですが、(私のように) ゲームの後半でこれを理解した場合は、実際のオブジェクトの数からカウンターを初期化する必要があります。しかし、これはデータストアの小規模オペレーションの無料割り当て (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

現在、エンティティカウントやその他のデータのクエリに使用できるデータストア統計があります。これらの値は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 = <*>quot;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()はインデックスの先頭から1つのレコードを返します。非常に高速です。

次に、オブジェクトの1000番目の制限を超えて取得する場合は、次のようにします。

MyObj.all().filter('num > ' , 2345).fetch(67)

アラルバルカンの痛烈なレビューを読んだときに、すでにこれを行っていました。 http://aralbalkan.com/1504。イライラしますが、慣れて、リレーショナルデータベースのcount()よりもはるかに高速であることに気づいたら、気にしません...

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top