Home > Software engineering >  How to query documents of 'X' collection from document id of 'Y ' collection in
How to query documents of 'X' collection from document id of 'Y ' collection in

Time:09-27

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,

CodePudding user response:

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.
  • Related