Firestore - 如何确定地从集合中获取多个随机(非重复的,特定数量的)文档?

Firestore - How to get multiple random (non duplicated, specific number of) documents from a collection with certainty?

使用 中 Dan McGrath 的解决方案(生成和查询随机索引),只要集合中有一个或多个文档,我当然可以得到一个随机文档。换句话说,获取一个随机文档很容易。

但是,我很难实施 Dan McGrath 的解决方案来确定地从集合中获取多个随机(非重复)文档。我指的是 Dan McGrath 的冲洗和重复解决方案。

那么,参考我下面的伪代码,当集合有 5 个文档时,如何随机得到 3 个文档*(不重复)?

import { Component, OnInit } from '@angular/core';

import { AngularFirestore, AngularFirestoreCollection } from '@angular/fire/firestore';
import { throwError, interval, of }  from 'rxjs';
import { retry, switchMap, mergeMap, take } from 'rxjs/operators';


@Component({
  selector: 'app-firestore',
  templateUrl: './firestore.component.html',
  styleUrls: ['./firestore.component.css']
})
export class FirestoreComponent implements OnInit {

  constructor(private afs: AngularFirestore) {

  } 

  // This is the sample data structure in Firestore with only 5 documents in the collection
  // -Collection name is testRandom
  // --Document Auto ID
  // --- { random: 1 }
  // --Document Auto ID
  // --- { random: 2 }
  // --Document Auto ID
  // --- { random: 4, remark: 'intentionally skip 3' }
  // --Document Auto ID
  // --- { random: 5 }
  // --Document Auto ID
  // --- { random: 7, remark: 'intentionally skip 6' }

  getRandomIntInclusive(min, max) {
    min = Math.ceil(min);
    max = Math.floor(max);
    return Math.floor(Math.random() * (max - min + 1) + min); //The maximum is inclusive and the minimum is inclusive
  }

  /*
  * This will return one document at random but this will not guarantee return if the random number is 3 or 6
  * */
  selectSingleRandomDocument_byQueryingRandomIndexes() {
    return this.afs.collection<Item>('testRandom',
        ref => ref.where('random', '==', this.getRandomIntInclusive(1,7)).limit(1)
    )
      .valueChanges()
      .pipe(
        mergeMap(doc => {
          if (doc.length > 0) {
            return of(doc);
          } else {
            return throwError('no random document queried because the random number is either 3 or 6, hence throw error so it will retry')
          }
        }),
      )
  }

  /*
  * This will query for 3 documents but it will not guarantee return 3 documents if error thrown
  * */
  rinseAndRepeat() {
    interval(2000)
      .pipe(
        take(3),
        switchMap(val => this.selectSingleRandomDocument_byQueryingRandomIndexes()
            .pipe(retry(5)) // return 5 times if get throwError from singleRandomDocument
        )
      )
      .subscribe();
  }

}

简而言之,当集合有5个文档时,如何随机得到3个文档*(不重复)?

*注意:在生产中,它会从一个集合中的数千个文档中随机查询 80 个文档;因此,请不要建议阅读整个集合并随机打乱文档然后阅读前 80 个文档。

看起来就像在查询之前检查随机整数以确保它们是唯一的一样简单。在 React 中可能有更优雅的方法来实现这一点,但这似乎可行。

getSetOfRandomInts(num) {
   const set = new Set();
   while(set.size < num) { // retry if we get dups
     set.add(this.getRandomIntInclusive(1,7));
   }
   return set;
}

selectSingleDocumentByRandomIndex(randomNumber) {
    return this.afs.collection<Item>('testRandom',
        ref => ref.where('random', '==', randomNumber).limit(1)
    )
      .valueChanges()
      .pipe(
        mergeMap(doc => {
          if (doc.length > 0) {
            return of(doc);
          } else {
            return throwError('no random document queried because the random number is either 3 or 6, hence throw error so it will retry')
          }
        }),
      )
  }

rinseAndRepeat() {
    interval(2000)
      .pipe(
        () => this.getSetOfRandomInts(3),
        switchMap(randomInt => this.selectSingleDocumentByRandomIndex(randomInt)
            .pipe(retry(5))
        )
      )
      .subscribe();
  }