如何在 Cloud Firestore 中使用逻辑或执行复合查询?
How to perform compound queries with logical OR in Cloud Firestore?
来自the docs:
You can also chain multiple where() methods to create more specific queries (logical AND).
如何执行 OR
查询?
示例:
- 给我字段
status
为 open
或 upcoming
的所有文档
- 给我所有文档,其中字段
status == open
或 createdAt <= <somedatetime>
OR
不受支持,因为服务器很难对其进行扩展(需要保持状态以进行重复数据删除)。解决方法是发出 2 个查询,每个条件一个,并在客户端上进行重复数据删除。
编辑(2019 年 11 月):
Cloud Firestore 现在支持 IN
查询,这是一种有限类型的 OR
查询。
对于上面的例子你可以这样做:
// Get all documents in 'foo' where status is open or upcmoming
db.collection('foo').where('status','in',['open','upcoming']).get()
然而,仍然不可能执行涉及多个字段的一般 OR
条件。
您可以使用 rxjs 合并运算符绑定两个 Observable。
这里有一个例子。
import { Observable } from 'rxjs/Observable';
import 'rxjs/add/observable/merge';
...
getCombinatedStatus(): Observable<any> {
return Observable.merge(this.db.collection('foo', ref => ref.where('status','==','open')).valueChanges(),
this.db.collection('foo', ref => ref.where('status','==','upcoming')).valueChanges());
}
然后就可以使用上面的方法订阅新的Observable更新了:
getCombinatedStatus.subscribe(results => console.log(results);
希望对你有所帮助,来自智利的问候!!
建议也给状态赋值。
例如。
{ name: "a", statusValue = 10, status = 'open' }
{ name: "b", statusValue = 20, status = 'upcoming'}
{ name: "c", statusValue = 30, status = 'close'}
你可以通过ref.where('statusValue', '<=', 20)
查询,然后'a'
和'b'
都会找到。
这可以节省您的查询成本和性能。
顺便说一句,它并不能解决所有的问题。
我没有 "status" 字段,但有状态相关字段,根据请求将它们更新为 true 或 false,例如
{ name: "a", status_open: true, status_upcoming: false, status_closed: false}
但是,请检查 Firebase Cloud Functions。你可以有一个监听状态变化的函数,更新状态相关的属性,比如
{ name: "a", status: "open", status_open: true, status_upcoming: false, status_closed: false}
一个或另一个,您的查询可能只是
...where('status_open','==',true)...
希望对您有所帮助。
我们刚才遇到了同样的问题,幸运的是我们唯一可能的值是 A,B,C,D (4) 所以我们必须查询 A||B, A||C, A ||B||C、D 等
就像几个月前一样,firebase 支持一个新查询 array-contains
所以我们要做的是创建一个数组,然后将 OR 值预处理到数组中
if (a) {
array addObject:@"a"
}
if (b) {
array addObject:@"b"
}
if (a||b) {
array addObject:@"a||b"
}
etc
我们对所有 4!
值或无论有多少组合都这样做。
然后我们可以简单地检查查询 [document arrayContains:@"a||c"]
或我们需要的任何类型的条件。
因此,如果某些内容仅符合我们 4 个条件(A、B、C、D)中的条件 A
,则其数组将包含以下文字字符串:@["A", "A||B", "A||C", "A||D", "A||B||C", "A||B||D", "A||C||D", "A||B||C||D"]
然后对于这些 OR
组合中的任何一个,我们可以只搜索 array-contains
我们可能想要的任何内容(例如 "A||C")
注意:只有在您有几个可能的值要比较 OR 时,这才是合理的方法。
关于 的更多信息,因为它是 firebase 文档的新手
或者不支持
但是如果你需要,你可以在你的代码中做到这一点
Ex:如果我想要查询产品,其中(Size Equal Xl OR XXL : AND Gender is Male)
productsCollectionRef
//1* first get query where can firestore handle it
.whereEqualTo("gender", "Male")
.addSnapshotListener((queryDocumentSnapshots, e) -> {
if (queryDocumentSnapshots == null)
return;
List<Product> productList = new ArrayList<>();
for (DocumentSnapshot snapshot : queryDocumentSnapshots.getDocuments()) {
Product product = snapshot.toObject(Product.class);
//2* then check your query OR Condition because firestore just support AND Condition
if (product.getSize().equals("XL") || product.getSize().equals("XXL"))
productList.add(product);
}
liveData.setValue(productList);
});
借助 recent addition of IN queries,Firestore 支持 "up to 10 equality clauses on the same field with a logical OR"
(1) 的可能解决方案是:
documents.where('status', 'in', ['open', 'upcoming']);
见Firebase Guides: Query Operators | in
and array-contains-any
对于 Flutter dart 语言,使用这个:
db.collection("projects").where("status", whereIn: ["public", "unlisted", "secret"]);
这并不能解决所有情况,但对于“枚举”字段,您可以通过为每个枚举值创建一个单独的布尔字段,然后为每个添加一个 where("enum_<value>", "==", false)
来模拟“或”查询不是您想要的“OR”子句的一部分的值。
例如,考虑您的第一个查询:
- Give me all documents where the field status is open OR upcoming
您可以通过将 status: string
字段拆分为多个布尔字段来完成此操作,每个字段对应一个枚举值:
status_open: bool
status_upcoming: bool
status_suspended: bool
status_closed: bool
要执行“状态为开放或即将到来”的查询,请执行以下操作:
where("status_suspended", "==", false).where("status_closed", "==", false)
这是如何工作的?好吧,因为它是一个枚举,所以您知道其中一个值必须分配 true
。因此,如果您可以确定所有 other 值都不匹配给定条目,那么通过推论,它必须与您最初查找的值之一匹配。
另见
in
/not-in
/array-contains-in
: https://firebase.google.com/docs/firestore/query-data/queries#in_and_array-contains-any
!=
: https://firebase.googleblog.com/2020/09/cloud-firestore-not-equal-queries.html
实际上我发现@Dan McGrath 在这里工作的答案是重写他的答案:
private void query() {
FirebaseFirestore db = FirebaseFirestore.getInstance();
db.collection("STATUS")
.whereIn("status", Arrays.asList("open", "upcoming")) // you can add up to 10 different values like : Arrays.asList("open", "upcoming", "Pending", "In Progress", ...)
.addSnapshotListener(new EventListener<QuerySnapshot>() {
@Override
public void onEvent(@Nullable QuerySnapshot queryDocumentSnapshots, @Nullable FirebaseFirestoreException e) {
for (DocumentSnapshot documentSnapshot : queryDocumentSnapshots) {
// I assume you have a model class called MyStatus
MyStatus status= documentSnapshot.toObject(MyStatus.class);
if (status!= null) {
//do somthing...!
}
}
}
});
}
如果您的字段数量有限,请务必像上面的示例一样使用 true 和 false 创建新字段。但是,如果您直到运行时才知道这些字段是什么,则只能组合查询。
这是一个标签或示例...
// the ids of students in class
const students = [studentID1, studentID2,...];
// get all docs where student.studentID1 = true
const results = this.afs.collection('classes',
ref => ref.where(`students.${students[0]}`, '==', true)
).valueChanges({ idField: 'id' }).pipe(
switchMap((r: any) => {
// get all docs where student.studentID2...studentIDX = true
const docs = students.slice(1).map(
(student: any) => this.afs.collection('classes',
ref => ref.where(`students.${student}`, '==', true)
).valueChanges({ idField: 'id' })
);
return combineLatest(docs).pipe(
// combine results by reducing array
map((a: any[]) => {
const g: [] = a.reduce(
(acc: any[], cur: any) => acc.concat(cur)
).concat(r);
// filter out duplicates by 'id' field
return g.filter(
(b: any, n: number, a: any[]) => a.findIndex(
(v: any) => v.id === b.id) === n
);
}),
);
})
);
遗憾的是,没有其他方法可以组合超过 10 个项目(如果 < 10 个项目,请使用 array-contains-any)。
也没有其他方法可以避免重复读取,因为您不知道搜索将匹配的 ID 字段。幸运的是,Firebase 有很好的缓存。
对于那些喜欢承诺的人...
const p = await results.pipe(take(1)).toPromise();
有关这方面的更多信息,请参阅 this article 我写的。
J
我不喜欢每个人都说不可能。
如果您在模型中创建另一个“hacky”字段来构建复合...
例如,为每个包含所有逻辑或元素的文档创建一个数组
然后查询 .where("field", arrayContains: [...]
来自the docs:
You can also chain multiple where() methods to create more specific queries (logical AND).
如何执行 OR
查询?
示例:
- 给我字段
status
为open
或upcoming
的所有文档
- 给我所有文档,其中字段
status == open
或createdAt <= <somedatetime>
OR
不受支持,因为服务器很难对其进行扩展(需要保持状态以进行重复数据删除)。解决方法是发出 2 个查询,每个条件一个,并在客户端上进行重复数据删除。
编辑(2019 年 11 月):
Cloud Firestore 现在支持 IN
查询,这是一种有限类型的 OR
查询。
对于上面的例子你可以这样做:
// Get all documents in 'foo' where status is open or upcmoming
db.collection('foo').where('status','in',['open','upcoming']).get()
然而,仍然不可能执行涉及多个字段的一般 OR
条件。
您可以使用 rxjs 合并运算符绑定两个 Observable。 这里有一个例子。
import { Observable } from 'rxjs/Observable';
import 'rxjs/add/observable/merge';
...
getCombinatedStatus(): Observable<any> {
return Observable.merge(this.db.collection('foo', ref => ref.where('status','==','open')).valueChanges(),
this.db.collection('foo', ref => ref.where('status','==','upcoming')).valueChanges());
}
然后就可以使用上面的方法订阅新的Observable更新了:
getCombinatedStatus.subscribe(results => console.log(results);
希望对你有所帮助,来自智利的问候!!
建议也给状态赋值。
例如。
{ name: "a", statusValue = 10, status = 'open' }
{ name: "b", statusValue = 20, status = 'upcoming'}
{ name: "c", statusValue = 30, status = 'close'}
你可以通过ref.where('statusValue', '<=', 20)
查询,然后'a'
和'b'
都会找到。
这可以节省您的查询成本和性能。
顺便说一句,它并不能解决所有的问题。
我没有 "status" 字段,但有状态相关字段,根据请求将它们更新为 true 或 false,例如
{ name: "a", status_open: true, status_upcoming: false, status_closed: false}
但是,请检查 Firebase Cloud Functions。你可以有一个监听状态变化的函数,更新状态相关的属性,比如
{ name: "a", status: "open", status_open: true, status_upcoming: false, status_closed: false}
一个或另一个,您的查询可能只是
...where('status_open','==',true)...
希望对您有所帮助。
我们刚才遇到了同样的问题,幸运的是我们唯一可能的值是 A,B,C,D (4) 所以我们必须查询 A||B, A||C, A ||B||C、D 等
就像几个月前一样,firebase 支持一个新查询 array-contains
所以我们要做的是创建一个数组,然后将 OR 值预处理到数组中
if (a) {
array addObject:@"a"
}
if (b) {
array addObject:@"b"
}
if (a||b) {
array addObject:@"a||b"
}
etc
我们对所有 4!
值或无论有多少组合都这样做。
然后我们可以简单地检查查询 [document arrayContains:@"a||c"]
或我们需要的任何类型的条件。
因此,如果某些内容仅符合我们 4 个条件(A、B、C、D)中的条件 A
,则其数组将包含以下文字字符串:@["A", "A||B", "A||C", "A||D", "A||B||C", "A||B||D", "A||C||D", "A||B||C||D"]
然后对于这些 OR
组合中的任何一个,我们可以只搜索 array-contains
我们可能想要的任何内容(例如 "A||C")
注意:只有在您有几个可能的值要比较 OR 时,这才是合理的方法。
关于
或者不支持
但是如果你需要,你可以在你的代码中做到这一点
Ex:如果我想要查询产品,其中(Size Equal Xl OR XXL : AND Gender is Male)
productsCollectionRef
//1* first get query where can firestore handle it
.whereEqualTo("gender", "Male")
.addSnapshotListener((queryDocumentSnapshots, e) -> {
if (queryDocumentSnapshots == null)
return;
List<Product> productList = new ArrayList<>();
for (DocumentSnapshot snapshot : queryDocumentSnapshots.getDocuments()) {
Product product = snapshot.toObject(Product.class);
//2* then check your query OR Condition because firestore just support AND Condition
if (product.getSize().equals("XL") || product.getSize().equals("XXL"))
productList.add(product);
}
liveData.setValue(productList);
});
借助 recent addition of IN queries,Firestore 支持 "up to 10 equality clauses on the same field with a logical OR"
(1) 的可能解决方案是:
documents.where('status', 'in', ['open', 'upcoming']);
见Firebase Guides: Query Operators | in
and array-contains-any
对于 Flutter dart 语言,使用这个:
db.collection("projects").where("status", whereIn: ["public", "unlisted", "secret"]);
这并不能解决所有情况,但对于“枚举”字段,您可以通过为每个枚举值创建一个单独的布尔字段,然后为每个添加一个 where("enum_<value>", "==", false)
来模拟“或”查询不是您想要的“OR”子句的一部分的值。
例如,考虑您的第一个查询:
- Give me all documents where the field status is open OR upcoming
您可以通过将 status: string
字段拆分为多个布尔字段来完成此操作,每个字段对应一个枚举值:
status_open: bool
status_upcoming: bool
status_suspended: bool
status_closed: bool
要执行“状态为开放或即将到来”的查询,请执行以下操作:
where("status_suspended", "==", false).where("status_closed", "==", false)
这是如何工作的?好吧,因为它是一个枚举,所以您知道其中一个值必须分配 true
。因此,如果您可以确定所有 other 值都不匹配给定条目,那么通过推论,它必须与您最初查找的值之一匹配。
另见
in
/not-in
/array-contains-in
: https://firebase.google.com/docs/firestore/query-data/queries#in_and_array-contains-any
!=
: https://firebase.googleblog.com/2020/09/cloud-firestore-not-equal-queries.html
实际上我发现@Dan McGrath 在这里工作的答案是重写他的答案:
private void query() {
FirebaseFirestore db = FirebaseFirestore.getInstance();
db.collection("STATUS")
.whereIn("status", Arrays.asList("open", "upcoming")) // you can add up to 10 different values like : Arrays.asList("open", "upcoming", "Pending", "In Progress", ...)
.addSnapshotListener(new EventListener<QuerySnapshot>() {
@Override
public void onEvent(@Nullable QuerySnapshot queryDocumentSnapshots, @Nullable FirebaseFirestoreException e) {
for (DocumentSnapshot documentSnapshot : queryDocumentSnapshots) {
// I assume you have a model class called MyStatus
MyStatus status= documentSnapshot.toObject(MyStatus.class);
if (status!= null) {
//do somthing...!
}
}
}
});
}
如果您的字段数量有限,请务必像上面的示例一样使用 true 和 false 创建新字段。但是,如果您直到运行时才知道这些字段是什么,则只能组合查询。
这是一个标签或示例...
// the ids of students in class
const students = [studentID1, studentID2,...];
// get all docs where student.studentID1 = true
const results = this.afs.collection('classes',
ref => ref.where(`students.${students[0]}`, '==', true)
).valueChanges({ idField: 'id' }).pipe(
switchMap((r: any) => {
// get all docs where student.studentID2...studentIDX = true
const docs = students.slice(1).map(
(student: any) => this.afs.collection('classes',
ref => ref.where(`students.${student}`, '==', true)
).valueChanges({ idField: 'id' })
);
return combineLatest(docs).pipe(
// combine results by reducing array
map((a: any[]) => {
const g: [] = a.reduce(
(acc: any[], cur: any) => acc.concat(cur)
).concat(r);
// filter out duplicates by 'id' field
return g.filter(
(b: any, n: number, a: any[]) => a.findIndex(
(v: any) => v.id === b.id) === n
);
}),
);
})
);
遗憾的是,没有其他方法可以组合超过 10 个项目(如果 < 10 个项目,请使用 array-contains-any)。
也没有其他方法可以避免重复读取,因为您不知道搜索将匹配的 ID 字段。幸运的是,Firebase 有很好的缓存。
对于那些喜欢承诺的人...
const p = await results.pipe(take(1)).toPromise();
有关这方面的更多信息,请参阅 this article 我写的。
J
我不喜欢每个人都说不可能。
如果您在模型中创建另一个“hacky”字段来构建复合...
例如,为每个包含所有逻辑或元素的文档创建一个数组
然后查询 .where("field", arrayContains: [...]