首页 > 解决方案 > Firebase 版本 9 使用多个条件 where 子句

问题描述

我正在将一个项目从 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 子句中的字段设置主要排序,如果您尝试按像我这样的另一个字段(时间戳)进行排序,这将不起作用。

最终,可行的解决方法是在每个文档中创建一个具有相同值的字段,例如布尔真值,然后将所有 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))

虽然这可行,但感觉确实是一种不必要的解决方法,我想看看是否有人知道实现相同结果的更好方法。

标签: javascriptfirebasegoogle-cloud-firestore

解决方案


您可以继续使用现有的逻辑,只是语法发生了变化。尝试重构代码,如下所示:

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

推荐阅读