Firebase 限制每个查询一个“array-contains-any” - 如果我需要多个,该怎么办?

Oli*_*via 3 firebase react-native google-cloud-firestore

就像问题所述,Firebase 只允许每个查询一个 array-contains-any ( https://firebase.google.com/docs/firestore/query-data/queries )。

当有人需要复杂的查询时会发生什么?我的查询需要大约 10 个.where(),其中有 4 个子句。array-contains-any是否有解决方法,或者我是否必须从第一次获取结果array-contains-any,然后一次又一次地标注?看起来很费力而且没有必要。

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));
  })
})
Run Code Online (Sandbox Code Playgroud)

Dan*_*ath 5

您要求的是 Cloud Firestore 无法提供的查询,因为我们无法以无论数据如何都可扩展的通用方式来执行此操作。您可以使用一些技巧来降低查询复杂性,这意味着减少需要分解的查询数量。

偏好.寻找

对于该where('preferences.lookingFor', 'array-contains-any', docDataPreferences.lookingFor)条件,假设可能的不同值是有限的并且总数已知,您可以创建可能性的映射并将其简化为单个等式。例如,

让我们假设 3 个可能的值AB、 和C,并且您必须至少有 1 个。这意味着只有 7 种不同的查询变体,可以将其编码为一系列布尔字段:

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>,
} 
Run Code Online (Sandbox Code Playgroud)

这些在写入时计算起来相当简单,并且意味着您可以轻松计算要查询的字段: where('preferences.lookingForCheck.BC', '==', true)

信息语言

这个可能是为了扩展想要组合爆炸,所以就保留这个吧。

年龄

我会过度简化,并假设一些简单的桶,所以:21-30, 31-44, 45-64, 65+。您可以像上面那样创建显式字段(A、B、C、D),这将是 15 个字段。如果您可以假设范围是连续的(例如,如果21-30选择 & '45-64',则31-30必须选择),您可以通过说有 2 个值列表来进行更多优化,用户必须从每个值中选择一个并且第二个值必须等于或大于第二个: lower_age: [21, 31, 45, 65+] upper_age: [30, 44, 64, 65+]

如何只需要 10 个带有真/假值的组合:21-30, 21-44, 21-64, 21-65+, 30-44, 30-64, 30-65+, 45-65, 45-65+, 65+-65+

职业

这又是一个难点。由于我们已经提交了info.languages,所以我们不能将其作为包含数组的查询。我们确实有一些选择需要考虑。

  1. 每个职业类型发出 1 个查询,并在客户端合并结果。这是更多代码,但很简单。
  2. 根本不要查询该字段,而是过滤掉客户端不匹配的任何文档。假设错误匹配的数量很少,否则您将阅读大量最终丢弃的文档。
  3. 一种混合方法,您可以将职业(例如[白、蓝、粉]领)与针对其他领域讨论的相同方法进行分类,然后在客户端按特定职业进行过滤。

最终查询

按照上面的步骤,你最终会得到类似的结果:

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));
  })
})
Run Code Online (Sandbox Code Playgroud)