0

In Firebase (v. 9), I have this firestore DB collection named "users". Each user has these fields: "gender" (string: 'male' or 'female'), "age" (number) and "language" (string: 'en-EN' or 'fr-FR' or 'es-ES' or 'de-DE' ). From a filter checkbox menu, a user can select the languages then execute a query, get the result and than applied another filter and get another result.

For example: I check, from the language menu "English" and "French" --> get the result, for example 3 users (2 female and 1 male). Then, from the gender menu, I check "male" --> get the result: just that one male user from the previous query result. But a user can also do the first query for the language and then, in the second one, check both 'male' and 'female'.

I'm trying to do the query combining 'array-contains' and 'in' operator but I have no luck.

The query

    const q = query(
          collection(db, 'users'),
          where('gender', 'array-contains', ['male', 'female']),
          where('language', 'in', ['en-EN', 'es-ES']),
          where('age', '>', 14),
          where('age', '<', 40)
        );

EDIT: For that query I changed my DB structure: gender has become an array but with 'array-contains' I can't do:

where('gender', 'array-contains', ['male', 'female'])

It must be something like that:

where('gender', 'array-contains', 'male')

but I want to check for both gender.

What could solve my problem is doing two queries with 'in' operator but I can't do that. (Firebase allows me to have only 1 'in' operator in the query). My goal is, for example, to get every users in the DB that speak English or French, both male or female and with an age between 14 and 40. Is that the correct way to do this? How can I do the first query for the language and then, do another query starting from that result in order to avoid redoing the first query (the language) when I query for the gender and then when I query for the age? I also create indexes as Firebase suggested me to do, but I still get an empty array.

I was reading the firebase 'Query limitation' from the doc:

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. In a compound query, range (<, <=, >, >=) and not equals (!=, not-in) comparisons must all filter on the same field. You can use at most one array-contains clause per query. You can't combine array-contains with array-contains-any. You can use at most one in, not-in, or array-contains-any clause per query. You can't combine in , not-in, and array-contains-any in the same query. You can't order your query by a field included in an equality (==) or in clause. The sum of filters, sort orders, and parent document path (1 for a subcollection, 0 for a root collection) in a query cannot exceed 100.

That says I can combine the 'in' operator only with 'array-contains'. It also says, that "for other cases, create a separate query for each OR condition and merge the query results in your app" but I can't find any example on how to do that.

I read an answer here > Firebase Firestore - Multiple array-contains in a compound query where someone suggest to change the structure of the data to query, from an array to a map and then query with the equal operator:

where('field.name1', '==', true),
where('field.name2', '==', true)

I still have no luck with this.

Edit2: I guess the only thing I could do, is to execute 2 different queries, get the results in two different arrays and than do whatever logic I need to implement using js..I mean, not with firebase query operator. Can someone guide me through the process?

Any help is appreciated.

Thank you

user16469315
  • 89
  • 1
  • 1
  • 9
  • Hi, have you already checked my posted answer? Let me know if you have any questions or clarifications. Also, See [What should I do when someone answers my question?](https://stackoverflow.com/help/someone-answers) – Marc Anthony B Jul 15 '22 at 12:22

1 Answers1

1

Google Cloud Firestore only allows one in condition per query. You'll need to do the second one in JavaScript processing the results. Probably, the best way you can do is to get the result from the original query and process the result using Javascript. See sample code below:

const q = query(
    collection(db, 'users'),
    where('language', 'in', ['en-EN', 'es-ES']),
    where('age', '>', 14),
    where('age', '<', 40)
  );

// Pass the data from the checkboxes.
// Can be 'male', 'female', or ('male' and 'female')
const gender = ['male', 'female'];
let array = [];
const snapshot = await getDocs(q);
snapshot.forEach((doc) => {
    if (gender.includes(doc.data().gender)) {
      array.push(doc.data());
    }
  });

console.log(array);

The above code will return the processed result whatever you pass on the gender variable. You could do it vice-versa, if you want to query the gender first then just interchange the query and variables.


Another option is to have a compound string, for example:

Checked:

  • Male
  • Female
  • en_EN
  • en_ES

Compound strings will be ['en_male', 'en_female', 'es_male', 'es_female']. You can query this by only one in statement. See sample code below:

// Combined data passed from the checkboxes.
// Can only be one and up to 10 comparison values.
const compound = ['en_male', 'en_female', 'es_male', 'es_female'];

const q = query(
    collection(db, 'users'),
    where('compound', 'in', compound),
    where('age', '>', 14),
    where('age', '<', 40)
    );

const snapshot = await getDocs(q);
snapshot.forEach((doc) => {
    console.log(doc.id, doc.data());
});

The downside of this approach is you can only have up to 10 comparison values for the in operator.


For more relevant information, you may check this documentation.

Marc Anthony B
  • 3,635
  • 2
  • 4
  • 19