Swift - Firebase数据库查询项目范围

时间:2022-09-08 21:24:52

Consider the following database tree:

请考虑以下数据库树:

root: {
    followees: {
        <uid-1> : {
            // list of all users "uid-1" is following (each child node is a different FCM topic)
            <uid-1-1>: true, // true = subscribed to FCM topic
            <uid-1-2>: false,
            // theoretically, "uid-1" could be following millions of users, but 100k is probably a more reasonable maximum
        }
        <uid-2> : {
            // list of all users "uid-2" is following
        }
    } 
}

What would be the most efficient and memory-saving method to iterate all children in subtree?

在子树中迭代所有子节点的最有效和节省内存的方法是什么?

I've created a recursive solution using a limiting query that reads 1000 children at a time. This works, but due to the recursive aspect, all children are stored in memory until the base case it hit; it's essentially the same as loading all children at once.

我使用限制查询创建了一个递归解决方案,一次读取1000个孩子。这是有效的,但由于递归方面的原因,所有子节点都存储在内存中,直到它遇到基本情况为止;它与一次装载所有孩子基本相同。

I've thought of clearing out the collection returned by observeSingleEvent(.value), but that doesn't work because the collection is immutable.

我想过清除observeSingleEvent(.value)返回的集合,但这不起作用,因为集合是不可变的。

The best solution I can think of is to query the database for the first 1000 children, then the second 1000, and so on:

我能想到的最好的解决方案是查询前1000个孩子的数据库,然后查询第二个1000,依此类推:

query(startIndex:0, endIndex:999)
query(startIndex:1000, endIndex:1999)
query(startIndex:2000, endIndex:2999)
...

How can this be done using Firebase? Can it be done at all? Should my database structure be redesigned so a subtree can't contain millions of entries?

如何使用Firebase完成此操作?它可以完成吗?我的数据库结构是否应该重新设计,以便子树不能包含数百万条目?

Any advice is appreciated!

任何建议表示赞赏!

P.S. Here's my recursive solution if you're interested. Note it's not valid Swift code -- it just shows the concept.

附:如果您有兴趣,这是我的递归解决方案。注意它不是有效的Swift代码 - 它只是显示了这个概念。

func iterateChildren(startingAt: String, block: (child) -> Void) {
    // The actual query would use queryStartingAt and queryLimited.
    ref.query { children in
        // Used in recursive call.
        var nextStartingId: String? = nil

        for (index, child) in children.enumerated() {
            if (UInt(index) == limit - 1) {
                // Firebase Database's queryStarting(atValue:) method is inclusive of
                // atValue, so skip the last item as it will be included in the next
                // iteration.
                nextStartingId = child.key
                break
            }
            block(child)
        }

        if (nextStartingId != nil) {
            self.iterateChildren(startingAtId: nextStartingId, block: block)
        }   
    }
}

1 个解决方案

#1


1  

Instead of a single big child, try to use small and concise child. Why you don't try to group your sub child with some logic?

而不是一个大孩子,尝试使用小而简洁的孩子。为什么你不尝试用你的子逻辑分组?

A nice solution might be to group data by insertion date (but depends of your use case):

一个很好的解决方案可能是按插入日期对数据进行分组(但取决于您的用例):

root : {
   child : {
      "20/09/2017" : {
          "uid" : true  
      },
      "21/09/2017" : {
          "uid" : false
          "uid" : true
      }
   }
}

With this solution you could iterate your child node with observe(.childAdded) where every completionHandler give you a small block of data.

使用此解决方案,您可以使用observe(.childAdded)迭代子节点,其中每个completionHandler都会为您提供一小块数据。

Anyway, what I'm sure is that using a .value observer is a bad solution.

无论如何,我确信使用.value观察者是一个糟糕的解决方案。

Firebase suggest to avoid god child, so try to follow this simple rule: https://firebase.google.com/docs/database/ios/structure-data

Firebase建议避免使用神童,因此请尝试遵循以下简单规则:https://firebase.google.com/docs/database/ios/structure-data

#1


1  

Instead of a single big child, try to use small and concise child. Why you don't try to group your sub child with some logic?

而不是一个大孩子,尝试使用小而简洁的孩子。为什么你不尝试用你的子逻辑分组?

A nice solution might be to group data by insertion date (but depends of your use case):

一个很好的解决方案可能是按插入日期对数据进行分组(但取决于您的用例):

root : {
   child : {
      "20/09/2017" : {
          "uid" : true  
      },
      "21/09/2017" : {
          "uid" : false
          "uid" : true
      }
   }
}

With this solution you could iterate your child node with observe(.childAdded) where every completionHandler give you a small block of data.

使用此解决方案,您可以使用observe(.childAdded)迭代子节点,其中每个completionHandler都会为您提供一小块数据。

Anyway, what I'm sure is that using a .value observer is a bad solution.

无论如何,我确信使用.value观察者是一个糟糕的解决方案。

Firebase suggest to avoid god child, so try to follow this simple rule: https://firebase.google.com/docs/database/ios/structure-data

Firebase建议避免使用神童,因此请尝试遵循以下简单规则:https://firebase.google.com/docs/database/ios/structure-data