首页 文章

在Firebase上查询多个键

提问于
浏览
11

我遵循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
    })
}

由于请求的异步性质,使用该方法使得检测查询何时完成变得棘手 . 除此之外,由于连接错误,某些请求可能会失败 .

另外,如果我想过滤一个具有给定名称的专辑(例如“我的艺术作品”)或者如果它不存在则返回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结构是错误的并且缺少一些额外的键吗?

谢谢

3 回答

  • 1

    我建议使用 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.notify(queue: .main) { 代码块中包含的任何内容都不会执行,直到 myGroup.enter()myGroup.leave() 被调用相同的次数 . 务必在Firebase观察块中调用 myGroup.leave() (在异步工作之后),并确保即使观察产生错误也会调用它 .

  • 0

    在您的情况下,唯一可能的方法是从侦听器内部调用另一个侦听器 . 这样,您就不需要单独处理请求的异步性质 .

    例如,在您的情况下: -

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

    我是iOS和firebase的新手,所以拿出我的解决方案 . 它是一种解决方法,它可能不是密不透风的 .

    如果我正确理解你的问题,我面临着类似的问题 . 你有“用户”和“专辑” . 我有“用户”和“globalWalls” . 在“用户”内部,我有“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
    

相关问题