Je me rends compte qu'il y a de nombreuses questions concernant la réplication de jointures avec des bases de données de documents NoSql telles que FireStore, mais je ne parviens pas à trouver une solution complète utilisant Dart / Flutter avec FireStore.
J'ai fait des recherches, je pense que dans l'exemple suivant, je rechercherais une relation «plusieurs à plusieurs» (veuillez me corriger si ce n'est pas le cas) car il pourrait être nécessaire de regarder tous les profils ainsi que toutes les connexions.
Dans Firebase, j'ai deux collections de niveau racine (profil et connexion):
stream: Firestore.instance.collection('profile') .where('uid', isEqualTo: "xyc4567").snapshots(),
Pour cet exemple, les documents 'connection' ont été créés hypothétiquement pour l'utilisateur John Smith (uid: xyc4567) lorsqu'il s'est connecté à Jane Doe (uid: abc1234) et Kate Dee (uid: efg8910).
Voici le SQL relationnel que je cherche à répliquer pour afficher une liste de profils que John Smith a connecté avec:
Select * FROM profile, connection WHERE profile.uid = connection.profileuid AND profile.uid = "xyc4567"
Dans Flutter mon application Flutter, j'ai un point de départ de requête FireStore:
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"
Évidemment, il ne renvoie que d'un collecti sur. Comment rejoindre les collections dans une relation plusieurs à plusieurs?
4 Réponses :
Malheureusement, il n'y a pas de clause JOIN
dans Cloud Firestore ni dans d'autres bases de données NoSQL. Dans Firestore, les requêtes sont superficielles. Cela signifie qu'ils obtiennent uniquement les éléments de la collection sur laquelle la requête est exécutée. Il n'y a aucun moyen d'obtenir des documents à partir de deux collections de niveau supérieur dans une seule requête. Firestore ne prend pas en charge les requêtes dans différentes collections en une seule fois. Une seule requête ne peut utiliser les propriétés des documents que dans une seule collection.
La solution la plus simple à laquelle je puisse penser est donc d'interroger la base de données pour obtenir le uid
d'un utilisateur à partir du collection de profils
. Une fois que vous avez cet identifiant, effectuez un autre appel à la base de données (dans le rappel) et obtenez les données correspondantes dont vous avez besoin à partir de la collection connection
en utilisant la requête suivante:
stream: Firestore.instance.collection('connection').where('uid', isEqualTo: "xyc4567").snapshots(),
Salut Alex, désolé dormait :) Votre réponse m'a envoyé sur le bon chemin - merci.
J'en ai fait quelques-uns comme ça pour joindre les résultats de deux objets et catégories de collections.
J'ai fait deux StreamBuilders à afficher dans une liste, dans le premier j'ai obtenu les catégories et mis dans une carte, puis j'interroge les objets et récupérez l'objet category de la carte en utilisant le 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), ), ), ), ], ); }), ), ), );
Je ne sais pas si c'est ce que vous voulez ou si c'est clair mais j'espère que cela vous aidera. p >
Je pense que la dénomination ne devrait pas être préférée car pour la maintenir, vous devez faire des écritures supplémentaires sur Firestore
au lieu de cela, jorge vieira est correct car vous êtes autorisé à faire des lectures doubles par rapport aux écritures
il est donc préférable de lire deux fois au lieu d'écrire des données deux fois et il est également très peu pratique de se souvenir de chaque chose démoralisée dans un grand projet
Supposons que vous souhaitiez utiliser un Stream
qui dépend de certains objets Future
.
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; }
Une requête équivalente SQL devrait ressembler à ceci
SELECT * FROM Favorites AS f, Stories AS s WHERE f.storyDocID = s.DocumentID AND f.userId = user.userId
Et une requête Firestore comme celle-ci
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