如何查询数千个值并仅返回数组中的值 - Firebase Swift

时间:2017-12-31 01:41:53

标签: swift firebase firebase-realtime-database

在firebase实时数据库中,我有这样的数据:

"users" : {
"37KfZKDwrieIEKI9juAC4Xm8aPi1" : {
  "isConnected" : false,
  "isGuestUser" : false,
  "lastOnline" : 1510250272022,
  "profilePicture" : "5a039030515e78653148",
  "userID" : "37KfZKDwrieIEKI9juAC4Xm8aPi1",
  "username" : "adsfasdfasdf"
},
"4D1GNiRH5NeRxpmTNg6JhJ3iTck1" : {
  "isConnected" : false,
  "isGuestUser" : true,
  "lastOnline" : 1510077502788,
  "profilePicture" : "5a01f2648278b6652011",
  "userID" : "4D1GNiRH5NeRxpmTNg6JhJ3iTck1",
  "username" : "ihoho"
},
"5UA3INZ7i0dnNtgX0ai5ABhjxh43" : {
  "isConnected" : false,
  "isGuestUser" : true,
  "lastOnline" : 1512610102474,
  "profilePicture" : "5a14df775a34f2388873",
  "userID" : "5UA3INZ7i0dnNtgX0ai5ABhjxh43",
  "username" : "jlkjlkjlkj"
},...

我正在使用外部API返回一个看起来像这样的Json

"candidates" : [
    {
      "enrollment_timestamp" : "1510182689539",
      "subject_id" : "37KfZKDwrieIEKI9juAC4Xm8aPi1",
    },
    {
      "enrollment_timestamp" : "1513557650425",
      "subject_id" : "CKUVZ7XtY9VKJakn1lBV7MVW1702",
    },
    {
      "enrollment_timestamp" : "1507578748901",
      "subject_id" : "l7VDdtGFpMe8BRbrlCyAciTvONk1",
    },...

最终目标是让来自外部api的json中的所有用户都在线。这需要聆听“isConnected”#39;每个用户的端点,并确定它是真还是假。

现在使用firebase和我当前的数据结构是不可能的,因为首先firebase不支持多个查询参数,所以我无法做到userID = subjectID&&其中isConnected == true,其次,更重要的是,firebase不允许我做相当于WHERE IN,即将userID映射到提供客户端的潜在userID数组(fyi subjectID === userID)

所以我所做的就像重新构建我的数据一样;

"onlineUsers" : {
"Aze1x7jTZIbPyyPmlUYWVdEyAd73" : true,
"CQdmMxkmqBerRGOQpFblx7SO4D33" : true,
"EptMK62Kt1Sp1EIb5meuKHVpUqs1" : true,
"J2X65KauDlSvkN4Yp5V4IF0sTnx1" : true,
"KnYEqsekY9YXV3ayOx082xw8VQX2" : true,
"aGLrKH31YvRKrB8KYQmZ4sA122m1" : true,
"ab3JZyw9YMdpW3mXkZc2BjYkxej2" : true,
"gpQic1EzSnXL9x5DhaoXxcWrGF22" : true,
"qQaBPMaDOrXrWddsijfMJWusuGG3" : true,
"tDWEUoKS4mUdQ1bWeiLTlhwCSoD3" : true

},

现在我需要做的就是让所有在外部api json中的在线用户查询onlineUsers,并通过密钥检查条件参数。这样我知道如果结果为null,则用户不在线:

    static func queryDatabase(child: String, queryEqual: String, keyOf: Int, completionHandler: @escaping (_ return: AnyObject?, _ error: String?) -> Void){
    print("querying db with child ", child)

    let ref = databaseReference.child(child).queryOrderedByKey().queryEqual(toValue: queryEqual)

     ref.observe(.value, with:{ (snapshot: DataSnapshot) in
        print("subjectID: ", queryEqual, "at key ", keyOf)
        print("queryResult", snapshot)

        if let value =  (snapshot.value as? [String: AnyObject])?[queryEqual] {
            print("unwrapped snapshot dict value from key: ", value)

            completionHandler(value,  nil)

        }else{
            print("no value for key \(queryEqual) so setting return as nil")

            completionHandler(nil,  nil)

        }



    }){ (error) in
        print(error.localizedDescription)

        completionHandler(nil,  error.localizedDescription )


    }

}

通过循环遍历外部api json,并在每次迭代时调用此函数^^,可以将此函数称为simple函数,如:

      for (key, valueSubjectID) in arrayOfOrderedMatches.enumerated(){
         //call function queryDatabase here

    queryDatabase(child: "onlineUsers", queryEqual: valueSubjectID, keyOf: key, completionHandler:{ (response, error) in


//it would return the user if they were online here


)}
    }

现在。这有效。在浏览互联网并考虑所有可能性之后,这是我能想到的最有效的方法。但是,现在出现了一个大问题:来自api的json可能会成千上万的用户长。这意味着将连接数千名听众,以检查每个用户是否在线。正如Firebase开发人员告诉我的那样,这并不理想。我不应该附上成千上万的听众。此外,出于某种原因,听众停止在3500左右添加,我猜测,因为它的错误。如果查询返回null(即离线),由于缓存离线持久性的工作方式,即使我不能认为这样可以解决问题,我也无法删除侦听器。有什么办法可以解决这个问题吗?

1 个答案:

答案 0 :(得分:2)

让我重申一下目标。

  

该应用程序提供了大量用户ID(作为JSON),目标是了解哪些用户在线。

以下是两个选项:

我们从用户节点开始

users
  uid_0
    name: "Frank"
    online: true
  uid_1
    name: "Jeff"
    online: false

当收到我们要检查的用户的JSON时,我们将其转换为数组以便于访问。

然后,我们有几个选项

1)为每个uid迭代数组和observeSingleEvent以获得它的当前状态。这不是查询,因为我们可以直接访问uid,因此与查询相比它会相当轻量级。*

let arrayToTest = [String]() //the array of uid's to test
for uid in arrayToTest {
    let thisUserRef = self.ref.child("users").child(uid)
    thisUserRef.observeSingleEvent(of: .value, with: { snapshot in
        let dict = snapshot.value as! [String: Any]
        let status = dict["online"] as! Bool
        print("uid: \(uid) online status is: \(status)")
    })
}

2)让Firebase通过向用户节点添加.childAdded事件来完成繁重工作,该节点将一次迭代所有用户。

