我意识到在复制NoSql文档数据库(如FireStore)的联接方面存在很多问题,但是我无法找到将Dart / Flutter与FireStore结合使用的全面解决方案。
我已经做过一些研究,我觉得在以下示例中,我将寻找“多对多”关系(如果这是错的,请纠正我),因为将来可能还需要查看所有个人资料和所有连接一样。
在firebase中,我有两个根级别集合(配置文件和连接):
profile
> documentKey(Auto Generated)
> name = "John Smith"
> uid = "xyc4567"
> documentKey(Auto Generated)
> name = "Jane Doe"
> uid = "abc1234"
> documentKey(Auto Generated)
> name = "Kate Dee"
> uid = "efg8910"
connection
> documentKey(Auto Generated)
> type = "friend"
> profileuid = "abc1234"
> uid = "xyc4567"
> documentKey(Auto Generated)
> type = "family"
> profileuid = "abc1234"
> uid = "efg8910"
在此示例中,假设用户John Smith(uid:xyc4567)连接到Jane Doe(uid:abc1234)和Kate Dee(uid:efg8910)时,已为用户创建了“连接”文档。
以下是我要复制的关系SQL,以显示John Smith与之关联的配置文件列表:
Select * FROM profile, connection
WHERE profile.uid = connection.profileuid
AND profile.uid = "xyc4567"
在Flutter我的Flutter应用程序中,我有一个fireStore查询起点:
stream: Firestore.instance.collection('profile')
.where('uid', isEqualTo: "xyc4567").snapshots(),
很显然,它仅从一个集合中返回。如何以多对多关系加入收藏?
答案 0 :(得分:2)
不幸的是,Cloud Firestore和其他NoSQL数据库中没有JOIN
子句。在Firestore中,查询很浅。这意味着它们仅从运行查询的集合中获取项目。在单个查询中无法从两个顶级集合中获取文档。 Firestore不一次性支持跨不同集合的查询。单个查询只能使用单个集合中的文档属性。
因此,我能想到的最简单的解决方案是查询数据库,以从uid
集合中获取用户的profile
。获得该ID后,请在回调中进行另一个数据库调用,并使用以下查询从connection
集合中获取所需的数据:
stream: Firestore.instance.collection('connection').where('uid', isEqualTo: "xyc4567").snapshots(),
另一种解决方案是在每个用户下创建一个名为connection
的子集合,并在其下添加所有connection
对象。这种做法称为denormalization
,是Firebase的常见做法。如果您不熟悉NoQSL数据库,建议您观看此视频Denormalization is normal with the Firebase Database,以更好地理解。它用于Firebase实时数据库,但相同的规则适用于Cloud Firestore。
此外,在复制数据时,需要记住一件事。用与添加数据相同的方式,您需要对其进行维护。换句话说,如果您想更新/删除项目,则需要在它存在的每个位置进行。
答案 1 :(得分:1)
我做了这样的事情,将两个对象和类别的结果结合起来。
我做了两个StreamBuilder来显示在一个列表中,在第一个中,我获得了类别并放入了地图,然后我查询了对象并使用categoryID从地图中获得了类别对象:
StreamBuilder<QuerySnapshot>(
stream: Firestore.instance
.collection('categoryPath')
.snapshots(),
builder: (BuildContext context,
AsyncSnapshot<QuerySnapshot> categorySnapshot) {
//get data from categories
if (!categorySnapshot.hasData) {
return const Text('Loading...');
}
//put all categories in a map
Map<String, Category> categories = Map();
categorySnapshot.data.documents.forEach((c) {
categories[c.documentID] =
Category.fromJson(c.documentID, c.data);
});
//then from objects
return StreamBuilder<QuerySnapshot>(
stream: Firestore.instance
.collection('objectsPath')
.where('day', isGreaterThanOrEqualTo: _initialDate)
.where('day', isLessThanOrEqualTo: _finalDate)
.snapshots(),
builder: (BuildContext context,
AsyncSnapshot<QuerySnapshot> objectsSnapshot) {
if (!objectsSnapshot.hasData)
return const Text('Loading...');
final int count =
objectsSnapshot.data.documents.length;
return Expanded(
child: Container(
child: Card(
elevation: 3,
child: ListView.builder(
padding: EdgeInsets.only(top: 0),
itemCount: count,
itemBuilder: (_, int index) {
final DocumentSnapshot document =
objectsSnapshot.data.documents[index];
Object object = Object.fromJson(
document.documentID, document.data);
return Column(
children: <Widget>[
Card(
margin: EdgeInsets.only(
left: 0, right: 0, bottom: 1),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.all(
Radius.circular(0)),
),
elevation: 1,
child: ListTile(
onTap: () {},
title: Text(object.description,
style: TextStyle(fontSize: 20)),
//here is the magic, i get the category name using the map
of the categories and the category id from the object
subtitle: Text(
categories[object.categoryId] !=
null
? categories[
object.categoryId]
.name
: 'Uncategorized',
style: TextStyle(
color: Theme.of(context)
.primaryColor),
),
),
),
],
);
}),
),
),
);
我不确定您想要的还是明确的,但希望对您有所帮助。
答案 2 :(得分:0)
我认为不应该使用宗派命名法,因为要保持该名称,您必须对Firestore进行额外的写入
相反,jorge vieira 是正确的,因为与写入相比,您可以进行两次读取
因此,最好两次读取而不是两次写入数据,并且记住大型项目中每一个沮丧的事物也是不切实际的
答案 3 :(得分:0)
假设您要使用依赖于某些Stream
对象的Future
。
Stories
Document ID (Auto Generated) //Suppose, "zddgaXmdadfHs"
> name = "The Lion & the Warthog"
> coverImage = "https://...."
> author = "Furqan Uddin Fahad"
> publisDate = 123836249234
Favorites
Document ID (Auto Generated)
> storyDocID = "zddgaXmdadfHs" //Document ID of a story
> userId = "adZXkdfnhoa" //Document ID of a user
Sql等效查询应如下所示
SELECT * FROM Favorites AS f, Stories AS s
WHERE f.storyDocID = s.DocumentID
AND f.userId = user.userId
还有类似的Firestore查询
final _storeInstance = Firestore.instance;
Stream <List<Favorite>> getFavorites() async* {
final user = await _getUser(); //_getUser() Returns Future<User>
yield* _storeInstance
.collection('Favorites')
.where('userId', isEqualTo: user.userId)
.snapshots()
.asyncMap((snapshot) async {
final list = snapshot.documents.map((doc) async {
final story = await _getStory(doc['storyDocID']);
return Favorite.from(doc, story); //Favorite.from(DocumentSnapshot doc, Story story) returns an instance of Favorite
}).toList(); //List<Future<Favorite>>
return await Future.wait(list); //Converts List<Future<Favorite>> to Future<List<Favorite>>
});
}
Future<Story> _getStory(String storyDocID) async {
final docRef = _storeInstance
.collection('Stories')
.document(storyDocID);
final document = await docRef.get();
final story = Story.from(document);
return story;
}