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

时间:2018-09-03 17:23:49

标签: firebase optimization google-cloud-firestore query-performance

我正在开发一个具有Firebase后端的应用程序。在注册过程中,我想让新用户看到他们中的哪些联系人已将其添加为应用程序。因此,基本上,请使用电话号码将用户与联系人匹配。

查询数据库以查找用户时,我的性能非常头痛。由于Firestore不支持OR查询,因此我针对每个电话号码运行两个查询(一个查询国家/地区格式,另一个查询国际格式),如果有返回文档,请将该文档设置为找到的用户:

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)
}

我确信该过程可以通过各种方式进行优化。也许:

  • 不同的数据库结构
  • 将所有查询捆绑为一个
  • 更好地使用 异步
  • 在注册流程中的较早步骤开始该过程

Whatsapp,HouseParty和许多其他应用程序都具有此功能,并且可以立即加载。我并没有试图达到完美的水平,但是必须有一些更好的方法……任何帮助/建议都将不胜感激。

0 个答案:

没有答案