How to query documents of 'X' collection from document id of 'Y ' collection in Flutter and Firestore
Asked Answered
P

1

0

I have two collection in firestore question and bookmark.

for saving a question to users bookmarks page , my approach for structuring database was to set the documents docID of bookmark collection to be same as of question collections documents docID and field of bookmark collection is only one that is uid of user who is saving the bookmark my structure looks like this,

this is my bookmark collection

enter image description here

this is my question collection

enter image description here

here bookmark documents ID == question document ID

so far I have used streambuilder but not able to fetch the question documents from bookmark collection, this is my code so far and I aint getting how to get question documents from bookmarks collection docID

import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:devcom/screens/detailsPage.dart';
import 'package:devcom/utils/responsive.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_svg/flutter_svg.dart';

class Bookmark extends StatefulWidget {
  final User user;
  const Bookmark({Key? key, required this.user}) : super(key: key);

  @override
  State<Bookmark> createState() => _BookmarkState();
}

class _BookmarkState extends State<Bookmark> {
  @override
  void initState() {
    _currentUser = widget.user;
    super.initState();
  }

  late User _currentUser;
  // final db = FirebaseFirestore.instance;

  @override
  Widget build(BuildContext context) {
    final Stream<QuerySnapshot> _bmStreams = FirebaseFirestore.instance
        .collection('bookmark')
        .where("uid", isEqualTo: "${_currentUser.uid}")
        .snapshots(includeMetadataChanges: true);
    return StreamBuilder<QuerySnapshot>(
      stream: _bmStreams,
      builder: (BuildContext context, AsyncSnapshot<QuerySnapshot> snapshot) {
        if (snapshot.hasError) {
          return Text('Something went wrong');
        }
        if (snapshot.hasData == false)
          return Text(
              'Howdy ${_currentUser.displayName}!!, 404 clever: you havent posted in a while');

        if (snapshot.connectionState == ConnectionState.waiting) {
          return Text("Loading");
        }

        return Scaffold(
            appBar: AppBar(
              title: Text('My Questions'),
              leading: IconButton(
                  onPressed: () {
                    Navigator.pop(context);
                  },
                  icon: Icon(Icons.arrow_back_ios_new_rounded)),
            ),
            backgroundColor: Colors.white,
            body: Padding(
                padding: EdgeInsets.all(40),
                child: ListView.builder(
                  shrinkWrap: true,
                  itemCount: snapshot.data!.docs.length,
                  itemBuilder: (context, int index) {
                    final DocumentSnapshot data = snapshot.data!.docs[index];
                    if (!data.exists) {
                      print('not exists');
                    }
                    return Text(data['mydatahere']);
                  },
                )));
      },
    );
  }
}

and is it possible to nest streambuilder or do I need to fetch documents separtely please help I am badly stuck at this and dont know how to make this work...

thank you,

Pogge answered 26/9, 2021 at 13:16 Comment(3)
This seems like a variant of #59061725Asuncion
@FrankvanPuffelen I dont have any data in bookmarks collection I have created this collection for just storing reference of user to post relation like which user have selected which post for bookmark by associating question documents docID to bookmarks document ID and I will show that list of bookmarks by querying docID from bookmarks collectionPogge
Yes, you should be able to nest streamBuilder, but consider using a future for the bookmark collection.Gisele
G
1

Yes, you can nest streamBuilders.

For each bookmark, we fetch it the question corresponding to that id.

Replace your listView builder with below.

itemBuilder: (context, int index) {
  final DocumentSnapshot data = snapshot.data!.docs[index];
  if (!data.exists) print('not exists');
  // here we return the second StreamBuilder
  return StreamBuilder<DocumentSnapshot<Map<String, dynamic>>>(
    stream: FirebaseFirestore.instance
        .collection('question')
        .doc(data.id)
        .snapshots(),
    builder: (BuildContext context,
        AsyncSnapshot<DocumentSnapshot<Map<String, dynamic>>>
            qSnapshot) {
      if (qSnapshot.hasError) return Text('Something went wrong');
      if (qSnapshot.connectionState == ConnectionState.waiting)
        return Text("Loading");
      print(qSnapshot.data!.data()); // should print the question
      return Text(data['mydatahere']);
    },
  );
},

There are some drawbacks with your document structure.

  1. If 2 users bookmark the same question, it will only retain one of their bookmarks. An easy way around this is to make uid (in bookmarks collection) an array of uids. If a user bookmarks the question, you add him to the array using this FieldValue.arrayUnion(uid) and if he un-bookmarks, you remove him with FieldValue.arrayRemove(uid) and to fetch all data for a user, you use .where('uids', arrayContains: uid).
  2. Also, It might be better to simply have an array in the questions collection called usersThatBookmarked where you add users that have bookmarked a question. This is not optimal but it should be better than my first suggestion
  3. Also, I do not think you should stream bookmarks collection. The user should not be able to add a bookmark while he's on the bookmark page. Therefore, it is better to use a future to fetch the bookmarks collection. This should improve performance.
  4. To avoid documents reaching the 1mb limit and to avoid collision (1), you can use the document id for the bookmarks collection as uid + '_' + questionId (This prevents a user from bookmarking a question twice). Then add the fields uid and questionId to each bookmark document. You can fetch using where('uid', isEqualTo: uid) and questionId will be the question document id.
Gisele answered 26/9, 2021 at 15:56 Comment(9)
so I guess 2nd option would be best for me I will implement that... thanks for your response,Pogge
but what if that array containing document gets above 1Mb, because my one doc is going to take a lot of megs so what should I do rather than creating another collection as I did earlier is there any alternative best wayPogge
can you please give me the sample code of how can do the second option I am getting assertion errors whlie doing so I followed as you said but stillPogge
I have added no 4 as answer for your 2nd comment. If you still want to try no 2, let me know so I will add working/sample code.Gisele
Okay, lemme watch thatGisele
No4 is a step above the way you did it in your question. Simply for the bookmark collection, the doc id should be uid + '_' + questionId not questionId. Then the docs in bookmark collection should contain the field uid and questionId. Then fetch the data the same way you fetched it in your question.Gisele
hey your code isnt working out its throwing me bad state field doesnt exist but my question collection has many docs with that fieldPogge
print the entire doc. Find out which field doesnt exist and confirm if it exists on your firestoreGisele
The field exists its printing out in console too but not in web app what could be wrong? Can we connect using vscode live server or meet then i can show you the better wayPogge

© 2022 - 2024 — McMap. All rights reserved.