私のアプリケーションにとって、Firebase のコレクションから複数のドキュメントをランダムに選択できることが重要です。
Firebase には、これを行うクエリを実装するための組み込みネイティブ関数 (私が知っている) がないため、最初に考えたのは、クエリ カーソルを使用してランダムな開始インデックスと終了インデックスを選択することでした。数値コレクション内のドキュメントの数。
このアプローチは機能しますが、各ドキュメントは毎回隣接するドキュメントとともに順番に提供されるため、限られた方法でのみ機能します。ただし、親コレクション内のインデックスによってドキュメントを選択できる場合は、実装することができます。ランダムなドキュメント クエリですが、問題は、これを行う方法、またはこれが可能であるかどうかを説明したドキュメントが見つからないことです。
これが私がやりたいことです。次の Firestore アーキテクチャを考えてみましょう:
リーリー次に、クライアント側 (私は Swift 環境にいます) で、これを行うクエリを書きたいと思います:
リーリー同様のことはできますか?あるいは、同様の方法でランダムなドキュメントを選択する他の方法はありますか?
###助けてください。
今後この問題に遭遇する人を助けるためにこれを投稿します。
自動 ID を使用する場合は、Dan McGrath の回答 .
のように、新しい自動 ID を生成し、最も近い自動 ID をクエリできます。私は最近、Firestore コレクションからランダムな引用符を取得する必要があるランダムな引用符 API を作成しました。
リーリーこれが私がこの問題を解決した方法です:
クエリのキーは次のとおりです:
リーリードキュメントが見つからない場合は、逆の操作で再度呼び出します。
###お役に立てれば!ランダムに生成されたインデックスと単純なクエリを使用して、Cloud Firestore のコレクションまたはコレクション グループからドキュメントをランダムに選択できます。
この回答は 4 つの部分に分かれており、各部分には異なるオプションがあります:
ランダムなインデックスを生成する方法
この回答の基礎は、昇順または降順で並べ替えたときにすべてのドキュメントがランダムに並べ替えられるインデックス フィールドを作成することです。これを作成するにはさまざまな方法があるので、最も簡単な方法から始めて 2 を見てみましょう。
バージョンを自動的に識別する
当社のクライアント ライブラリで提供されるランダムに生成された自動 ID を使用する場合、同じシステムを使用してドキュメントをランダムに選択できます。この場合、ランダムに並べられたインデックス が ドキュメント ID になります。
後のクエリ セクションで、生成するランダムな値は、新しい自動 ID (iOS、Android、Web) です。クエリされるフィールドは次のとおりです。
__name__
フィールド、および後述の「下限値」は空の文字列です。これはランダムなインデックスを生成する最も簡単な方法であり、言語やプラットフォームに関係なく機能します。デフォルトでは、ドキュメント名 (
__name__
) は昇順でのみインデックス付けされ、既存のドキュメントの名前を変更するには、ドキュメントを削除して再作成する必要があります。これらのいずれかが必要な場合でも、この方法を使用できます。この目的でドキュメント名をオーバーロードする代わりに、自動 ID をrandom
という名前の実際のフィールドとして保存するだけです。ランダムな整数バージョン
ドキュメントを作成するときは、まず制限された範囲内でランダムな整数を生成し、それを
random
という名前のフィールドに設定します。予想されるドキュメントの数に応じて、異なる境界範囲を使用してスペースを節約したり、競合のリスクを軽減したりできます (これにより、この手法の有効性が低下します)。考慮すべき点は異なるため、どの言語が必要か検討する必要があります。 Swift はシンプルですが、JavaScript には顕著な問題があります:
これにより、ランダムに並べ替えられたドキュメントのインデックスが作成されます。後のクエリ セクションで、生成するランダム値はこれらの値の別の値になり、後述する「下限値」は -1 になります。
ランダムインデックスをクエリする方法
ランダムなインデックスを作成したので、それをクエリする必要があります。以下では、ランダムな 1 つのドキュメントを選択するいくつかの簡単なバリエーションと、複数の 1 ドキュメントを選択するオプションを見ていきます。
これらすべてのオプションでは、ドキュメントの作成時に作成したインデックス値と同じ形式で新しいランダム値を生成する必要があります。これは、以下の変数
###囲む###random
で表されます。この値を使用して、インデックス上のランダムなポイントを見つけます。ランダムな値を取得したので、個々のドキュメントをクエリできます。
リーリー書類が返却されたかどうかを確認してください。そうでない場合は、ランダム インデックスの「低い値」を使用して再度クエリを実行します。たとえば、ランダムな整数を実行する場合、
lowValueは
0
:となります。 リーリー
ドキュメントが 1 つある限り、少なくとも 1 つのドキュメントが返されることが保証されます。両方向
ラップアラウンド方式は実装が簡単で、昇順インデックスのみを有効にしてストレージを最適化できます。欠点の 1 つは、価値が不当に保護される可能性があることです。たとえば、10K 内の最初の 3 つのドキュメント (A、B、C) がランダムなインデックス値 A:409496、B:436496、C:818992 を持つ場合、A と C が選択される確率は 1/10K 未満です。一方、A は近接から効果的に保護されており、確率は約 1/160K のみであるため、B が選択されます。
一方向にクエリを実行して値が見つからない場合に折り返すのではなく、
>=と
リーリーのどちらかをランダムに選択できるため、値が不当にマスクされる可能性が低くなります。インデックス ストレージを 2 倍にする代わりに、コストは半分になります。
一方向で結果が返されない場合は、他の方向に切り替えます:
複数のランダムなドキュメントを選択します
通常、複数のドキュメントを一度にランダムに選択する必要があります。必要なトレードオフに応じて、上記のテクニックを適応させる 2 つの異なる方法があります。
洗い流して繰り返します
この方法は非常に簡単です。毎回新しいランダムな整数を選択するなど、プロセスを繰り返すだけです。
この方法では、同じパターンが繰り返し表示されることを心配することなく、ランダムなドキュメントのシーケンスが得られます。
トレードオフは、各ドキュメントを提供するために個別のラウンドトリップが必要なため、次の方法よりも時間がかかることです。
###それを維持する###この方法では、必要な書類の制限数を増やすだけです。呼び出し内で
0..limitドキュメントを返す可能性があるため、これは少し複雑です。次に、同じ方法で不足しているドキュメントを取得する必要がありますが、制限は相違点のみに限定されます。ドキュメントの合計数が要求した数よりも多いことがわかっている場合は、2 回目の呼び出し (最初の呼び出しではない) で十分なドキュメントが取得されないというエッジ ケースを無視することで最適化できます。
このソリューションのトレードオフは、シーケンスが繰り返されることです。ドキュメントはランダムに並べ替えられますが、範囲が重複する場合は、前に見たのと同じパターンが表示されます。この懸念を軽減する方法があります。それについては、再シードに関する次のセクションで説明します。
この方法は、最良の場合は 1 回の呼び出しで、最悪の場合は 2 回の呼び出しですべてのドキュメントを要求するため、「リンスして繰り返す」よりも高速です。
一貫したランダム性を実現するための再シード
ドキュメント セットが静的な場合、このメソッドはドキュメントをランダムに提供しますが、各ドキュメントが返される確率も静的になります。一部の値は、取得元の最初のランダム値に応じて、不当に低い確率または高い確率を持つ可能性があるため、これは問題です。多くのユースケースではこれで問題ありませんが、場合によっては、長期的なランダム性を高めて、任意の 1 つのドキュメントが返される可能性をより均等にする必要がある場合があります。
挿入された文書は最終的に絡み合い、確率が徐々に変化します。削除された文書についても同様であることに注意してください。特定のドキュメント数に対して挿入/削除率が小さすぎる場合、この問題を解決するための戦略がいくつかあります。
複数のランダム
再シードについて心配する必要はありません。いつでもドキュメントごとに複数のランダムなインデックスを作成し、毎回そのうちの 1 つをランダムに選択できます。たとえば、フィールド
ここで、random.1、random.2、random.3 をランダムにクエリして、より大きなランダム性の分布を作成します。これは基本的に、再シードによる計算 (ドキュメントの書き込み) の増加を節約するために、増加したストレージ領域を使用します。random
をサブフィールド 1 ~ 3 を含むマップとします。 リーリーrandom
読み取り時の再シードフィールドのランダム値が再生成されます。これにより、ドキュメントがランダムなインデックスに移動されます。
if random(0,100) === 0) update;
)。