marsuser
marsuser

Reputation: 145

Firebase Firestore Query based on filters

Let's say I have a collection of 2000 documents with fields id, age, location in cloud firestore.

On client side(nodejs), I have 50 ids and want to perform a query on the firestore only for these 50 ids and should satisfy the conditions like age>25, location=chicago. I don't want to read all 50 documents. Want to read the documents from these 50 ids which satisfies the conditions I mentioned (The resulting documents could be less than 50) trying to reduce read operations. Is it possible to do this? Looking at the documentation I see there is in operator but my understanding is it only supports for 10 comparison values. Is it possible to query as per my requirement?

While creating the documents, I can adjust the documentId to be the field id if that makes the query possible.

Upvotes: 1

Views: 2992

Answers (1)

Marc Anthony B
Marc Anthony B

Reputation: 4069

Based on your requirement, you can still use comparison values like code below:

Firebase Web Version 8:

db.collection("users")
    .where("age", ">", "25")
    .where("location", "==", "chicago")
    .get()
    .then((querySnapshot) => {
        querySnapshot.forEach((doc) => {
            // doc.data() is never undefined for query doc snapshots
            console.log(doc.id, " => ", doc.data());
        });
    })
    .catch((error) => {
        console.log("Error getting documents: ", error);
    });

Firebase Web Version 9:

const q = query(collection(db, "users"), where("age", ">", "25"), where("location", "==", "chicago");

const querySnapshot = await getDocs(q);
querySnapshot.forEach((doc) => {
  // doc.data() is never undefined for query doc snapshots
  console.log(doc.id, " => ", doc.data());
});

You can use comparison value as much as you want using these operators (<, <=, >, >=). By using multiple where queries, it provides you a query statement with logical AND query. The only limitation are for logical OR queries which on these documentation states:

Cloud Firestore provides limited support for logical OR queries. The in, and array-contains-any operators support a logical OR of up to 10 equality (==) or array-contains conditions on a single field. For other cases, create a separate query for each OR condition and merge the query results in your app.


UPDATE:

If you want to query 50 documents at the same time. You can query it using FieldPath.documentID() and "IN" query. See code below:

const docArray = [
// Array of Document IDs.
];

db.collection("users")
    .where(firebase.firestore.FieldPath.documentId(), 'in', docArray)
    .where("age", ">", "25")
    .where("location", "==", "chicago")
    .get()
    .then((querySnapshot) => {
        querySnapshot.forEach((doc) => {
            // doc.data() is never undefined for query doc snapshots
            console.log(doc.id, " => ", doc.data());
        });
    })
    .catch((error) => {
        console.log("Error getting documents: ", error);
    });

As I stated on my previous answer, this only works if the array is of less than 10 items (Firestore limitation). If you need more, you'll have to either batch the IDs, into smaller arrays and perform multiple queries. Check code I written below:

// Consists of 11 values
const docArray = [
    '5fKmta1jgEiCQBZvSJ3Y',
    'BubAGhg6IZbnzU70wdPf',
    'FVEq9trqF2Hy8ROsAjeW',
    'Nhz0pF2z3zw3aBcmnpqs',
    'TYYDYyIiyhVuWp91h7OS',
    'VDDiUGKkGnwrdo8jmDpb',
    'VXnXEuNfrhUAM8UbZ2AH',
    'XukTkr4XBey3yqAvFa3d',
    'cSNI1NgHP8OEsDY66RBw',
    'eCPc9F1bpS1qXD9WGKE2',
    'eHdKQPhE7snnRjoWJzBK'
];
const chunkedArray = [];
const resArray = [];
var promises = [];

// Firestore query limit
size = 10;

for(var i = 0; i < docArray.length; i += size) {
chunkedArray.push(docArray.slice(i, i+size));
}

chunkedArray.forEach((chunked) => {
    promises.push(
        db.collection("users")
            .where(firebase.firestore.FieldPath.documentId(), 'in', chunked)
            .where("age", ">", "25")
            .where("location", "==", "chicago")
            .get()
            .then((querySnapshot) => {
                querySnapshot.forEach((doc) => {
                    resArray.push(doc.data());
                });
            })
            .catch((error) => {
                console.log("Error getting documents: ", error);
            })
    );
})

Promise.all(promises).then(() => 
    // Returns merged data from forEach
    console.log(resArray)
);

You can also simply iterate the IDs, and get() each document individually. Look example code below:

docArray.forEach((docId) => {
    promises.push(
        db.collection("users")
            .where(firebase.firestore.FieldPath.documentId(), '==', docId)
            // .where("age", ">", "25")
            // .where("location", "==", "chicago")
            .get()
            .then((querySnapshot) => {
                querySnapshot.forEach((doc) => {
                    resArray.push(doc.data());
                });
            })
            .catch((error) => {
                console.log("Error getting documents: ", error);
            })
    );
})

Promise.all(promises).then(() => 
    // Returns merged data from forEach
    console.log(resArray)
);

Upvotes: 4

Related Questions