2009-07-09 7 views
9

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

私は現在、データストアに入れたすべての種類に乱数を割り当てています。私はランダムなレコードを照会するときに別の乱数を生成し、レコードを検索します> rand ORDER BY asc LIMIT 1.

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

+0

は、私はあなたがそれを固定し得るのを助けるためにそれを主演することができ、このために問題を作成しました: https://code.google.com/p/googleappengine/issues/detail?id=9044 –

答えて

5

"Under the hood"では、単一の検索クエリ呼び出しでは、いくつかのインデックスから連続した行のセットしか返せません。このため、!=の使用を含むいくつかのGQLクエリは、複数のデータストア呼び出しに拡張されます。

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

QED。

おそらく、エンティティを格納するためにmemcacheを使用し、それらのNを取得するコストを削減できます。または、「ランダム」選択がインデックス内で接近していても構わない場合は、ランダムに選択した1つのクエリのブロックを100個選択し、その中からランダムにNを選択します。あなたは既に無作為化されたフィールドを持っているので、N個のアイテムが関連していることは、外部からはすぐには分かりません。少なくとも、サンプルをたくさん見て、アイテムAとZがランダム化されたインデックスで100以上離れているので、同じグループに表示されないことに注意してください。また、パフォーマンスが許せば、エンティティを時折再ランダム化することができます。

+0

感謝を - 私は本当に無作為化された結果が必要なので、私は複数のデータストアコールを使用しなければならないと思います。私はNを最小限に抑えようと思っています。 – aloo

+0

これは当てはまりません。 [バッチ処理](https://developers.google.com/appengine/docs/python/datastore/entities?hl=en#Batch_Operations)と['IN'](https://developers.google.com/ appengine/docs/python/datastore/queries#Property_Filters)クエリ演算子は、連続していないエンティティを返すことができます。 – ryan

+0

@ryan: '!='と同じです。それらと「IN」の両方が、限られた数のサブクエリとして実装されている。バッチ操作は本当に問題とは関係ありませんが、特定の操作は、どのインデックスでも連続していないエンティティで動作することは事実です。ただ検索しません。 –

3

唯一の方法のように見えるのは、各エンティティの特別なプロパティにランダムな整数値を格納し、それに照会することです。これは、自動的に初期化されたプロパティを追加するだけで、非常に自動的に行うことができます。データストアは、すでに入力されている場合

残念ながら、これは、一度すべてのエンティティの処理が必要になります。それは奇妙だ

を、私は知っています。

+0

これは素晴らしいアプローチであり、読み込みではなく書き込みを行うNoSQLモデルに合っていると思います。もちろん、これは完全にランダムではありません。常にN個のシーケンシャルエントリを取得しておけば、同じレコードを隣に表示することがあります。しかし、それはOPのために十分ランダムであるかもしれません。 (異なる乱数を使って何百ものプロパティを作成し、そこから描画するインデックスを回転させることもできます) – npdoty

4

どのようなトレードオフをお探しですか?これらのエンティティの挿入時にパフォーマンスが低下することを覚えている場合は、N個のエンティティをすばやく取得するソリューションを作成できます。ここで

は、あなたがする必要があるものです:

あなたのエンティティを挿入すると、キーを指定します。エンティティに順番にキーを1から始まり、そこから上に移動したいとします。 (これは、アプリケーションエンジンにautoincrement()がないため、他のエンティティで最後に使用したIDを追跡する必要があるため、IdGeneratorと呼んでください)

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

この方法は、いくつかの厄介な詳細を扱う必要はない:あなたは(数秒以上)その場でこれらの項目の多くを挿入する場合

  1. あなたIdGeneratorがボトルネックになることがあり、これを必要としますいくつかの種類のIdGenerator実装を破棄しました。このデータがすべてプリロードされている場合、または大量ではない場合は、簡単に使用できます。
  2. idを削除したり、どこかでput()が失敗したりしたため、実際にはIdに関連付けられているエンティティが実際には存在しないことがあります。これが起こった場合は、別のランダムなエンティティを取得する必要があります。 (あなたが空想を得て、この確率を減らしたい場合は、IdGeneratorがこの穴を埋めるために再利用できるようにすることができます)

このようにNアイテムを追加したり削除したりする頻度と、パフォーマンスの向上のために少し複雑になるかどうかを判断します。

+1

App Engineに組み込まれているIDの番号付けを使用して多かれ少なかれ実装することができます。あなたはランダムに一様にいくつかを選ぶことができます。いくつかは存在しないので、新しい乱数などで再試行します。あなたのIDスペースが密な場合、これは正常に動作します。 –

+0

sweet。私は1から始まり、そこから1で1に上がるためにナンバリングに頼ることができるのか分からなかった。 –

+0

ブロックすることはできませんが、ブロック単位で割り当てられます。ブロックが使用される限り、再試行は管理可能なほど小さくなければなりません。 –

0

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

キーでソートされた「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) 

は私にとって、これはやや複雑であることに注意してください、と私はまだ私がずつオフまたはオフによって-Xのエラーを持っていけないことconvicedありませんよ。

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

1

私はSteveの答えに同意します.1つのクエリでN個のランダムな行を検索する方法はありません。

ただし、単一のエンティティを取得する方法であっても、返される結果の可算性が均等になるように通常は機能しません。与えられたエンティティを返す確率は、ランダムに割り当てられた数と次に高い乱数のギャップに依存します。例えば。乱数1,2および10が割り当てられている場合(3〜9のいずれもない場合)、アルゴリズムは「1」よりも8倍多く「2」を返します。

私はこれをやや高価な方法で修正しました。もし誰かが興味があれば、私は共有することを嬉しく思っています

-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. 
+0

データストアに100万のエンティティがある場合は、データストアからすべてのキーをロードする - 悪いように見える... – aloo

+0

@aloo非常に多くのインスタンスがある場合は、データストアとmemcacheでそれらの合計数を追跡することができます。次に、numberの範囲で 'random.choice'を実行します0〜総数の間で指定します。そして、あなたが生成したインデックスを持つキーを反復するだけです。または、limitとoffsetだけを使用してください。 –

関連する問題