使用Firestore,侦听器是关注集合中文档更改的好方法。
let listener = db.collection("cities").whereField("state", isEqualTo: "CA")
.addSnapshotListener { querySnapshot, error in
guard let snapshot = querySnapshot else {
print("Error fetching snapshots: \(error!)")
return
}
snapshot.documentChanges.forEach { diff in
if (diff.type == .added) {
print("New city: \(diff.document.data())")
}
if (diff.type == .modified) {
print("Modified city: \(diff.document.data())")
}
if (diff.type == .removed) {
print("Removed city: \(diff.document.data())")
}
}
}
在本文档中的代码示例中,使用查询在等于“ CA”的固定字段“ state”上放置了侦听器。该州与城市静态相关联,只能有一个答案(即一对一)。
城市-> CA
一对多(选项1): 如果我们正在开发旅游应用程序,并且要听的“城市”文档是根据对那个城市感兴趣的单个用户(一对多)定义的,则可以在每个“城市”文档中添加一个字段,以使查询成为可能使用相同的方法。
城市A->用户1,用户2,用户3
CityB-> User1,User3,User4
在包含User1的城市上运行查询,将使用User1作为字段捕获所有城市。但是,这违反了将文档保持较小且固定长度的设计规则。随着每个新用户的添加,城市文档将继续增长。另外,还会向所有其他用户发送新用户对同一个城市感兴趣的触发器,这可能对他们来说并不是很有用。
选项2: 解决一对多问题的另一种方法是添加另一个名为“ UserCities”的集合,其中包含显示每个用户感兴趣的城市的文档。
用户1-> CityA,CityB
User2-> CityA
User3-> CityA,CityB
对于要由“城市”集合中的城市文档的更改触发的每个用户,都需要在“ UserCities”集合中对该城市的每个实例进行额外的写操作。但这会导致很多额外的写操作,但是这些写操作很昂贵,而且从编码角度来看似乎也不是很优雅。
选项3: 第三种方法可能是在“城市”下创建一个名为“用户”的子集合。
城市A
|-> User1,User2,User3
城市B
|-> User1,User2,User4
Firestore团队不久前添加了在子集合内的字段上运行查询的功能,这确实很酷。但是,将其用于侦听器也会遇到问题,因为侦听器不会拾取子集合(或父集合)中的更改,并且当任一CityA时,都不会触发User1的“用户”子集合上的侦听器或CityB在“城市”集合中获得更新。解决此问题遇到了多次写入集合和子集合的问题。
选项3b: 或者,我们可以对“用户”子集合上的侦听器执行某些操作,以根据该侦听器的结果在此首先获取更改,然后对“城市”集合中的第二个侦听器进行更改。为此,我们仍然需要以某种方式基于第一个侦听器在“城市”上定义该侦听器。
编辑:经过进一步的挖掘,此第三个选项被正确称为收集组查询。如果在侦听器中使用此代码,则其代码如下所示:
let listener = db.collectionGroup("users").whereField("user", isEqualTo: "User1")
.addSnapshotListener { querySnapshot, error in
guard let snapshot = querySnapshot else {
print("Error fetching snapshots: \(error!)")
return
}
snapshot.documentChanges.forEach { diff in
if (diff.type == .added) {
print("New user: \(diff.document.data())")
}
if (diff.type == .modified) {
print("Modified user: \(diff.document.data())")
}
if (diff.type == .removed) {
print("Removed user: \(diff.document.data())")
}
}
}
对于集合组查询,我了解到需要在控制台中设置索引,这将解决另一个问题。
因此,我们基本上正在研究三种方法,每种方法都有明显的缺点,因为它们涉及额外的写操作或违反了文档的设计规则。也许很多写入只是我们在NoSQL世界中必须接受的一个缺点,但是Firestore试图使用集合克服这些问题的需要。我希望其他许多人也面临这个选择。我想知道您是否找到了比这三个更好的解决方案,并且可以提出建议。