【问题标题】:Firebase child query coming up nil after deleting a key then adding it backFirebase子查询在删除一个键然后添加回来后出现零
【发布时间】:2019-09-17 10:17:58
【问题描述】:

我运行下面的代码并使用打印语句数据库说孩子是null

但在数据库中肯定有一些东西:

我唯一能想到的就是让用户删除一条消息(通过删除-LoyeLv... 键),但如果他们想将其添加回来,他们可以。我保留了已删除密钥的副本,然后将其发送回数据库,以便消息仍然同步。唯一的问题是即使我不这样做(如在我的示例中)并且我删除了消息,回来并使用全新的密钥创建一个全新的消息,它仍然显示为空?

即使那里有孩子,这怎么可能?顺便说一句,这是我第一次遇到这种情况。

Database.database().reference()
            .child("favorite")
            .child(uid) // TKAETTLWAuREMZXCvrVeZd8yIPw2
            .queryOrderedByKey()
            .queryLimited(toLast: 10)
            .observeSingleEvent(of: .value, with: { [weak self](snapshot) in

                print(snapshot.key) // prints TKAETTLWAuREMZXCvrVeZd8yIPw2
                print(snapshot.value) // prints null

                if snapshot.hasChildren() {
                    print("yay")
                } else {
                    print("nay") // prints nay
                }

                // doesn't go past here
                guard let children = snapshot.children.allObjects.first as? DataSnapshot else { return }

更新我刚刚在下面尝试过,它工作正常,但上面仍然不起作用:

Database.database().reference()
            .child("favorite")
            .child(uid) // prints TKAETTLWAuREMZXCvrVeZd8yIPw2
            .observeSingleEvent(of: .value, with: { [weak self](snapshot) in

                print(snapshot.key) // prints TKAETTLWAuREMZXCvrVeZd8yIPw2
                print(snapshot.value) // prints the json data

                if snapshot.hasChildren() {
                    print("yay") // prints nay
                } else {
                    print("nay")
                }

我想删除该引用处的密钥而不是添加相同的密钥而不是再次删除它而不是添加一个全新的密钥会以某种方式抛出数据库?

再次更新我没有使用 .queryOrderedByKey() 而是将其更改为使用 .queryOrdered(byChild: "timeStamp") 并且它工作正常:

Database.database().reference()
            .child("favorite")
            .child(uid) // TKAETTLWAuREMZXCvrVeZd8yIPw2
            .queryOrdered(byChild: "timeStamp")
            .queryLimited(toLast: 10)
            .observeSingleEvent(of: .value, with: { [weak self](snapshot) in

当删除一个键,然后将相同的键添加回数据库,然后通过queryOrderedByKey() 查询时,一定会导致某种内部问题,不仅会弄乱键,还会弄乱整个引用。我认为在我删除密钥之后会发生什么,无论内部跟踪机制将其从系统中擦除。当我重新添加它时,它不再是同一个键,而是一个普通的String,没有内部含义,这就是为什么说null。我要求它查询一些没有意义的东西。这是一个疯狂的猜测。

但我想知道为什么从未删除并重新添加到同一个引用的全新密钥会出现同样的问题?

这是@FrankvanPuffelen 在 cmets 中请求的代码:

这是发送/删除数据的vc。

1- sendDataToFirebase()
2- deleteDataFromFirebase()
3- sendDataToFirebase()

按这个顺序做

var someOtherId = "12345" // this id has nothing to do with firebase and might be something like "x778-248-000"

var snapKey: String?
var originalTimeStamp: Double?

func sendDataToFirebase() {

    guard let uid = Auth.auth().currentUser?.uid else { return }
    guard let fbKey = Database.database().reference().child("favorite").childByAutoId().key else { return }

    let timeStamp = Date().timeIntervalSince1970

    var favoriteDict = [String: Any]()
    favoriteDict.updateValue(uid, forKey: "uid")
    favoriteDict.updateValue(originalTimeStamp ?? timeStamp, forKey: "timeStamp")

    var whichKeyToUse: String?

    if let snapKey = self.snapKey {

          whichKeyToUse = snapKey
    } else {

          whichKeyToUse = fbKey
    }  

    var carDict = [String: Any]()
    carDict.updateValue(originalTimeStamp ?? timeStamp, forKey: whichKeyToUse!)

    let favoriteRef = "favorite/\(uid)/\(whichKeyToUse!)"

    let carRef = "carType/\(someOtherId)/\(uid)"

    var multiLocationDict = [String: Any]()
    multiLocationDict.updateValue(favoriteDict, forKey: favoriteRef)
    multiLocationDict.updateValue(carDict, forKey: carRef)

    Database.database().reference().updateChildValues(multiLocationDict, withCompletionBlock: { (error, ref) in

           if self.snapKey == nil {

               self.snapKey = fbKey
           }

           if self.originalTimeStamp == nil {

               self.originalTimeStamp = timeStamp
           }

            // if no error this 100% saves it the way it's supposed to
    })
}

func deleteDataFromFirebase() {

    guard let uid = Auth.auth().currentUser?.uid else { return }
    guard let snapKey = self.snapKey else { return }

    let favoriteRef = "favorite/\(uid)/\(snapKey)"

    let carRef = "carType/\(someOtherId)/\(uid)"

    var multiLocationDict = [String: Any]()
    multiLocationDict.updateValue(NSNull(), forKey: favoriteRef)
    multiLocationDict.updateValue(NSNull(), forKey: carRef)

    Database.database().reference().updateChildValues(multiLocationDict, withCompletionBlock: { [weak self](error, ref) in

            // if no error this 100% deletes what it's supposed to
    })
}

2。这是读取数据的vc,这是一个完全不同的vc,问题出在哪里

func firstCheckIfCurrentUserExistsAtFavoriteRef() {

    guard let uid = Auth.auth().currentUser?.uid else { return }

    let favoriteRef = Database.database().reference()
        .child("favorite")
        .child(uid)
    favoriteRef.observeSingleEvent(of: .value) { [weak self](snapshot) in

        if !snapshot.exists() {
            return
        }

        print(snapshot.key) // prints the key
        print(snapshot.value) // *** the value prints fine here but in handlePagination it prints null ***

        if snapshot.hasChildren() {
            print("yay") // prints yay
        } else {
            print("nay")
        }

        self?.handlePagination(with: uid)
    }
}

var startKey: String?
func handlePagination(with currentUserId: String) {

    if startKey == nil {

        Database.database().reference()
            .child("favorite")
            .child(currentUserId)
            .queryOrderedByKey() // it works fine with this >>> .queryOrdered(byChild: "timeStamp")
            .queryLimited(toLast: 10)
            .observeSingleEvent(of: .value, with: { [weak self](snapshot) in

                print(snapshot.key) // prints the uid
                print(snapshot.value) // *** prints null but in firstCheckIfCurrentUserExistsAtFavoriteRef() it prints fine ***

                if snapshot.hasChildren() {
                    print("yay")
                } else {
                    print("nay") // prints nay
                }

                guard let children = snapshot.children.allObjects.first as? DataSnapshot else { return }

                // ...

            })

    } else {

        // it never makes it this far but it's the same code from above
        Database.database().reference()
            .child("favorite")
            .child(currentUserId)
            .queryOrderedByKey() // it works fine with this >>> .queryOrdered(byChild: "timeStamp")
            .queryEnding(atValue: startKey!)
            .queryLimited(toLast: 11)
            .observeSingleEvent(of: .value, with: { [weak self](snapshot) in

}

我打电话给firstCheckIfCurrentUserExistsAtFavoriteRef()viewDidLoad

【问题讨论】:

  • 这个描述听起来不像是我从实时数据库中看到的行为。你能展示一个任何人都可以运行来重现问题的sn-p代码吗?这可能应该包括删除/写入节点的代码,因为这些似乎是您问题的关键。
  • @FrankvanPuffelen 我添加了整个代码。并且所有内容都会被添加和删除,而正确的参考文献没有问题。除了问题中提出的奇怪问题之外,它完全按照我的预期工作。
  • 您在任何地方都在使用observeSingleEvent,这意味着听众不会保持活跃。你在哪里调用这些方法。另请注意,您现在共享了三个侦听器,而不仅仅是之前的一个。有没有办法construct a single code block that someone can run end to end to (reasonably reliably) reproduce the issue
  • 我在 viewDidLoad 中调用 firstCheckIfCurrentUserExistsAtFavoriteRef。除此之外,我的代码在其他 vcs 中工作的问题。当我分页时,一切正常。这是用户在历史记录中检查的数据,因此无需使用 .observe,因为不会出现新数据。它只是在滚动时分页。
  • 您是否启用了持久性?

标签: ios swift firebase firebase-realtime-database snapshot


【解决方案1】:

我之前在启用持久性的情况下看到过这个问题 - 问题是数据部分同步但不完全同步。

如需快速测试,请尝试关闭持久性并重新运行查询。

Database.database().isPersistenceEnabled = false

这显然不是一个长期的解决方案,只是为了测试。

如果您想使用持久性,您还需要确保数据保持最新并且始终保持同步。这是代码

let postsRef = Database.database().reference(withPath: "posts")
postsRef.keepSynced(true)

Firebase 实时数据库客户端会自动下载数据 在这些位置并保持同步,即使参考没有 活跃的听众。您可以使用 以下代码行

postsRef.keepSynced(false)

【讨论】:

  • 感谢杰,一旦我关闭它,问题就消失了。干杯!
  • 嘿,关于您给出的持久性答案,我有几个问题。 1. 你在哪里调用 .keepSynced(false) -in viewWillDisappear?因为在分页时,我只有你 observSingleEvent,如果用户切换选项卡并返回,我需要重新开启同步。 2. 为什么不使用 .referehce(withPath: “xyz”) 而不是 .child(“xyz”)。 3.持久性是否下载整个参考?如果您在所有 singleEvent refs 上使用它,这似乎在金钱上很昂贵,并且它违背了分页的目的。
  • 如果你有时间看一下,这些家伙说 keepSynced(true) 大大提高了他们的费用并建议不要使用它:pamartinezandres.com/…
  • 让我来解决问题。 1) 你会一遍又一遍地看到 firebases 说 磁盘持久性和一次性/单值事件侦听器只是不混合。 所以不要那样做。从@frankvanpuffelen 阅读[此答案]。 2) 因为文档有些断断续续,但官方 API 调用 .child 3) 在那个位置是的,所以您需要在同步的内容中有所选择,但需要避免用户可能正在寻找陈旧数据的情况.
  • 3) 继续。更高的货币成本是正确的。但是,在您的情况下,应用程序 需要 新鲜数据,因此别无选择。存在替代方案,例如当应用程序离线时不进行查询或由于数据陈旧而导致结果为零。我使用的天气应用程序就是这样做的——当我在没有互联网的情况下尝试获取一个城市的天气时,它只是说“没有互联网连接——一旦建立连接就会刷新。见How Persistence Works
猜你喜欢
  • 2017-06-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-06-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多