使用Firebase后端通过电话号码快速查找用户

使用Firebase后端通过电话号码快速查找用户,firebase,optimization,google-cloud-firestore,query-performance,Firebase,Optimization,Google Cloud Firestore,Query Performance,我正在开发一个带有Firebase后端的应用程序。在注册期间,我想让新用户看到他们的哪些联系人已经在应用程序中,并将他们添加为好友。因此,基本上,使用电话号码来匹配用户和联系人 在查询数据库以查找用户时,我的性能非常糟糕。
由于Firestore不支持或查询,我对每个电话号码运行两个查询(一个用于检查国家格式,另一个用于检查国际格式),如果有任何查询返回文档,请将该文档设置为找到的用户: findUserByPhoneNumber = (number, callback) => {

我正在开发一个带有Firebase后端的应用程序。在注册期间,我想让新用户看到他们的哪些联系人已经在应用程序中,并将他们添加为好友。因此,基本上,使用电话号码来匹配用户和联系人

在查询数据库以查找用户时,我的性能非常糟糕。
由于Firestore不支持或查询,我对每个电话号码运行两个查询(一个用于检查国家格式,另一个用于检查国际格式),如果有任何查询返回文档,请将该文档设置为找到的用户:

findUserByPhoneNumber = (number, callback) => {

  //utility function to, well, sanitize phone numbers
  sanitizeNumber = (str) => {
    if (str) {
      var num = str.match(/\d/g);
      num = num.join("");
      return num;
    } else {
      return null
    }
  }

  var foundUser = null

  Promise.all([
    usersRef.where('phoneNumbers.nationalFormat', '==', sanitizeNumber(number)).get()
      .then(snapshot => {
        if (snapshot.docs.length > 0 && snapshot.docs[0].data()) {
          // console.log('nationalFormat result: ', snapshot.docs[0]);
          foundUser = snapshot.docs[0].data()
        }
        return foundUser
      }),
    usersRef.where('phoneNumbers.internationalFormat', '==', sanitizeNumber(number)).get()
      .then(snapshot => {
        if (snapshot.docs.length > 0 && snapshot.docs[0].data()) {
          // console.log('internationalFormat result: ', snapshot.docs[0]);
          foundUser = snapshot.docs[0].data()
        }
        return foundUser
      })
  ])
  .then(results => {
    res = results.filter(el => { return el != null })
    if (results.length > 0) {
      callback(res[0])
    }
  })
}
findUserByPhoneNumber
为循环中的每个联系人运行。在我的手机上测试205个联系人时,整个过程大约需要30秒,比我想要的长约29秒,特别是考虑到测试数据库只有8条记录

getContacts = () => {

  getCs = () => {
    // Declare arrays
    const contactsWithAccount = []
    const contactsWithNoAccount = []

    // Get contacts from user's phone
    Contacts.getAll((err, contacts) => {
      if (err) throw err

      // For each contact, iterate
      for (var i = 0; i < contacts.length; i++) {
        const item = contacts[i]

        if (item.phoneNumbers && item.phoneNumbers.length > 0) {
          const phone = item.phoneNumbers[0].number

          // If the sanitized phone number is different from the current user's phone number (saved in DB), run the following logic
          if (this.state.user.phoneNumbers.nationalFormat != sanitizeNumber(phone)
            && this.state.user.phoneNumbers.internationalFormat != sanitizeNumber(phone)
          ) {

            findUserByPhoneNumber(phone, (fu) => {
              contactObject = {
                key: item.recordID,
                name: item.givenName,
                normalizedName: item.givenName.toLowerCase(),
                phoneNumber: phone,
                user: this.state.user,
                hasAccount: null,
                friendId: null,
                isFriend: null
              }

              const foundUser = fu

              // if found user, push in contactsWithAccount, otherwise push in contactsWithNoAccount
              if (foundUser && foundUser._id != this.state.user._id) {
                contactObject.hasAccount = true
                contactObject.friendId = foundUser._id
                if (this.state.user.friends && this.state.user.friends.includes(foundUser._id)) {
                  contactObject.isFriend = true
                }
                contactsWithAccount.push(contactObject)
              }
              else {
                contactsWithNoAccount.push(contactObject)
              }

              // if the two arrays are filled up, run the callback
              // NOTE_1: we use the two lengths +1 to account for the current
              //         user's document that we skip and dont add to any of the arrays
              // NOTE_2: this bizare method was the only way to handle the results
              //         coming in asynchronously
              if (contactsWithAccount.length + contactsWithNoAccount.length + 1 == contacts.length) {
                console.log('finished');
                sortCs(contactsWithAccount, contactsWithNoAccount)
              }
            })
          }
        }
      }

    })

  }

  // sorts the two arrays alphabetically
  sortCs = (withAccount, withNoAccount) => {
    compare = (a,b) => {
      if (a.name < b.name)
        return -1;
      if (a.name > b.name)
        return 1;
      return 0;
    }
    withAccount.sort(compare)
    withNoAccount.sort(compare)
    this.setState({ withAccount, withNoAccount })
  }

  // unleash the monster
  getCs(sortCs)
}
getContacts=()=>{
getCs=()=>{
//声明数组
const contactsWithAccount=[]
const contactsWithNoAccount=[]
//从用户的手机获取联系人
Contacts.getAll((错误,Contacts)=>{
如果(错误)抛出错误
//对于每个联系人,迭代
对于(变量i=0;i0){
const phone=项目。电话号码[0]。号码
//如果经过消毒的电话号码与当前用户的电话号码(以DB格式保存)不同,请运行以下逻辑
if(this.state.user.phoneNumbers.nationalFormat!=sanitizeNumber(电话)
&&this.state.user.phoneNumbers.internationalFormat!=sanitizeNumber(电话)
) {
findUserByPhoneNumber(电话,(fu)=>{
contactObject={
关键字:item.recordID,
名称:item.givenName,
normalizedName:item.givenName.toLowerCase(),
电话号码:电话,
用户:this.state.user,
hasAccount:null,
friendId:null,
isFriend:null
}
const foundUser=fu
//如果找到用户,则按入contactsWithAccount,否则按入contactsWithNoAccount
if(foundUser&&foundUser.\u id!=this.state.user.\u id){
contactObject.hasAccount=true
contactObject.friendId=foundUser.\u id
if(this.state.user.friends&&this.state.user.friends.includes(foundUser.\u id)){
contactObject.isFriend=true
}
ContactSwitAccount.push(contactObject)
}
否则{
contactsWithNoAccount.push(contactObject)
}
//如果两个数组已满,请运行回调
//注1:我们使用两个长度+1来说明电流
//我们跳过且不添加到任何数组中的用户文档
//注2:bizare方法是处理结果的唯一方法
//异步传入
if(contactsWithAccount.length+contactsWithNoAccount.length+1==contacts.length){
console.log('finished');
SORTC(contactsWithAccount、contactsWithNoAccount)
}
})
}
}
}
})
}
//按字母顺序对两个数组排序
sortCs=(有账户,无账户)=>{
比较=(a,b)=>{
如果(a.nameb.name)
返回1;
返回0;
}
withAccount.sort(比较)
withNoAccount.sort(比较)
this.setState({withAccount,withNoAccount})
}
//释放怪物
getCs(sortCs)
}
我相信这个过程可以通过各种方式进行优化。也许 吧:


  • 不同的数据库结构
  • 将所有查询捆绑到一个查询中
  • 更好地利用 异步的
  • 在注册流程中的较早步骤启动流程

Whatsapp、HouseParty和其他一些应用程序都有这个功能,可以立即加载。我还没有达到那个完美的程度,但一定有更好的方法…
如果您有任何帮助/建议,我们将不胜感激。

请始终将电话号码存储在经过消毒的国际格式中。1/3的查询。@JamesPoag对此我想了想,但问题是电话号码通常以国家格式保存在用户的通讯簿中,但并不总是。。。电话号码真的很棘手:/I我正在考虑将这两种格式存储在一个数组中,然后使用
where('phoneNumbers','array includes',number)进行查询
如果一种格式与提供的号码匹配,它将返回用户文档。请进行一些分析,以了解哪个部分需要很长时间,以便我们可以关注该部分以任何格式从联系人处获取号码->解析为净化的国际格式->存储在firebase->每个号码查询一次