Firestore query subcollections

后端 未结 11 1390
余生分开走
余生分开走 2020-11-22 09:24

I thought I read that you can query subcollections with the new Firebase Firestore, but I don\'t see any examples. For example I have my Firestore setup in the following way

相关标签:
11条回答
  • 2020-11-22 09:33

    I have found a solution. Please check this.

    var museums = Firestore.instance.collectionGroup('Songs').where('songName', isEqualTo: "X");
            museums.getDocuments().then((querySnapshot) {
                setState(() {
                  songCounts= querySnapshot.documents.length.toString();
                });
            });
    

    And then you can see Data, Rules, Indexes, Usage tabs in your cloud firestore from console.firebase.google.com. Finally, you should set indexes in the indexes tab.

    Fill in collection ID and some field value here. Then Select the collection group option. Enjoy it. Thanks

    0 讨论(0)
  • 2020-11-22 09:36

    You can always search like this:-

        this.key$ = new BehaviorSubject(null);
    
        return this.key$.switchMap(key =>
          this.angFirestore
            .collection("dances").doc("danceName").collections("songs", ref =>
              ref
                .where("songName", "==", X)
            )
            .snapshotChanges()
            .map(actions => {
              if (actions.toString()) {
                return actions.map(a => {
                  const data = a.payload.doc.data() as Dance;
                  const id = a.payload.doc.id;
                  return { id, ...data };
                });
              } else {
                return false;
              }
            })
        );
    
    0 讨论(0)
  • 2020-11-22 09:38

    UPDATE 2019

    Firestore have released Collection Group Queries. See Gil's answer above or the official Collection Group Query Documentation


    Previous Answer

    As stated by Gil Gilbert, it seems as if collection group queries is currently in the works. In the mean time it is probably better to use root level collections and just link between these collection using the document UID's.

    For those who don't already know, Jeff Delaney has some incredible guides and resources for anyone working with Firebase (and Angular) on AngularFirebase.

    Firestore NoSQL Relational Data Modeling - Here he breaks down the basics of NoSQL and Firestore DB structuring

    Advanced Data Modeling With Firestore by Example - These are more advanced techniques to keep in the back of your mind. A great read for those wanting to take their Firestore skills to the next level

    0 讨论(0)
  • 2020-11-22 09:39
    var songs = []    
    db.collection('Dances')
          .where('songs.aNameOfASong', '==', true)
          .get()
          .then(function(querySnapshot) {
            var songLength = querySnapshot.size
            var i=0;
            querySnapshot.forEach(function(doc) {
               songs.push(doc.data())
               i ++;
               if(songLength===i){
                    console.log(songs
               }
              console.log(doc.id, " => ", doc.data());
            });
           })
           .catch(function(error) {
             console.log("Error getting documents: ", error);
            });
    
    0 讨论(0)
  • 2020-11-22 09:39

    It could be better to use a flat data structure.
    The docs specify the pros and cons of different data structures on this page.

    Specifically about the limitations of structures with sub-collections:

    You can't easily delete subcollections, or perform compound queries across subcollections.

    Contrasted with the purported advantages of a flat data structure:

    Root-level collections offer the most flexibility and scalability, along with powerful querying within each collection.

    0 讨论(0)
  • 2020-11-22 09:39

    I'm working with Observables here and the AngularFire wrapper but here's how I managed to do that.

    It's kind of crazy, I'm still learning about observables and I possibly overdid it. But it was a nice exercise.

    Some explanation (not an RxJS expert):

    • songId$ is an observable that will emit ids
    • dance$ is an observable that reads that id and then gets only the first value.
    • it then queries the collectionGroup of all songs to find all instances of it.
    • Based on the instances it traverses to the parent Dances and get their ids.
    • Now that we have all the Dance ids we need to query them to get their data. But I wanted it to perform well so instead of querying one by one I batch them in buckets of 10 (the maximum angular will take for an in query.
    • We end up with N buckets and need to do N queries on firestore to get their values.
    • once we do the queries on firestore we still need to actually parse the data from that.
    • and finally we can merge all the query results to get a single array with all the Dances in it.
    type Song = {id: string, name: string};
    type Dance = {id: string, name: string, songs: Song[]};
    
    const songId$: Observable<Song> = new Observable();
    const dance$ = songId$.pipe(
      take(1), // Only take 1 song name
      switchMap( v =>
        // Query across collectionGroup to get all instances.
        this.db.collectionGroup('songs', ref =>
          ref.where('id', '==', v.id)).get()
      ),
      switchMap( v => {
        // map the Song to the parent Dance, return the Dance ids
        const obs: string[] = [];
        v.docs.forEach(docRef => {
          // We invoke parent twice to go from doc->collection->doc
          obs.push(docRef.ref.parent.parent.id);
        });
        // Because we return an array here this one emit becomes N
        return obs;
      }),
      // Firebase IN support up to 10 values so we partition the data to query the Dances
      bufferCount(10),
      mergeMap( v => { // query every partition in parallel
        return this.db.collection('dances', ref => {
          return ref.where( firebase.firestore.FieldPath.documentId(), 'in', v);
        }).get();
      }),
      switchMap( v => {
        // Almost there now just need to extract the data from the QuerySnapshots
        const obs: Dance[] = [];
        v.docs.forEach(docRef => {
          obs.push({
            ...docRef.data(),
            id: docRef.id
          } as Dance);
        });
        return of(obs);
      }),
      // And finally we reduce the docs fetched into a single array.
      reduce((acc, value) => acc.concat(value), []),
    );
    const parentDances = await dance$.toPromise();
    
    

    I copy pasted my code and changed the variable names to yours, not sure if there are any errors, but it worked fine for me. Let me know if you find any errors or can suggest a better way to test it with maybe some mock firestore.

    0 讨论(0)
提交回复
热议问题