Skip to content

Commit f7ec3d1

Browse files
feat(firestore): Firestore Filter instance. Use Filter, Filter.or() & Filter.and() in Firestore queries. (#7045)
1 parent 3c47228 commit f7ec3d1

File tree

13 files changed

+2910
-145
lines changed

13 files changed

+2910
-145
lines changed

docs/firestore/usage/index.md

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -239,6 +239,43 @@ firestore()
239239
To learn more about all of the querying capabilities Cloud Firestore has to offer, view the
240240
[Firebase documentation](https://firebase.google.com/docs/firestore/query-data/queries).
241241

242+
It is now possible to use the `Filter` instance to make queries. They can be used with the existing query API.
243+
For example, you could chain like so:
244+
245+
```js
246+
const snapshot = await firestore()
247+
.collection('Users')
248+
.where(Filter('user', '==', 'Tim'))
249+
.where('email', '==', '[email protected]')
250+
.get();
251+
```
252+
253+
You can use the `Filter.and()` static method to make logical AND queries:
254+
255+
```js
256+
const snapshot = await firestore()
257+
.collection('Users')
258+
.where(Filter.and(Filter('user', '==', 'Tim'), Filter('email', '==', '[email protected]')))
259+
.get();
260+
```
261+
262+
You can use the `Filter.or()` static method to make logical OR queries:
263+
264+
```js
265+
const snapshot = await firestore()
266+
.collection('Users')
267+
.where(
268+
Filter.or(
269+
Filter.and(Filter('user', '==', 'Tim'), Filter('email', '==', '[email protected]')),
270+
Filter.and(Filter('user', '==', 'Dave'), Filter('email', '==', '[email protected]')),
271+
),
272+
)
273+
.get();
274+
```
275+
276+
For an understanding of what queries are possible, please consult the query limitation documentation on the official
277+
[Firebase Firestore documentation](https://firebase.google.com/docs/firestore/query-data/queries#limits_on_or_queries).
278+
242279
#### Limiting
243280

244281
To limit the number of documents returned from a query, use the `limit` method on a collection reference:
@@ -317,7 +354,6 @@ with an ID of `DEF`.
317354
Cloud Firestore does not support the following types of queries:
318355

319356
- Queries with range filters on different fields, as described in the previous section.
320-
- Logical OR queries. In this case, you should create a separate query for each OR condition and merge the query results in your app.
321357

322358
## Writing Data
323359

packages/firestore/android/src/reactnative/java/io/invertase/firebase/firestore/ReactNativeFirebaseFirestoreQuery.java

Lines changed: 120 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
import com.google.android.gms.tasks.Task;
2727
import com.google.android.gms.tasks.Tasks;
2828
import com.google.firebase.firestore.FieldPath;
29+
import com.google.firebase.firestore.Filter;
2930
import com.google.firebase.firestore.Query;
3031
import com.google.firebase.firestore.QuerySnapshot;
3132
import com.google.firebase.firestore.Source;
@@ -65,62 +66,139 @@ private void applyFilters(ReadableArray filters) {
6566
for (int i = 0; i < filters.size(); i++) {
6667
ReadableMap filter = filters.getMap(i);
6768

68-
ArrayList fieldPathArray = Objects.requireNonNull(filter).getArray("fieldPath").toArrayList();
69-
String[] segmentArray = (String[]) fieldPathArray.toArray(new String[0]);
69+
if (filter.hasKey("fieldPath")) {
70+
ArrayList<Object> fieldPathArray =
71+
Objects.requireNonNull(Objects.requireNonNull(filter).getArray("fieldPath"))
72+
.toArrayList();
73+
String[] segmentArray = (String[]) fieldPathArray.toArray(new String[0]);
74+
75+
FieldPath fieldPath = FieldPath.of(Objects.requireNonNull(segmentArray));
76+
String operator = filter.getString("operator");
77+
ReadableArray arrayValue = filter.getArray("value");
78+
Object value = parseTypeMap(query.getFirestore(), Objects.requireNonNull(arrayValue));
79+
80+
switch (Objects.requireNonNull(operator)) {
81+
case "EQUAL":
82+
query = query.whereEqualTo(Objects.requireNonNull(fieldPath), value);
83+
break;
84+
case "NOT_EQUAL":
85+
query = query.whereNotEqualTo(Objects.requireNonNull(fieldPath), value);
86+
break;
87+
case "GREATER_THAN":
88+
query =
89+
query.whereGreaterThan(
90+
Objects.requireNonNull(fieldPath), Objects.requireNonNull(value));
91+
break;
92+
case "GREATER_THAN_OR_EQUAL":
93+
query =
94+
query.whereGreaterThanOrEqualTo(
95+
Objects.requireNonNull(fieldPath), Objects.requireNonNull(value));
96+
break;
97+
case "LESS_THAN":
98+
query =
99+
query.whereLessThan(
100+
Objects.requireNonNull(fieldPath), Objects.requireNonNull(value));
101+
break;
102+
case "LESS_THAN_OR_EQUAL":
103+
query =
104+
query.whereLessThanOrEqualTo(
105+
Objects.requireNonNull(fieldPath), Objects.requireNonNull(value));
106+
break;
107+
case "ARRAY_CONTAINS":
108+
query =
109+
query.whereArrayContains(
110+
Objects.requireNonNull(fieldPath), Objects.requireNonNull(value));
111+
break;
112+
case "ARRAY_CONTAINS_ANY":
113+
query =
114+
query.whereArrayContainsAny(
115+
Objects.requireNonNull(fieldPath),
116+
Objects.requireNonNull((List<Object>) value));
117+
break;
118+
case "IN":
119+
query =
120+
query.whereIn(
121+
Objects.requireNonNull(fieldPath),
122+
Objects.requireNonNull((List<Object>) value));
123+
break;
124+
case "NOT_IN":
125+
query =
126+
query.whereNotIn(
127+
Objects.requireNonNull(fieldPath),
128+
Objects.requireNonNull((List<Object>) value));
129+
break;
130+
}
131+
} else if (filter.hasKey("operator") && filter.hasKey("queries")) {
132+
query = query.where(applyFilterQueries(filter));
133+
}
134+
}
135+
}
136+
137+
private Filter applyFilterQueries(ReadableMap filter) {
138+
if (filter.hasKey("fieldPath")) {
139+
String operator =
140+
(String) Objects.requireNonNull(Objects.requireNonNull(filter).getString("operator"));
141+
ReadableMap fieldPathMap = Objects.requireNonNull(filter.getMap("fieldPath"));
142+
ReadableArray segments = Objects.requireNonNull(fieldPathMap.getArray("_segments"));
143+
int arraySize = segments.size();
144+
String[] segmentArray = new String[arraySize];
70145

71-
FieldPath fieldPath = FieldPath.of(Objects.requireNonNull(segmentArray));
72-
String operator = filter.getString("operator");
146+
for (int i = 0; i < arraySize; i++) {
147+
segmentArray[i] = segments.getString(i);
148+
}
149+
FieldPath fieldPath = FieldPath.of(segmentArray);
73150
ReadableArray arrayValue = filter.getArray("value");
151+
74152
Object value = parseTypeMap(query.getFirestore(), Objects.requireNonNull(arrayValue));
75153

76-
switch (Objects.requireNonNull(operator)) {
154+
switch (operator) {
77155
case "EQUAL":
78-
query = query.whereEqualTo(Objects.requireNonNull(fieldPath), value);
79-
break;
156+
return Filter.equalTo(fieldPath, value);
80157
case "NOT_EQUAL":
81-
query = query.whereNotEqualTo(Objects.requireNonNull(fieldPath), value);
82-
break;
83-
case "GREATER_THAN":
84-
query =
85-
query.whereGreaterThan(
86-
Objects.requireNonNull(fieldPath), Objects.requireNonNull(value));
87-
break;
88-
case "GREATER_THAN_OR_EQUAL":
89-
query =
90-
query.whereGreaterThanOrEqualTo(
91-
Objects.requireNonNull(fieldPath), Objects.requireNonNull(value));
92-
break;
158+
return Filter.notEqualTo(fieldPath, value);
93159
case "LESS_THAN":
94-
query =
95-
query.whereLessThan(Objects.requireNonNull(fieldPath), Objects.requireNonNull(value));
96-
break;
160+
return Filter.lessThan(fieldPath, value);
97161
case "LESS_THAN_OR_EQUAL":
98-
query =
99-
query.whereLessThanOrEqualTo(
100-
Objects.requireNonNull(fieldPath), Objects.requireNonNull(value));
101-
break;
162+
return Filter.lessThanOrEqualTo(fieldPath, value);
163+
case "GREATER_THAN":
164+
return Filter.greaterThan(fieldPath, value);
165+
case "GREATER_THAN_OR_EQUAL":
166+
return Filter.greaterThanOrEqualTo(fieldPath, value);
102167
case "ARRAY_CONTAINS":
103-
query =
104-
query.whereArrayContains(
105-
Objects.requireNonNull(fieldPath), Objects.requireNonNull(value));
106-
break;
168+
return Filter.arrayContains(fieldPath, value);
107169
case "ARRAY_CONTAINS_ANY":
108-
query =
109-
query.whereArrayContainsAny(
110-
Objects.requireNonNull(fieldPath), Objects.requireNonNull((List<Object>) value));
111-
break;
170+
assert value != null;
171+
return Filter.arrayContainsAny(fieldPath, (List<?>) value);
112172
case "IN":
113-
query =
114-
query.whereIn(
115-
Objects.requireNonNull(fieldPath), Objects.requireNonNull((List<Object>) value));
116-
break;
173+
assert value != null;
174+
return Filter.inArray(fieldPath, (List<?>) value);
117175
case "NOT_IN":
118-
query =
119-
query.whereNotIn(
120-
Objects.requireNonNull(fieldPath), Objects.requireNonNull((List<Object>) value));
121-
break;
176+
assert value != null;
177+
return Filter.notInArray(fieldPath, (List<?>) value);
178+
default:
179+
throw new Error("Invalid operator");
122180
}
123181
}
182+
183+
String operator = Objects.requireNonNull(filter).getString("operator");
184+
ReadableArray queries =
185+
Objects.requireNonNull(Objects.requireNonNull(filter).getArray("queries"));
186+
ArrayList<Filter> parsedFilters = new ArrayList<>();
187+
int arraySize = queries.size();
188+
for (int i = 0; i < arraySize; i++) {
189+
ReadableMap map = queries.getMap(i);
190+
parsedFilters.add(applyFilterQueries(map));
191+
}
192+
193+
if (operator.equals("AND")) {
194+
return Filter.and(parsedFilters.toArray(new Filter[0]));
195+
}
196+
197+
if (operator.equals("OR")) {
198+
return Filter.or(parsedFilters.toArray(new Filter[0]));
199+
}
200+
201+
throw new Error("Missing 'Filter' instance return");
124202
}
125203

126204
private void applyOrders(ReadableArray orders) {

0 commit comments

Comments
 (0)