Like the question states, Firebase only allows one array-contains-any per query (https://firebase.google.com/docs/firestore/query-data/queries).
What happens when someone needs a complex query? I need about 10 .where()
with 4 array-contains-any
clauses on my query. Is there a work around or do I have to take the results from the first array-contains-any
and then callout again and again? Seems taxing and unnecessary.
var usersMatchesCollection = config.db.collection("Users");
var currentUserMatchesAllData = usersMatchesCollection.where('preferences.lookingFor', 'array-contains-any', docDataPreferences.lookingFor)
.where('info.identifyAs', '==', docDataPreferences.prefIdentifyAs)
.where('info.languages', 'array-contains-any', docDataPreferences.prefLanguages)
.where('age', 'array-contains-any', docDataPreferences.ageRange)
.where('profession', 'array-contains-any', docDataPreferences.prefProfession)
.where('education', '==', docDataPreferences.prefEducation)
.where('kids', '==', docDataPreferences.prefKids)
.where('married', '==', docDataPreferences.prefMarried)
.where('drinking', '==', docDataPreferences.prefDrinking)
.where('smokingCig', '==', docDataPreferences.prefSmokingCig)
await currentUserMatchesAllData.get().then( function (matchesQuerySnapshot) {
matchesQuerySnapshot.forEach(function(doc) {
console.log('doc data: ' + JSON.stringify(doc.data().id));
})
})
What your asking for is a query Cloud Firestore cannot serve, as we cannot do it in a generic way that's scales regardless of the data. There are some tricks you could employ that reduce the queries complexity, which means reducing the number of queries you'd need to break this into.
For the where('preferences.lookingFor', 'array-contains-any', docDataPreferences.lookingFor)
condition, assuming possible different values is limited and the total number is know, you could create a mapping of possibilities and reduce it to a single equality. For example,
Let's assume 3 possible values A
, B
, and C
, and that you must have at least 1. This means there is only 7 different query variations, which could be encoded as a series of Boolean fields:
preferences.lookingForCheck = {
A: <if A>,
B: <If B>,
C: <If C>,
AB: <if A OR B>,
AC: <If A OR C>,
BC: <If B OR C>,
ABC: <if A OR B OR C>,
}
These are fairly simple at write-time to calculate, and mean you can easily calculate which field to query against: where('preferences.lookingForCheck.BC', '==', true)
This one is probably to extension to want to explode as combinations, so let's keep this one.
I'll over simplify, and assume some simple buckets, so: 21-30
, 31-44
, 45-64
, 65+
. You could make this explicit fields like above (A, B, C, D), which would be 15 fields. If you can assume the range will be continuous (e.g if 21-30
& '45-64' is selected, then 31-30
must be selected to), you can optimize even more by instead saying there 2 lists of values from which users must select one from each and the value in the second must be equal or greater than the second:
lower_age: [21, 31, 45, 65+]
upper_age: [30, 44, 64, 65+]
How you only need 10 combinations with a true/false value: 21-30, 21-44, 21-64, 21-65+, 30-44, 30-64, 30-65+, 45-65, 45-65+, 65+-65+
This is another hard one. Since we've already committed info.languages
, we can't have this as an array-contains query. We do have some options to consider.
Following the above, you'll end up with something like:
preferences = `preferences.lookingForCheck.${prefCombo}`;
ageRange = `ageRange.${lowerRange}to${upperRange}`;
var usersMatchesCollection = config.db.collection("Users");
var currentUserMatchesAllData = usersMatchesCollection.where(preferences, '==', docDataPreferences.lookingForMatch)
.where('info.identifyAs', '==', docDataPreferences.prefIdentifyAs)
.where('info.languages', 'array-contains-any', docDataPreferences.prefLanguages)
.where(ageRange, '==', docDataPreferences.ageRange)
.where('profession', '==', docDataPreferences.prefProfessionCollarCheck)
.where('education', '==', docDataPreferences.prefEducation)
.where('kids', '==', docDataPreferences.prefKids)
.where('married', '==', docDataPreferences.prefMarried)
.where('drinking', '==', docDataPreferences.prefDrinking)
.where('smokingCig', '==', docDataPreferences.prefSmokingCig)
await currentUserMatchesAllData.get().then( function (matchesQuerySnapshot) {
matchesQuerySnapshot.forEach(function(doc) {
// Filter by profession here
console.log('doc data: ' + JSON.stringify(doc.data().id));
})
})
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With