将快照中的uid与数组中的uid进行比较,如果匹配,则获取它的状态(on / offline),如果数组中不存在,则忽略它。

let usersRef = self.ref.child("users")
let arrayToTest = [String]() //the array of uid's to test
usersRef.observe(.childAdded, with: { snapshot in
    let dict = snapshot.value as! [String: Any]
    let uid = dict["user_id"] as! String
    if arrayToTest.contains(uid) {
        let status = dict["online"] as! Bool
        print("uid: \(uid) online status is: \(status)")
    }
})

选项2)通常会更好,因为它一次只加载一个并且Firebase正在完成所有工作 - 我们只是检查快照中的uid是否存在于代码中。

3)

作为第三种选择,我们可以利用查询。请注意,在前两个选项中,我们使用轻量级观察函数检索数据。相比之下,查询更重,并且利用更多资源 - 但是,快照结果仅限于那些登录的用户,如果有大量用户可能是最佳选择。

let usersRef = self.ref.child("users")
let queryRef = usersRef.queryOrdered(byChild: "online").queryEqual(toValue: true)
let arrayToTest = [String]() //the array of uid's to test
queryRef.observe(.childAdded, with: { snapshot in
    let dict = snapshot.value as! [String: Any]
    let uid = dict["user_id"] as! String
    if arrayToTest.contains(uid) {
        print("uid: \(uid) online status is: true")
    }
})

* Firebaser将对1)皱眉,因为他们强烈建议不要在紧密循环中进行观察。所以2)如果你想确认每个用户都存在,3)你是否只对在线用户感兴趣。