Firebase версии 9 с использованием нескольких условных предложений where

# #javascript #firebase #google-cloud-firestore

Вопрос:

Я обновляю проект с Firebase версии 8 до версии 9. У меня есть условно отфильтрованные запросы, которые раньше были структурированы следующим образом:

 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);
    }
 

Затем запрос выполняется в крючке, который пересылается каждый раз, когда меняются фильтры. Используя версию 8, это работает идеально.

В версии 9 запросы теперь строятся в соответствии с форматом, в котором каждый запрос передается в качестве параметра функции запроса. Обычный запрос будет выглядеть так:

 query(collection(db, "collectionName"), where("id", "==", filters.selectedStaff.id), where("studentIDs", "array-contains", filters.selectedStudent.id), orderBy("timestamp"))
 

Вы все еще можете сохранить функцию where в качестве переменной и передать эту переменную в качестве параметра функции запроса:

 let w1 = where("id", "==", filters.selectedStaff.id)
let w2 = where("studentIDs", "array-contains", filters.selectedStudent.id)
query(collection(db, "collectionName"), w1, w2, orderBy(timestamp))
 

Но проблема, которую я не понял, как решить, заключается в том, как сделать условия where условными. Не похоже, что firebase допускает, чтобы значение предложения where было любым. Например, если значение w1 по умолчанию равно:

 let w1 = where("id", "==", "*")
 

Если вы попытаетесь сделать оператор также переменной и по умолчанию использовать что-либо, кроме==, например != :

 let w1 = where("id", "!=", "")
 

Firebase заставляет вас установить первичную сортировку по полю в предложении where, которое не будет работать, если вы пытаетесь выполнить сортировку по другому полю, как я (метка времени).

В конечном счете, обходной путь, который работает, состоит в том, чтобы создать поле в каждом документе, имеющее одинаковое значение, например логическое значение true, а затем установить, чтобы все ваши предложения where изначально были равны этому значению, а затем динамически изменялись:

 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))
 

Хотя это работает, мне кажется, что это действительно ненужный обходной путь, и я хотел посмотреть, знает ли кто-нибудь лучший способ добиться того же результата.

Ответ №1:

Вы можете продолжать использовать существующую логику, изменился только синтаксис. Попробуйте выполнить рефакторинг кода, как показано ниже:

 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);
 

Другой подход состоял бы в том, чтобы условно поместить эти условия в массив:

 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);
 

Комментарии:

1. Оба работают замечательно, большое вам спасибо за помощь. Условное включение предложений в запрос также является действительно элегантным и универсальным решением.