Firebase - for循环回调中的嵌套observeSingleEvent查询太多时间更新

Posted

技术标签:

【中文标题】Firebase - for循环回调中的嵌套observeSingleEvent查询太多时间更新【英文标题】:Firebase - Nested observeSingleEvent query in for loop callback too many time update 【发布时间】:2018-10-13 23:22:48 【问题描述】:

我在 firebase 实时数据库更新方面面临着严重的回调地狱。

情况: 我有 cmets 节点,它存储所有评论的详细信息,例如属于谁的 userId (uid) 、消息和帖子 id (pid)。请看下图。

我有另一个 post-comment 节点,它在每个 post id 键下存储 cmets 键。请看下图。

最后第三个节点是用户评论,它将所有的 cmets 密钥存储在唯一的用户帐户 id 密钥下。请看下图。

问题:

“写评论”功能一切正常,因为它只是创建一个评论键并将评论数据更新到这些节点。

但是,当用户调用“删除帖子”功能时,将删除所有属于该帖子ID的cmets数据。因此,我有这个代码逻辑循环所有 cmets 数据。重点是首先我必须获取帖子评论快照以限制cmets节点上的查询量(因为cmets节点存储了所有应用程序用户的评论详细数据。在不知道评论数量属于目标帖子的情况下,它需要在整个 cmets 节点上循环,它太重了。)

为了循环post-comment会得到commentKey,然后我可以在cmets节点和post-comment节点上设置Null。

但是问题发生在我需要使用 cmets 节点来找出 userId,以便在用户评论上设置 NSNull。当我在下面调用事件时:

cmetsRef.child((snap as AnyObject).key).observeSingleEvent(of: .value, with: (commentSnapshot) in

)

cmetsRef 回调范围成为另一个线程。因此,如果我在此范围之外调用 rootRef.updateChildValues 并在 for 循环结束(评论后),这只会更新 cmets 节点和评论后节点。用户评论更新数据仍然会在另一个线程上分配 key:value。

updates["user-comment/(userId)/cmets/(commentKey)"] = NSNull()

我必须把 rootRef.updateChildValue 放在

cmetsRef.child((snap as AnyObject).key).observeSingleEvent(of: .value, with: (commentSnapshot) in

...

rootRef.updateChildValues(updates)

)

如果 cmets 超过 10,000 或超过 100 万,此逻辑将导致 updateChildValues 被调用太多次,因为它处于 for 循环中。我使用倒计时方法尝试在 for 循环结束时只调用一次更新。但是commentRef范围内的计数始终为0...我不知道为什么...

请在不更改当前节点结构的情况下帮助我找到更好的解决方案来处理此嵌套的 observeSingleEvent 更新问题。我的目标是只调用一次 rootRef.updateChildValue。

感谢您的帮助。

演示代码:

func deleteAllCommentsRelateTo(postId: String, callback: ((CommentServiceError?) -> Void)?) 
        var error: CommentServiceError?
        guard session.isValid else 
            error = .authenticationNotFound(message: "Authentication not found.")
            callback?(error)
            return
        

        let uid = session.user.id
        let rootRef = Database.database().reference()
        let path1 = "posts/\(postId)/comments_count"
        let path2 = "posts/\(postId)/uid"

        let commentCountRef = rootRef.child(path1)
        let authorRef = rootRef.child(path2)

        authorRef.observeSingleEvent(of: .value, with:  authorSnapshot in
            guard let authorId = authorSnapshot.value as? String else 
                error = .failedToDelete(message: "Author not found")
                callback?(error)
                return
            

            if uid != authorId 
                error = .failedToDelete(message: "User has no permission to delete this post comments")
                callback?(error)
                return
            

            commentCountRef.runTransactionBlock( (data) -> TransactionResult in

                if let _ = data.value as? Int 
                    data.value = 0
                
                return TransactionResult.success(withValue: data)

            )  (err, committed, snapshot) in
                guard err == nil, committed else 
                    error = .failedToDelete(message: "Unable to delete a comment")
                    callback?(error)
                    return
                

                var updates: [AnyHashable: Any] = [:]

                    /**
                     * [CHECKED] Set NSNull() on comments, post-comment, and user-comment nodes.
                     */
                    let commentsRef = rootRef.child("comments")
                    let postCommentRef = rootRef.child("post-comment")
                    let query = postCommentRef.child(postId).child("comments").queryOrderedByKey()

                    query.observeSingleEvent(of: .value, with:  (data) in
                        guard data.hasChildren() else 
                            error = .failedToDelete(message: "No comments data")
                            callback?(error)
                            return
                        

                        var count = data.childrenCount

                        print("post-comment count!!!!!!!: ", data.childrenCount)

                        for snap in data.children 
                            guard let commentKeySnap = snap as? DataSnapshot else 
                                continue
                            

                            count -= 1

                            let commentKey = commentKeySnap.key

                            if count == 0 
                                print("this is totally not right!!!!!")
                            

                            updates["comments/\(commentKey)"] = NSNull()
                            updates["post-comment/\(postId)/comments/\(commentKey)"] = NSNull()

                            commentsRef.child((snap as AnyObject).key).observeSingleEvent(of: .value, with:  (commentSnapshot) in
                                guard let userId = commentSnapshot.childSnapshot(forPath: "uid").value as? String else 
                                    return
                                


                                updates["user-comment/\(userId)/comments/\(commentKey)"] = NSNull()

                                print("In this observeSingleEvent will always be 0 count::::: ", count)

                                if count == 0 
rootRef.updateChildValues(updates, withCompletionBlock:  err, ref in
                                            guard err == nil else 
                                                error = .failedToDelete(message: "Failed to delete comment")
                                                callback?(error)
                                                return
                                            
                                        )

                                    print("deleteAllComments: ", updates)
                                    callback?(nil)
                                
                            )
                            print("count down: ", count)
                        
                    )
                )
            
        )
    

【问题讨论】:

【参考方案1】:

解决方案:

一不小心找到了正确的放置count -= 1的地方。本来我是放在for循环作用域的,但是作用域内的commentRef中count并没有减少。因此,我将 count -= 1 放在 commentRef 范围内,成功计数为零,并且只调用 rootRef.update 一次。

【讨论】:

以上是关于Firebase - for循环回调中的嵌套observeSingleEvent查询太多时间更新的主要内容,如果未能解决你的问题,请参考以下文章

c语言中的循环的嵌套是怎么运行的

无法从嵌套for循环访问父for循环

如何打破 Objective-C 中的两个嵌套 for 循环?

在VB中 for 循环嵌套语句的用法语解释(必须清楚!!)

从嵌套for循环中的指针集中删除项目

Java初学者,比较嵌套for循环中的字符串