I'm updating a project from Firebase Version 8 to Version 9. I have conditional filtered queries that used to be structured as follows:
let query = db.collection("collectionName").orderBy("timestamp")
if (filters.selectedStaff) {
query = query.where("id", "==", filters.selectedStaff.id);
}
if (filters.selectedStudent) {
query = query.where("studentIDs", "array-contains", filters.selectedStudent.id);
}
Then the query gets executed in a hook that rerenders everytime the filters change. Using Version 8, this works perfectly.
In version 9, queries are now build to follow a format where each query is passed as a parameter to the query function. A normal query would look like:
query(collection(db, "collectionName"), where("id", "==", filters.selectedStaff.id), where("studentIDs", "array-contains", filters.selectedStudent.id), orderBy("timestamp"))
You can still store the where function as a variable, and pass that variable as a parameter to the query function:
let w1 = where("id", "==", filters.selectedStaff.id)
let w2 = where("studentIDs", "array-contains", filters.selectedStudent.id)
query(collection(db, "collectionName"), w1, w2, orderBy(timestamp))
But, the problem I haven't figured out how to solve, is how to make where clauses conditional. It doesn't seem like firebase allows a where clause value to be any. For example, making w1 default to:
let w1 = where("id", "==", "*")
If you try to make the operator also a variable and default to anything other than ==, like != :
let w1 = where("id", "!=", "")
Firebase forces you to set the primary sort by the field in the where clause which won't working if you're trying to sort by another field like I am (timestamp).
Ultimately, the workaround that works is to create a field in each document that has the same value, like a boolean true value and then set all your where clauses to be equal to that initially, and then dynamically change:
let w1 = where("randomField", "==", true)
let w2 = where("randomField", "==", true)
if(filters.selectedStaff){
w1 = where("id", "==", filters.selectedStaff.id)
}
if(filters.selectedStudent){
w2 = where("studentIDs", "array-contains", filters.selectedStudent.id)
}
query(collection(db, "collectionName"), w1, w2, orderBy(timestamp))
While this works, it feels like a really unnecessary workaround and I wanted to see if anyone know of a better way to accomplish the same outcome.
CodePudding user response:
You can keep using the existing logic, it's only the syntax that has changed. Try refactoring the code as shown below:
let q = query(collection("db", "collectionName"), orderBy("timestamp"));
if (filters.selectedStaff) {
q = query(q, where("id", "==", filters.selectedStaff.id));
}
if (filters.selectedStudent) {
q = query(q, where("studentIDs", "array-contains", filters.selectedStudent.id));
}
const snapshot = await getDocs(q);
Another approach would be to conditionally push those conditions in an array:
const conditions = [orderBy("timestamp")]
if (filters.selectedStaff) {
conditions.push(where("id", "==", filters.selectedStaff.id));
}
if (filters.selectedStudent) {
conditions.push(where("studentIDs", "array-contains", filters.selectedStudent.id));
}
const q = query(collection(db, "collectionName"), ...conditions);
// Do note the spread operator ^
const snapshot = await getDocs(q);