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.
Advertisement
Answer
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);