【问题标题】:Query multiple keys on Firebase在 Firebase 上查询多个键
【发布时间】:2016-10-09 12:56:33
【问题描述】:

我正在遵循 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 结构是否错误并缺少一些额外的键?

谢谢

【问题讨论】:

  • “此外,由于连接不良,某些请求可能会失败”所有请求都通过同一连接,该连接由 Firebase SDK 自动维护。有关这方面的更多信息,请参阅stackoverflow.com/questions/35931526/…
  • 这很好,至少有一件事不是问题。尽管如此,这并没有改变必须有更好的方法来过滤数据的事实。鉴于 Firebase 的群聊示例,在指南中,一个用户可以是多个聊天室的一部分,一个聊天室可以有多个成员。如何获取(列出)给定用户的所有聊天室,因为您在用户对象中可以访问的只是聊天室 ID 列表?谢谢
  • 您通常会在组和用户中保留索引。因此,对于每个组,您都会保留该组中的用户列表。对于每个用户,您都会保留他们所在组的列表。请参阅firebase.google.com/docs/database/ios/structure-data#fanout
  • @FrankvanPuffelen,是的,但是有了这些数据,您如何使用组 ID 列表执行查询?假设我是组 id1、id2、id3 的用户。如何使用此 ID 列表来获取实际的组信息(名称、日期等)。感谢您的帮助,我真的很困惑,如果您认为它更有效,您可以通过 guime84 与我联系
  • @FrankvanPuffelen, fanout vs loop through,如果您正在开发 Guilherme 描述的相同功能,您有什么建议?我对fanout 中的数据重复不满意。

标签: ios swift firebase firebase-realtime-database


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

【讨论】:

  • 谢谢!很高兴我能帮上忙
  • @Guilherme Sprint - 我建议将此标记为正确答案
【解决方案2】:

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

例如,在您的情况下:-

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

【讨论】:

  • 是的,这就是我最终所做的。但是,最后我经历了 Firebase 数据库的缓慢响应,所以我要远离它。有替代品会更好。
  • 好吧,在我看来,如果您想让它与单个侦听器一起工作,您可以使用冗余与速度权衡。如中,您可以在用户对象中复制专辑数据。这样你的查询不会很慢。
【解决方案3】:

我是 iOS 和 firebase 的新手,所以请谨慎对待我的解决方案。 它是一种解决方法,它可能不是密封的。

如果我正确理解了您的问题,我将面临类似的问题。 你有“用户”和“专辑”。我有“用户”和“globalWalls”。 在“用户”中,我有“wallsBuiltByUser”,其中包含 globalWalls 的键。

我想遍历 wallBuiltByUser 并从 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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2018-06-11
    • 2017-10-13
    • 1970-01-01
    • 1970-01-01
    • 2018-05-18
    • 1970-01-01
    • 2018-10-10
    • 1970-01-01
    相关资源
    最近更新 更多