【发布时间】:2018-06-10 21:07:28
【问题描述】:
在 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",
},...
最终目标是从 json 中获取所有在线的外部 api 用户。这需要监听每个用户的“isConnected”端点,并确定它是真还是假。
现在使用 firebase 和我当前的数据结构这是不可能的,因为首先 firebase 不支持多个查询参数,所以我不能做 where userID = subjectID && where isConnected == true,其次,更重要的是,firebase 不允许我做相当于 WHERE IN,即将用户 ID 映射到客户端提供的潜在用户 ID 数组(仅供参考 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 并通过键检查条件参数。这样我就知道结果是否为空,用户不在线:
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 调用这个函数很简单,每次迭代调用这个函数 ^^,类似于:
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 (即脱机),我无法删除侦听器,因为缓存脱机持久性的工作方式,即使我可以,我也不认为这会解决问题。有什么办法可以解决这个问题吗?
【问题讨论】:
-
信息量很大,但不清楚为什么要附加成千上万的听众。看来您有一个已连接用户的节点;您可以通过.value 在该节点上观察SingleEvent,将键放在一个数组中并遍历每个子节点并从外部源中提取您需要的任何数据吗?不需要听众。也许你可以缩短(见MCV)并澄清问题?
-
observeSingleEvent 太可怕了,永远不应该使用。它只监听缓存,然后分离自身,因此它永远不会获取最新的服务器值,这对于检测用户是否在线至关重要。这个问题中的所有信息对于理解问题都是必要的。此外,正如我所解释的,外部源返回一次性 json,这意味着我不能只查询我想要的数据。无论如何,由于 api 的性质,json 将包含数千个结果。 json 也告诉我需要查询哪些用户,而不是反过来。
-
哇。哈哈。这确实是错误的信息,我不确定你从哪里得到的。 ObserveSingleEvent 专门用于读取一次值而不离开观察者。请参阅 Firebase 文档read data once。 它只监听缓存 也是不正确的,它绝对从服务器获取当前值(再次阅读文档)。您是说您的应用收到一个 JSON 节点 candidates 并且您想知道这些特定用户是否在线并且他们的状态是否存储在 Firebase 中?
-
我已经非常彻底地阅读了文档,并且花了很多天试图弄清楚观察和听众是如何工作的。文档没有解释这一点,但这是一个一致的结果,您可以测试:如果该值之前已从服务器读取,它将存储在缓存中,因此下次您观察SingleEvent 时,它将从缓存中获取。如果缓存中没有值,则observeSingleEvent 将从服务器获取数据。但这不能用于检查用户是否在线,我们需要始终获取服务器值。
-
如果你附加一个监听器,你可以看看你是否打印出返回的数据,它总是会立即返回2个结果,一个来自缓存,一个来自服务器。这证明,如果有可用的缓存,单次观察只会导致缓存读取。
标签: swift firebase firebase-realtime-database