Reputation: 3705
For example I have dynamic filter for my list of books where I can set specific color, authors and categories. This filter can set multiple colors at once and multiple categories.
Book > Red, Blue > Adventure, Detective.
How can I add "where" conditionally?
firebase
.firestore()
.collection("book")
.where("category", "==", )
.where("color", "==", )
.where("author", "==", )
.orderBy("date")
.get()
.then(querySnapshot => {...
Upvotes: 87
Views: 87256
Reputation: 56996
Firebase Version 9
The docs do not cover this but here is how to add conditional where clauses to a query:
import { collection, query, where } from 'firebase/firestore'
const queryConstraints = []
if (group != null) queryConstraints.push(where('group', '==', group))
if (pro != null) queryConstraints.push(where('pro', '==', pro))
const q = query(collection(db, 'videos'), ...queryConstraints)
The source of this answer is a bit of intuitive guesswork.
Upvotes: 34
Reputation: 4663
const citiesRef = db.collection('cities');
then you can combine constraints with a logical AND by chaining multiple equality operators (== or array-contains).
citiesRef.where('state', '==', 'CO').where('name', '==', 'Denver');
citiesRef.where('state', '==', 'CA').where('population', '<', 1000000);index.js
Reference to firebase documentation here
Upvotes: 1
Reputation: 9933
This could be also a solution to this problem.
async function filterCollection(collectionName, filterArr) {
if (!collectionName) return [];
let query = db.collection(collectionName);
if (filterArr.length > 0) {
filterArr.forEach((filter) => {
query = query.where(filter.field, filter.operator, filter.value);
});
}
const snapshot = await query.get();
const response = snapshot.docs.map((doc) => {
return { id: doc.id, result: doc.data() }
});
return response;
}
/**List the Data */
app.get("/list-documet-ids", async (req, res, next) => {
const data = await filterCollection(
"Users",
[
{
field: "color",
operator: "==",
value: 'red',
},
{
field: "categories",
operator: "==",
value: 'Detective',
},
]
);
res.send(data);
});
Upvotes: 0
Reputation: 1
You can filter data with multiple where clauses:
import { query, collection, where, getDocs } from "firebase/firestore";
const q = query(
collection(db, "products"),
where("category", "==", "Computer"),
where("types", "array-contains", ['Laptop', 'Lenovo', 'Intel']),
where("price", "<=", 1000),
);
const docsSnap = await getDocs(q);
docsSnap.forEach((doc) => {
console.log(doc.data());
});
Upvotes: 16
Reputation: 82
Simple function where you can specify the path and an array of filters that you can pass and get you documents, hope it helps.
async function filterDoc(path, filters) {
if (!path) return [];
//define the collection path
let q = db.collection(path);
//check if there are any filters and add them to the query
if (filters.length > 0) {
filters.forEach((filter) => {
q = q.where(filter.field, filter.operator, filter.value);
});
}
//get the documents
const snapshot = await q.get();
//loop through the documents
const data = snapshot.docs.map((doc) => doc.data());
//return the data
return data;
}
//call the function
const data = await filterDoc(
"categories_collection",
[
{
field: "status",
operator: "==",
value: "active",
},
{
field: "parent_id",
operator: "==",
value: "kSKpUc3xnKjtpyx8cMJC",
},
]
);
Upvotes: 1
Reputation: 358
As CollectionRef
does not have query
method in firebase web version 9,
I modified @abk's answer.
async getQueryResult(path, options = {}) {
/* Example
options = {
where: [
["isPublic", "==", true],
["isDeleted", "==", false]
],
orderBy: [
["likes"],
["title", "desc"]
],
limit: 30
}
*/
try {
let { where, orderBy, limit } = options;
let collectionRef = collection(<firestore>, path);
let queryConstraints = [];
if (where) {
where = where.map((w) => firestore.where(...w));
queryConstraints = [...queryConstraints, ...where];
}
if (orderBy) {
orderBy = orderBy.map((o) => firestore.orderBy(...o));
queryConstraints = [...queryConstraints, ...orderBy];
}
if (limit) {
limit = firestore.limit(limit);
queryConstraints = [...queryConstraints, limit];
}
const query = firestore.query(collectionRef, ...queryConstraints);
const querySnapshot = await firestore.getDocs(query);
const docList = querySnapshot.docs.map((doc) => {
const data = doc.data();
return {
id: doc.id,
...data,
};
});
return docList;
} catch (error) {
console.log(error);
}
}
Upvotes: 0
Reputation: 2632
Note that a multiple WHERE
clause is inherently an AND
operation.
Upvotes: 2
Reputation: 4689
If you're using angular fire, you can just use reduce
like so:
const students = [studentID, studentID2,...];
this.afs.collection('classes',
(ref: any) => students.reduce(
(r: any, student: any) => r.where(`students.${student}`, '==', true)
, ref)
).valueChanges({ idField: 'id' });
This is an example of multiple tags...
You could easily change this for any non-angular framework.
For OR queries (which can't be done with multiple where clauses), see here.
Upvotes: 1
Reputation: 317467
As you can see in the API docs, the collection() method returns a CollectionReference. CollectionReference extends Query, and Query objects are immutable. Query.where() and Query.orderBy() return new Query objects that add operations on top of the original Query (which remains unmodified). You will have to write code to remember these new Query objects so you can continue to chain calls with them. So, you can rewrite your code like this:
var query = firebase.firestore().collection("book")
query = query.where(...)
query = query.where(...)
query = query.where(...)
query = query.orderBy(...)
query.get().then(...)
Now you can put in conditionals to figure out which filters you want to apply at each stage. Just reassign query
with each newly added filter.
if (some_condition) {
query = query.where(...)
}
Upvotes: 155
Reputation: 11
async yourFunction(){
const Ref0 = firebase.firestore().collection("your_collection").doc(doc.id)
const Ref1 = appointmentsRef.where('val1', '==',condition1).get();
const Ref2 = appointmentsRef.where("val2", "!=", condition2).get()
const [snapshot_val1, snapshot_val2] = await Promise.all([Ref1, Ref2]);
const val1_Array = snapshot_val1.docs;
const val2_Array = snapshot_val2.docs;
const globale_val_Array = val1_Array .concat(val2_Array );
return globale_val_Array ;
}
/*Call you function*/
this.checkCurrentAppointment().then(docSnapshot=> {
docSnapshot.forEach(doc=> {
console.log("Your data with multiple code query:", doc.data());
});
});
Upvotes: 1
Reputation: 193
For example, there's an array look like this
const conditionList = [
{
key: 'anyField',
operator: '==',
value: 'any value',
},
{
key: 'anyField',
operator: '>',
value: 'any value',
},
{
key: 'anyField',
operator: '<',
value: 'any value',
},
{
key: 'anyField',
operator: '==',
value: 'any value',
},
{
key: 'anyField',
operator: '==',
value: 'any value',
},
]
Then you can just put the collection which one you want to set query's conditions into this funcion.
function* multipleWhere(
collection,
conditions = [{ field: '[doc].[field name]', operator: '==', value: '[any value]' }],
) {
const pop = conditions.pop()
if (pop) {
yield* multipleWhere(
collection.where(pop.key, pop.operator, pop.value),
conditions,
)
}
yield collection
}
You will get the collection set query's conditions.
Upvotes: 1
Reputation: 2233
In addition to @Doug Stevenson answer. When you have more than one where
it is necessary to make it more dynamic as in my case.
function readDocuments(collection, options = {}) {
let {where, orderBy, limit} = options;
let query = firebase.firestore().collection(collection);
if (where) {
if (where[0] instanceof Array) {
// It's an array of array
for (let w of where) {
query = query.where(...w);
}
} else {
query = query.where(...where);
}
}
if (orderBy) {
query = query.orderBy(...orderBy);
}
if (limit) {
query = query.limit(limit);
}
return query
.get()
.then()
.catch()
}
// Usage
// Multiple where
let options = {where: [["category", "==", "someCategory"], ["color", "==", "red"], ["author", "==", "Sam"]], orderBy: ["date", "desc"]};
//OR
// A single where
let options = {where: ["category", "==", "someCategory"]};
let documents = readDocuments("books", options);
Upvotes: 8