2
votes

Firestore répliquant une jointure SQL pour noSQL et Flutter

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?


0 commentaires

4 Réponses :


3
votes

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(),


1 commentaires

Salut Alex, désolé dormait :) Votre réponse m'a envoyé sur le bon chemin - merci.



2
votes

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 >


0 commentaires

0
votes

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


0 commentaires

0
votes

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


0 commentaires