查询Firebase上的多个密钥

时间:2016-06-09 03:38:59

标签: ios swift firebase firebase-realtime-database

我遵循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 个答案:

答案 0 :(得分:1)

我建议使用*和互斥来处理for循环中的异步函数。这是您为DispatchGroup提供的代码,以确保循环中的所有异步函数在检查if语句之前都已完成:

DispatchGroup

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()代码块中包含的所有内容都不会执行。确保在Firebase观察块中调用myGroup.leave()(在异步工作之后),并确保即使观察产生错误,也要调用它。

答案 1 :(得分: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)
 }

答案 2 :(得分:0)

我对iOS和firebase都很陌生,所以请尽量使用我的解决方案。 它是一种解决方法,它可能不是密不透风的。

如果我理解你的问题,我就会面临类似的问题。 你有"用户"和#34;专辑"。我有"用户"和" 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