在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(即离线),由于缓存离线持久性的工作方式,即使我不能认为这样可以解决问题,我也无法删除侦听器。有什么办法可以解决这个问题吗?
答案 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)你是否只对在线用户感兴趣。