在 Firebase 上查询多个键
Query multiple keys on Firebase
我正在遵循 Firebase 的扁平化数据建议,但我无法从我的数据库中列出一系列项目。
这是我的数据库文件的示例:
"users" : {
"UID12349USER" : {
"firstName" : "Jon",
"lastName" : "Snow",
"email" : "jonsnow@winterfell.com",
"albums" : {
"UID124ALBUM" : true,
"UID125ALBUM" : true
}
}
},
"albums" : {
"UID124ALBUM" : {
"name" : "My Artwork",
},
"UID125ALBUM" : {
"name" : "My Sketches",
}
}
我正在检索给定用户的相册列表:
let userAlbums = database.child(usersKey).child(user.uid).child(albumsKey)
userAlbums.observeSingleEventOfType(.Value, withBlock: { snapshot in
// fetch [UID124ALBUM: 1, UID125ALBUM: 1]
})
现在我希望我可以在一次查询中检索所有用户的相册。
我可以做一批查询,并填充一个异步数组,但这对我来说似乎不是一个好方法...
for key in albumKeys {
let album = database.child(self.albumsKey).child(key)
album.observeSingleEventOfType(.Value, withBlock: { snapshot in
// fetch album.. append to array
})
}
由于请求的异步性质,使用该方法很难检测查询何时完成。此外,由于连接不良,某些请求可能会失败。
此外,如果我想过滤具有给定名称(例如 "My Artwork")或 return nil(如果它不存在)的专辑之一,我也会以一个棘手的结局告终条件。
var found = false
for key in albumKeys {
let album = database.child(self.albumsKey).child(key)
album.observeSingleEventOfType(.Value, withBlock: { snapshot in
// if current.name == "My Artwork"
// completion(current)
})
}
// This block will be called before observeSingleEventOfType =.=
if !found {
completion(nil)
}
我在 iOS 和 Swift 方面有很好的背景,但我知道 Firebase 和 NoSQL 数据库。有人能给我指出一个好的方向吗?我应该放弃 Firebase 并尝试其他方法吗?我是否缺少一些可以查询我需要的方法?我的 json 结构是否错误并且缺少一些额外的键?
谢谢
在您的情况下,唯一可能的解决方法是从侦听器内部调用另一个侦听器。这样,您就不需要单独处理请求的异步性质。
例如,在您的情况下:-
let userAlbums = database.child(usersKey).child(user.uid).child(albumsKey)
userAlbums.observeSingleEventOfType(.Value, withBlock: { snapshot in
if(snapshot.exists()) {
// fetch [UID124ALBUM: 1, UID125ALBUM: 1] in the albumKeys
for key in albumKeys {
let album = database.child(self.albumsKey).child(key)
album.observeSingleEventOfType(.Value, withBlock: { snapshot in
// fetch album.. append to array
})
}
}
})
现在,要过滤您的一个相册,您可以在一个函数中使用这样的完成:
var found = false
let userAlbums = database.child(usersKey).child(user.uid).child(albumsKey)
userAlbums.observeSingleEventOfType(.Value, withBlock: { snapshot in
if(snapshot.exists()) {
// fetch [UID124ALBUM: 1, UID125ALBUM: 1] in the albumKeys
for key in albumKeys {
let album = database.child(self.albumsKey).child(key)
album.observeSingleEventOfType(.Value, withBlock: { snapshot in
// if current.name == "My Artwork"
found = true
completion(current)
})
}
}
})
if !found {
completion(nil)
}
我是 iOS 和 firebase 的新手,所以对我的解决方案持保留态度。
这是一种变通方法,它可能不是密不透风的。
如果我没有正确理解你的问题,那么我也面临着类似的问题。
您有 "users" 和 "albums"。我有 "users" 和 "globalWalls"。
在 "users" 里面我有 "wallsBuiltByUser" 里面有 globalWalls 的键。
我想遍历 wallsBuiltByUser 并从 globalWalls 中检索它们对应的节点。最后,我需要打电话给我的代表,通知他们已经取回了墙。
我相信这可能与您正在尝试做的类似。
这是我的解决方案:
databaseRef.child("users/\(userID)/WallsBuiltByUser/").observeSingleEvent(of: FIRDataEventType.value, with: { (snapshot:FIRDataSnapshot) in
let numOfWalls = Int(snapshot.childrenCount)
var wallNum:Int = 0
for child in snapshot.children {
let wallId = (child as! FIRDataSnapshot).key
self.databaseRef.child("globalWalls").child(wallId).observeSingleEvent(of: .value, with: { (snapshot: FIRDataSnapshot) in
wallNum = wallNum + 1
let wallServerInfo = snapshot.value as? NSDictionary!
if (wallServerInfo != nil){
let wall = Wall(wallInfo: wallServerInfo)
self.returnedWalls.append(wall)
}
if(wallNum == numOfWalls){
print("this should be printed last")
self.delegate?.retrieved(walls: self.returnedWalls)
}
})//end of query of globalWalls
}//end of for loop
})//end of query for user's walls
我建议使用 DispatchGroup
和互斥来处理 for 循环中的异步函数。这是您提供的带有 DispatchGroup
的代码,以确保在检查 if 语句之前循环中的所有异步函数都已完成:
let myGroup = DispatchGroup()
var found = false
// iterate through your array
for key in albumKeys {
let album = database.child(self.albumsKey).child(key)
// lock the group
myGroup.enter()
album.observeSingleEventOfType(.Value, withBlock: { snapshot in
if current.name == "My Artwork" {
found = true
}
// after the async work has been completed, unlock the group
myGroup.leave()
})
}
// This block will be called after the final myGroup.leave() of the looped async functions complete
myGroup.notify(queue: .main) {
if !found {
completion(nil)
}
}
在 myGroup.enter()
和 myGroup.leave()
被调用相同次数之前,myGroup.notify(queue: .main) {
代码块中包含的任何内容都不会执行。请务必在 Firebase 观察块中调用 myGroup.leave()
(在异步工作之后),并确保即使观察产生错误也会调用它。
我正在遵循 Firebase 的扁平化数据建议,但我无法从我的数据库中列出一系列项目。
这是我的数据库文件的示例:
"users" : {
"UID12349USER" : {
"firstName" : "Jon",
"lastName" : "Snow",
"email" : "jonsnow@winterfell.com",
"albums" : {
"UID124ALBUM" : true,
"UID125ALBUM" : true
}
}
},
"albums" : {
"UID124ALBUM" : {
"name" : "My Artwork",
},
"UID125ALBUM" : {
"name" : "My Sketches",
}
}
我正在检索给定用户的相册列表:
let userAlbums = database.child(usersKey).child(user.uid).child(albumsKey)
userAlbums.observeSingleEventOfType(.Value, withBlock: { snapshot in
// fetch [UID124ALBUM: 1, UID125ALBUM: 1]
})
现在我希望我可以在一次查询中检索所有用户的相册。 我可以做一批查询,并填充一个异步数组,但这对我来说似乎不是一个好方法...
for key in albumKeys {
let album = database.child(self.albumsKey).child(key)
album.observeSingleEventOfType(.Value, withBlock: { snapshot in
// fetch album.. append to array
})
}
由于请求的异步性质,使用该方法很难检测查询何时完成。此外,由于连接不良,某些请求可能会失败。
此外,如果我想过滤具有给定名称(例如 "My Artwork")或 return nil(如果它不存在)的专辑之一,我也会以一个棘手的结局告终条件。
var found = false
for key in albumKeys {
let album = database.child(self.albumsKey).child(key)
album.observeSingleEventOfType(.Value, withBlock: { snapshot in
// if current.name == "My Artwork"
// completion(current)
})
}
// This block will be called before observeSingleEventOfType =.=
if !found {
completion(nil)
}
我在 iOS 和 Swift 方面有很好的背景,但我知道 Firebase 和 NoSQL 数据库。有人能给我指出一个好的方向吗?我应该放弃 Firebase 并尝试其他方法吗?我是否缺少一些可以查询我需要的方法?我的 json 结构是否错误并且缺少一些额外的键?
谢谢
在您的情况下,唯一可能的解决方法是从侦听器内部调用另一个侦听器。这样,您就不需要单独处理请求的异步性质。
例如,在您的情况下:-
let userAlbums = database.child(usersKey).child(user.uid).child(albumsKey)
userAlbums.observeSingleEventOfType(.Value, withBlock: { snapshot in
if(snapshot.exists()) {
// fetch [UID124ALBUM: 1, UID125ALBUM: 1] in the albumKeys
for key in albumKeys {
let album = database.child(self.albumsKey).child(key)
album.observeSingleEventOfType(.Value, withBlock: { snapshot in
// fetch album.. append to array
})
}
}
})
现在,要过滤您的一个相册,您可以在一个函数中使用这样的完成:
var found = false
let userAlbums = database.child(usersKey).child(user.uid).child(albumsKey)
userAlbums.observeSingleEventOfType(.Value, withBlock: { snapshot in
if(snapshot.exists()) {
// fetch [UID124ALBUM: 1, UID125ALBUM: 1] in the albumKeys
for key in albumKeys {
let album = database.child(self.albumsKey).child(key)
album.observeSingleEventOfType(.Value, withBlock: { snapshot in
// if current.name == "My Artwork"
found = true
completion(current)
})
}
}
})
if !found {
completion(nil)
}
我是 iOS 和 firebase 的新手,所以对我的解决方案持保留态度。 这是一种变通方法,它可能不是密不透风的。
如果我没有正确理解你的问题,那么我也面临着类似的问题。 您有 "users" 和 "albums"。我有 "users" 和 "globalWalls"。 在 "users" 里面我有 "wallsBuiltByUser" 里面有 globalWalls 的键。
我想遍历 wallsBuiltByUser 并从 globalWalls 中检索它们对应的节点。最后,我需要打电话给我的代表,通知他们已经取回了墙。
我相信这可能与您正在尝试做的类似。
这是我的解决方案:
databaseRef.child("users/\(userID)/WallsBuiltByUser/").observeSingleEvent(of: FIRDataEventType.value, with: { (snapshot:FIRDataSnapshot) in
let numOfWalls = Int(snapshot.childrenCount)
var wallNum:Int = 0
for child in snapshot.children {
let wallId = (child as! FIRDataSnapshot).key
self.databaseRef.child("globalWalls").child(wallId).observeSingleEvent(of: .value, with: { (snapshot: FIRDataSnapshot) in
wallNum = wallNum + 1
let wallServerInfo = snapshot.value as? NSDictionary!
if (wallServerInfo != nil){
let wall = Wall(wallInfo: wallServerInfo)
self.returnedWalls.append(wall)
}
if(wallNum == numOfWalls){
print("this should be printed last")
self.delegate?.retrieved(walls: self.returnedWalls)
}
})//end of query of globalWalls
}//end of for loop
})//end of query for user's walls
我建议使用 DispatchGroup
和互斥来处理 for 循环中的异步函数。这是您提供的带有 DispatchGroup
的代码,以确保在检查 if 语句之前循环中的所有异步函数都已完成:
let myGroup = DispatchGroup()
var found = false
// iterate through your array
for key in albumKeys {
let album = database.child(self.albumsKey).child(key)
// lock the group
myGroup.enter()
album.observeSingleEventOfType(.Value, withBlock: { snapshot in
if current.name == "My Artwork" {
found = true
}
// after the async work has been completed, unlock the group
myGroup.leave()
})
}
// This block will be called after the final myGroup.leave() of the looped async functions complete
myGroup.notify(queue: .main) {
if !found {
completion(nil)
}
}
在 myGroup.enter()
和 myGroup.leave()
被调用相同次数之前,myGroup.notify(queue: .main) {
代码块中包含的任何内容都不会执行。请务必在 Firebase 观察块中调用 myGroup.leave()
(在异步工作之后),并确保即使观察产生错误也会调用它。