Using Dan McGrath's solution from https://stackoverflow.com/a/46801925/3073280 (to generate and query random indexes), I can certainly and definitely get one random document whenever the collection has one or more documents. Put another way, getting one random document is easy.
But, I have difficulty in implementing Dan McGrath's solution to get multiple random (non-duplicated) documents from a collection with certainty. I am referring to Dan McGrath’s rinse and repeat solution.
So, with reference to my pseudo code below, how to get exactly 3 documents* (non duplicated) at random when the collection has 5 documents?
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();
}
}
In brief, how to get exactly 3 documents* (non duplicated) at random when the collection has 5 documents?
*Note: In production, it would query for 80 documents at random out of thousands of documents from a collection; hence, please do not suggest reading the whole collection and shuffle the documents randomly then read the top 80 documents.