對於我的應用程式來說,能夠從 Firebase 的集合中隨機選擇多個文件至關重要。
由於Firebase 中沒有內建的本機函數(據我所知)來實現執行此操作的查詢,因此我的第一個想法是使用查詢遊標來選擇隨機的開始和結束索引,前提是我有數字集合中的文件數量。
這種方法可以工作,但只能以有限的方式工作,因為每個文檔每次都會與其相鄰文檔按順序提供;但是,如果我能夠通過其父集合中的索引選擇文檔,我可以實現隨機文件查詢,但問題是我找不到任何文件來描述如何執行此操作,或者即使可以執行此操作。
這是我想做的事情,請考慮以下 firestore 架構:
root/ posts/ docA docB docC docD
然後在我的客戶端(我在 Swift 環境中)我想編寫一個可以執行此操作的查詢:
db.collection("posts")[0, 1, 3] // would return: docA, docB, docD
我可以做一些類似的事情嗎?或者,是否有其他方法可以以類似的方式選擇隨機文件?
請幫忙。
發布此內容是為了幫助將來遇到此問題的任何人。
如果您使用自動 ID,則可以產生新的自動 ID 並查詢最接近的自動 ID,例如Dan McGrath 的答案.
我最近建立了一個隨機報價 api,需要從 firestore 集合中取得隨機報價。
這就是我解決這個問題的方法:
查詢的關鍵是這樣的:
如果沒有找到文檔,則以相反的操作再次呼叫。
希望這會有所幫助!
使用隨機產生的索引和簡單查詢,您可以從 Cloud Firestore 中的集合或集合群組中隨機選擇文件。
這個答案分成 4 個部分,每個部分都有不同的選項:
如何產生隨機索引
這個答案的基礎是創建一個索引字段,當按升序或降序排序時,會導致所有文件隨機排序。有多種不同的方法來創建它,所以讓我們看一下 2,從最容易獲得的方法開始。
自動辨識版本
如果您使用我們的客戶端庫中提供的隨機產生的自動 ID,您可以使用相同系統隨機選擇文件。在這種情況下,隨機排序的索引是文件ID。
稍後在我們的查詢部分中,您產生的隨機值是一個新的自動ID (iOS#,Android,Web),您查詢的字段是
__name__
字段,而'後面提到的「low value」就是一個空字串。這是迄今為止生成隨機索引的最簡單方法,無論語言和平台如何,都可以工作。預設情況下,文件名稱 (
__name__
) 僅按升序索引,並且除了刪除和重新建立之外,您也無法重新命名現有文件。如果您需要其中任何一個,您仍然可以使用此方法,只需將自動 ID 儲存為名為random
的實際字段,而不是為此目的重載文件名稱。隨機整數版本
當你寫文件時,先產生一個有界範圍內的隨機整數,並將其設定為名為
random
的欄位。根據您期望的文件數量,您可以使用不同的有界範圍來節省空間或降低衝突風險(這會降低此技術的有效性)。您應該考慮您需要哪種語言,因為會有不同的考慮因素。雖然 Swift 很簡單,但 JavaScript 卻有一個值得注意的問題:
這將建立一個索引,其中的文件隨機排序。稍後在我們的查詢部分中,您產生的隨機值將是這些值中的另一個,而後面提到的「低值」將是 -1。
如何查詢隨機索引
現在您已經有了一個隨機索引,您將需要查詢它。下面我們來看一些選擇 1 個隨機文件的簡單變體,以及選擇多個 1 個文件的選項。
對於所有這些選項,您需要產生一個新的隨機值,其形式與您在編寫文件時建立的索引值相同,並由下面的變數
random
表示。我們將使用該值在索引上尋找隨機點。環繞式
現在您有了隨機值,您可以查詢單一文件:
檢查是否已傳回文件。如果沒有,請再次查詢,但使用隨機索引的「低值」。例如,如果您執行隨機整數,則
lowValue
為0
:只要您有一個文檔,就保證您至少會返回 1 個文檔。
雙向
環繞方法實作起來很簡單,並且允許您在僅啟用升序索引的情況下最佳化儲存。缺點之一是價值觀可能受到不公平的保護。例如,如果10K 中的前3 個文檔(A、B、C)具有隨機索引值A:409496、B:436496、C:818992,則A 和C 被選擇的機會小於1/10K,而B 因A的接近而被有效屏蔽,並且只有大約1/160K 的機會。
您可以在
>=
和之間隨機選擇,而不是單向查詢並在未找到值時迴繞,這會降低機率不公平屏蔽的值減半,代價是索引儲存加倍。
如果一個方向沒有回傳結果,則切換到另一個方向:
選擇多個隨機文件
通常,您需要一次選擇多個隨機文件。根據您想要的權衡,有兩種不同的方法可以調整上述技術。
沖洗並重複
這個方法很簡單。只需重複該過程,包括每次選擇新的隨機整數。
此方法將為您提供隨機的文件序列,而不必擔心重複看到相同的模式。
代價是它會比下一個方法慢,因為它需要為每個文件單獨往返服務。
繼續保持
在此方法中,只需增加所需文件的限制數量即可。這有點複雜,因為您可能會在呼叫中傳回
0..limit
文件。然後,您需要以相同的方式獲取丟失的文檔,但限制減少到僅存在差異。如果您知道文件總數比您要求的數量多,則可以透過忽略在第二次呼叫(但不是第一次)時永遠無法取回足夠文件的邊緣情況來進行最佳化。此解決方案的權衡是重複序列。雖然文件是隨機排序的,但如果最終出現重疊範圍,您將看到與之前看到的相同模式。有一些方法可以減輕這種擔憂,我們將在下一節有關重新播種的內容中討論。
此方法比「沖洗並重複」更快,因為您將在最佳情況下一次呼叫或最壞情況下兩次呼叫中請求所有文件。
重新播種以獲得持續的隨機性
雖然如果文檔集是靜態的,則此方法會隨機為您提供文檔,但返回每個文檔的機率也將是靜態的。這是一個問題,因為根據它們獲得的初始隨機值,某些值可能具有不公平的低或高機率。在許多用例中,這很好,但在某些用例中,您可能希望增加長期隨機性,以便有更均勻的機會返回任何 1 個文件。
請注意,插入的文件最終會交織在中間,逐漸改變機率,刪除文件也是如此。如果給定文件數量,插入/刪除率太小,有一些策略可以解決這個問題。
多隨機
您不必擔心重新播種,您始終可以為每個文件建立多個隨機索引,然後每次隨機選擇其中一個索引。例如,讓欄位
random
成為包含子欄位 1 到 3 的對應:現在您將隨機查詢 random.1、random.2、random.3,從而創建更大的隨機性分佈。這本質上是用增加的儲存空間來節省因重新播種而增加的計算(文件寫入)。
寫入時重新設定種子
每次更新文件時,都會重新產生
random
欄位的隨機值。這將在隨機索引中移動文件。讀取時重新播種
如果產生的隨機值不是均勻分佈的(它們是隨機的,因此這是預期的),則可能會在不適當的時間內選擇相同文件。透過在讀取隨機選擇的文檔後使用新的隨機值更新該文檔,可以輕鬆解決此問題。
由於寫入成本更高並且可能成為熱點,因此您可以選擇僅在讀取時間的子集時進行更新(例如,
if random(0,100) === 0) update;
)。