1

i have read a documents about random java, swift but JS random document firestore i can't find and I see this problem not yet for JS?

swift

postsRef.whereField("random", isGreaterThanOrEqualTo: random)
                   .order(by: "random")
                   .limit(to: 1)

JS

postsRef.where("random", ">=", random)
                   .order("random")
                   .limit(1)

is this correct?

Dharmaraj
  • 47,845
  • 8
  • 52
  • 84

1 Answers1

2

Typescript (remove the types if you use Javascript) version for getting random documents using random integers (and wrap-around). This answer uses Cloud function for testing but essentially you just need to copy the getDocuments function.

export const getRandomDoc = functions.https.onRequest(async (req, res): Promise<any> => {
  try {
    const randomDocs = await getDocuments(2)
    return res.send(randomDocs)
  } catch (err) {
    return res.send(err)
  }
});

const getDocuments = async (count: number): Promise<Array<FirebaseFirestore.DocumentData>> => {
  const randomNum = Math.floor(Math.random() * 10000)
  const snapshot = await admin.firestore().collection("col").where("rInt", ">=", randomNum).limit(count).get()
  if (snapshot.empty) {
    return getDocuments(count)
  }
  return snapshot.docs.map(d => d.data())
}

Whenever you add a new document to that collection, add the rInt field along with it which is an integer between 0 to 10000 (both inclusive). You pass the number of documents you need in the getDoucments function. It will fetch N consecutive matched docs though as this uses limit() method.

In the query we look for documents where rInt is greater than or equal to that random number generated using Math.random() and limit the results to count parameter passed in the function. If the snapshot is empty we retry the function. (It'll be worth to add a logic which makes this function repeat only N number of time else recursion will take it's time).


Using .limit() as in the function above will end up returning N documents in a row. By default, docs will be ordered by their document ID unless you specify any particular field using orderBy method. Instead making a separate request for each doc will increase the randomicity.

/**
 * @param {number} count - number of documents to retrieve
 * @param {number} loopNum - number of times the loop is being repeated. Defaults to 0 
 * @param {Array<any>} curDocs - array of documents matched so far. Defaults to an empty array
 * @returns 
 */ 
const getDocuments = async (count: number, loopNum: number, curDocs: Array<any>): Promise<Array<FirebaseFirestore.DocumentData>> => {
  // Creating an array of requests to get documents
  const requests = []
  for (let i = 0; i < count; i++) {
    // New random number for each request
    const randomNum = Math.floor(Math.random() * 10000)
    console.log(`Random Num: ${randomNum}`);
    requests.push(admin.firestore().collection("col").where("rInt", ">=", randomNum).limit(1).get())
    // limit is set to 1 so each request will return 1 document only
  }
  
  // Using Promise.all() to run all the promises
  const snapshots = await Promise.all(requests)
  
  // Removing empty snapshots
  const filteredSnapshots = snapshots.filter(d => !d.empty)

  // Creating an array of doc data
  const matchedDocs = filteredSnapshots.map(doc => doc.docs[0].data())
  
  // If documents received are less than requested,
  // repeat the function
  if (matchedDocs.length !== count) {
    // If the function is repeated 5 times,
    // return with whatever has matched so far
    if (loopNum + 1 === 5) {
      console.log("Returning CurDocs")
      return curDocs
    }
    return getDocuments(count, loopNum + 1, [...curDocs, ...matchedDocs])
  }
  
  // Return if you get requested num of docs in first go
  return snapshots.map(d => d.docs[0].data())
}

// Calling the function
getDocuments(5, 0, [])

Do note that you can use in operator if you are requesting less than or equal to 10 documents. The in operator combines up to 10 equality (==) clauses on the same field with a logical OR.


For original version (swift) by @Dan McGrath, check this answer.

Dharmaraj
  • 47,845
  • 8
  • 52
  • 84