9

特定の種類の N 個のランダム レコードを返す GQL クエリを作成しようとしています。私の現在の実装は機能しますが、データストアへの N 回の呼び出しが必要です。できればデータストアへの呼び出しは1回にしたいです。

現在、データストアに入れるすべての種類に乱数を割り当てています。ランダム レコードをクエリすると、別の乱数を生成し、レコード > rand ORDER BY asc LIMIT 1 をクエリします。

これは機能しますが、1 つのレコードしか返されないため、N 個のクエリを実行する必要があります。この 1 つのクエリを作成する方法についてのアイデアはありますか? ありがとう。

4

6 に答える 6

5

どのようなトレードオフを探していますか?これらのエンティティを挿入する際のパフォーマンスへの小さな影響に我慢したい場合は、それらのN個を非常に迅速に取得するためのソリューションを作成できます。

これがあなたがする必要があることです:

エンティティを挿入するときに、キーを指定します。1から始まり、そこから上に向かって、エンティティに順番にキーを与えたいと考えています。(アプリエンジンにはautoincrement()がないため、これには多少の労力が必要です。そのため、他のエンティティで最後に使用したIDを追跡する必要があります。これをIdGeneratorと呼びましょう)

これで、N個のランダムエンティティが必要になったときに、1から最後に生成したIDまでの間にN個の乱数を生成します(IdGeneratorはこれを認識します)。次に、Nキーを使用してキーによるバッチ取得を実行できます。これは、データストアへの1回のトリップのみを必要とし、キー取得は一般にクエリよりも高速であるため、クエリよりも高速です。

この方法では、いくつかの厄介な詳細を処理する必要があります。

  1. これらのアイテムをオンザフライで(数秒以上)挿入する場合、IdGeneratorがボトルネックになる可能性があります。これには、ある種のシャーディングされたIdGeneratorの実装が必要になります。このすべてのデータがプリロードされているか、大量でない場合は、簡単に実行できます。
  2. 一部のIDは、削除したか、put()がどこかで失敗したために、実際にはエンティティが関連付けられていないことに気付く場合があります。これが発生した場合は、別のランダムなエンティティを取得する必要があります。(空想を得て、この可能性を減らしたい場合は、このIDをIdGeneratorで使用できるようにして、「穴を埋める」ために再利用できます)

したがって、問題は、これらのN個のアイテムが必要な速度と、それらを追加および削除する頻度、およびパフォーマンスを向上させる価値があるかどうかということです。

于 2009-07-09T18:49:10.803 に答える
5

「内部」では、1回の検索クエリ呼び出しで、一部のインデックスから連続する行のセットのみを返すことができます。これが、!=の使用を含む一部のGQLクエリが、複数のデータストア呼び出しに拡張される理由です。

N個の独立した均一ランダム選択は、(一般に)どのインデックスでも連続していません。

QED。

おそらくmemcacheを使用してエンティティを保存し、それらのN個を取得するコストを削減できます。または、インデックス内で「ランダムな」選択が近くにあることを気にしない場合は、1つのクエリでランダムに選択された(たとえば)100のブロックを選択し、それらからランダムにNを選択します。すでにランダム化されたフィールドがあるため、N個のアイテムが関連していることは部外者にはすぐにはわかりません。少なくとも、彼らが多くのサンプルを見て、アイテムAとZがランダム化されたインデックスで100以上離れているため、同じグループに表示されないことに気付くまでは。また、パフォーマンスが許せば、エンティティを随時再ランダム化できます。

于 2009-07-09T16:59:10.743 に答える
3

唯一の方法は、各エンティティの特別なプロパティにランダムな整数値を格納し、それをクエリすることであるように見えます。自動的に初期化されたプロパティを追加するだけで、これを完全に自動的に行うことができます。

残念ながら、データストアがすでに入力されている場合は、すべてのエンティティを1回処理する必要があります。

それは奇妙だ、私は知っている。

于 2009-07-09T17:05:13.773 に答える
0

私はちょうど同じ問題を抱えていました。データストア内の既存のエントリに ID を割り当てないことに決め、これを行いました。これは、シャード カウンターからの totalcount が既にあるためです。

これは、 keyでソートされた "totalcount" エントリから "count" エントリを選択します。

    # select $count from the complete set
    numberlist = random.sample(range(0,totalcount),count)
    numberlist.sort()

    pagesize=1000

    #initbuckets
    buckets = [ [] for i in xrange(int(max(numberlist)/pagesize)+1) ]
    for k in numberlist:
        thisb = int(k/pagesize)
        buckets[thisb].append(k-(thisb*pagesize))
    logging.debug("Numbers: %s. Buckets %s",numberlist,buckets)

    #page through results.

    result = []
    baseq =  db.Query(MyEntries,keys_only=True).order("__key__")
    for b,l in enumerate(buckets):
        if len(l) > 0: 
            result += [ wq.fetch(limit=1,offset=e)[0] for e in l ]

        if b < len(buckets)-1: # not the last bucket
            lastkey  = wq.fetch(1,pagesize-1)[0]
            wq = baseq.filter("__key__ >",lastkey)

これは私にとってはやや複雑であり、off-by-one または off-by-x エラーがないことをまだ確信していないことに注意してください。

また、count が totalcount に近い場合、これは非常に高くつく可能性があることに注意してください。また、数百万行の場合、appengine の時間境界内では実行できない可能性があることに注意してください。

于 2009-09-13T11:52:15.243 に答える
-1

私が正しく理解している場合は、N個のランダムインスタンスを取得する必要があります。

それは簡単です。キーだけでクエリを実行するだけです。そして、キーのリスト結果に対してrandom.choiceをN回実行します。次に、キーをフェッチして結果を取得します。

keys = MyModel.all(keys_only=True)

n = 5 # 5 random instance

all_keys = list(keys)
result_keys = []

for _ in range(0,n) 
    key = random.choice(all_keys)
    all_keys.remove(key)
    result_keys.append(key)

# result_keys now contain 5 random keys.
于 2012-10-03T09:19:14.873 に答える