我有一个模型模式,如下图所示。
在用户界面中,我试图获取所有Countries
和相关数据。我已经创建了各自的结构,我的想法是使用链接Custom_Objects中所示的自定义对象方法。现在我遇到的问题是subcollections
不会出现在querysnapshot
中(我只获取字段),因此我无法进行直接对象映射(因为我必须查询和检索子集合)使对象完整)。例如:Country
结构具有States
作为属性之一,并且没有状态Country
对象无法创建,并且States
还有省。现在,我正在执行递归循环以构建我感觉不太好的整个结构。
所以我的问题是,处理此类数据的最佳方法是什么(假设没有标准化的空间,并且我们不能避免子集合)?
如果我想获得州,省或镇的任何更改的通知,我是否需要分别向每个集合添加侦听器或将其添加到根目录就足够了?
这是当前的代码捕捉
db.collection("countries").getDocuments { (QuerySnapshot, error) in
if let error = error {
print("\(error.localizedDescription)")
}else{
var docCount = QuerySnapshot!.documents.count
for document in QuerySnapshot!.documents {
self.fetchStatesForDoc(document: document, completion: { (nodes) in
var data = document.data()
data["states"] = nodes
let country = Country(dictionary: data)
self.countryList.append(country!)
print(self.sectionList)
docCount = docCount - 1
if docCount == 0{
DispatchQueue.main.async {
self.countryCollection.reloadData()
}
}
})
}
}
}
}
func fetchStatesForDoc(document: DocumentSnapshot, completion:@escaping ([State])-> Void){
var states = [State]()
document.reference.collection("states").getDocuments(completion: { (QuerySnapshot, error) in
if let error = error {
print("\(error.localizedDescription)")
}else{
var docCount = QuerySnapshot!.documents.count
for document in QuerySnapshot!.documents {
//print("\(document.documentID) => \(document.data())")
var data = document.data()
self.fetchProvincesForDoc(document: document, completion: { (provinces) in
data["Provinces"] = provinces
let state = State(dictionary: data)
states.append(state!)
docCount = docCount - 1
if docCount == 0{
completion(state)
}
})
}
}
})
}
func fetchProvincesForDoc(document: DocumentSnapshot, completion:@escaping ([Province])-> Void){
var provinces = [Province]()
document.reference.collection("provinces").getDocuments(completion: { (QuerySnapshot, error) in
if let error = error {
print("\(error.localizedDescription)")
}else{
var docCount = QuerySnapshot!.documents.count
for document in QuerySnapshot!.documents {
//print("\(document.documentID) => \(document.data())")
var data = document.data()
self.fetchTownsForDoc(document: document, completion: { (towns) in
data["towns"] = provinces
let province = Province(dictionary: data)
provinces.append(province!)
docCount = docCount - 1
if docCount == 0{
completion(province)
}
})
}
}
})
}
func fetchTownssForDoc(document: DocumentSnapshot, completion:@escaping ([Towns])-> Void) {
var towns = [Towns]()
document.reference.collection("towns").getDocuments(completion: { (QuerySnapshot, error) in
if let error = error {
print("\(error.localizedDescription)")
}else{
for document in QuerySnapshot!.documents {
//print("\(document.documentID) => \(document.data())")
}
towns = QuerySnapshot!.documents.compactMap({Towns(dictionary: $0.data())})
completion(towns)
}
})
}
答案 0 :(得分:1)
现在我遇到的问题是子集合不会出现在querysnapshot中(我只获得字段)
是的,这就是Cloud Firestore查询的工作方式。这些查询被命名为“浅”,这意味着它们仅从运行查询的集合中获取项目。无法通过单个查询从顶级集合和子集合中获取文档。 Firestore不一次性支持跨不同集合的查询。单个查询只能使用单个集合中的文档属性。这就是ypu无法在querysnapshot
对象中看到子集合的原因,因此您可以执行直接对象映射。
处理此类数据的最佳方法是什么(前提是没有标准化的空间,并且我们不能避免子集合)?
在这种情况下,您应该查询数据库两次,一次是在集合中获取对象,第二次是在子集合中获取所有对象。
还有另一种称为反规范化的实践,这是涉及Firebase的常见实践。该技术还意味着两次查询数据库。如果您不熟悉NoQSL数据库,建议您观看此视频Denormalization is normal with the Firebase Database,以更好地理解。它用于Firebase实时数据库,但相同的规则适用于Cloud Firestore。
此外,在复制数据时,需要记住一件事。用与添加数据相同的方式,您需要对其进行维护。换句话说,如果您想更新/删除项目,则需要在它存在的每个位置进行。
因此,在这种情况下,您可以通过创建顶层集合来对数据进行非规范化,在顶层集合中应添加子集合中存在的所有对象。由您决定哪种做法更适合您。