Me doy cuenta de que hay muchas preguntas con respecto a la replicación de uniones con bases de datos de documentos NoSql como FireStore, sin embargo, no puedo encontrar una solución exhaustiva utilizando Dart / Flutter con FireStore.

He investigado un poco, creo que en el siguiente ejemplo buscaría una relación de 'muchos a muchos' (corríjame si esto está mal) ya que puede haber una necesidad futura de ver todos los perfiles y todas las conexiones .

En firebase, tengo dos colecciones de nivel raíz (perfil y conexión):

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"

Para este ejemplo, los documentos de 'conexión' se crearon hipotéticamente para el usuario John Smith (uid: xyc4567) cuando se conectó con Jane Doe (uid: abc1234) y Kate Dee (uid: efg8910).

Aquí está el SQL relacional que estoy buscando replicar para mostrar una lista de perfiles con los que John Smith se ha conectado:

Select * FROM profile, connection 
WHERE profile.uid = connection.profileuid 
AND profile.uid = "xyc4567"

En flutter mi aplicación flutter tengo un punto de inicio de la consulta fireStore:

stream: Firestore.instance.collection('profile')
.where('uid', isEqualTo: "xyc4567").snapshots(),

Obviamente solo regresa de una colección. ¿Cómo me uno a las colecciones en una relación de muchos a muchos?

1
Jake Anderson 12 feb. 2019 a las 15:35

3 respuestas

La mejor respuesta

Desafortunadamente, no hay una cláusula JOIN en Cloud Firestore ni en otras bases de datos NoSQL. En Firestore las consultas son poco profundas. Esto significa que solo obtienen elementos de la colección en la que se ejecuta la consulta. No hay forma de obtener documentos de dos colecciones de nivel superior en una sola consulta. Firestore no admite consultas en diferentes colecciones de una vez. Una sola consulta solo puede usar propiedades de documentos en una sola colección.

Entonces, la solución más simple que se me ocurre es consultar la base de datos para obtener el uid de un usuario de la colección profile. Una vez que tenga esa identificación, realice otra llamada a la base de datos (dentro de la devolución de llamada) y obtenga los datos correspondientes que necesita de la colección connection utilizando la siguiente consulta:

stream: Firestore.instance.collection('connection').where('uid', isEqualTo: "xyc4567").snapshots(),

Otra solución sería crear una subcolección llamada connection debajo de cada usuario y agregar todos los objetos connection debajo de él. Esta práctica se llama denormalization y es una práctica común cuando se trata de Firebase. Si es nuevo en las bases de datos NoQSL, le recomiendo que vea este video, La desnormalización es normal con Firebase Database para una mejor comprensión. Es para la base de datos en tiempo real de Firebase, pero las mismas reglas se aplican a Cloud Firestore.

Además, cuando está duplicando datos, hay una cosa que debe tener en cuenta. De la misma manera que agrega datos, debe mantenerlos. En otras palabras, si desea actualizar / detectar un elemento, debe hacerlo en todos los lugares donde exista.

2
Alex Mamo 12 feb. 2019 a las 12:49

Hice algo así para unir los resultados de dos objetos y categorías de colecciones.

Hice dos StreamBuilders para mostrar en una lista, en la primera obtuve las categorías y las puse en un mapa, luego consulto los objetos y obtengo el objeto de categoría del mapa usando el ID de categoría:

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

                                      ),
                                    ),
                                  ],
                                );
                              }),
                        ),
                      ),
                    );

No estoy segura de si es lo que quieres o está claro, pero espero que te ayude.

1
Jorge Vieira 12 feb. 2019 a las 13:38

Creo que no se debe preferir la denominación porque para mantenerla hay que hacer escrituras adicionales en el almacén de incendios

En su lugar, jorge vieira es correcto, ya que puede realizar lecturas dobles en comparación con las escrituras

Así que es mejor leer dos veces en lugar de escribir escribiendo datos dos veces y también es muy poco práctico recordar cada cosa desmoralizada en un proyecto grande

0
zaid saeed 24 may. 2020 a las 01:50