Firestore query subcollections

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:

  • Dances [collection]
    • danceName
    • Songs [collection]
      • songName

How would I be able to query "Find all dances where songName == 'X'"


Solution 1:

Update 2019-05-07

Today we released collection group queries, and these allow you to query across subcollections.

So, for example in the web SDK:

db.collectionGroup('Songs')
  .where('songName', '==', 'X')
  .get()

This would match documents in any collection where the last part of the collection path is 'Songs'.

Your original question was about finding dances where songName == 'X', and this still isn't possible directly, however, for each Song that matched you can load its parent.

Original answer

This is a feature which does not yet exist. It's called a "collection group query" and would allow you query all songs regardless of which dance contained them. This is something we intend to support but don't have a concrete timeline on when it's coming.

The alternative structure at this point is to make songs a top-level collection and make which dance the song is a part of a property of the song.

Solution 2:

UPDATE Now Firestore supports array-contains

Having these documents

    {danceName: 'Danca name 1', songName: ['Title1','Title2']}
    {danceName: 'Danca name 2', songName: ['Title3']}

do it this way

collection("Dances")
    .where("songName", "array-contains", "Title1")
    .get()...

@Nelson.b.austin Since firestore does not have that yet, I suggest you to have a flat structure, meaning:

Dances = {
    danceName: 'Dance name 1',
    songName_Title1: true,
    songName_Title2: true,
    songName_Title3: false
}

Having it in that way, you can get it done:

var songTitle = 'Title1';
var dances = db.collection("Dances");
var query = dances.where("songName_"+songTitle, "==", true);

I hope this helps.

Solution 3:

What if you store songs as an object instead of as a collection? Each dance as, with songs as a field: type Object (not a collection)

{
  danceName: "My Dance",
  songs: {
    "aNameOfASong": true,
    "aNameOfAnotherSong": true,
  }
}

then you could query for all dances with aNameOfASong:

db.collection('Dances')
  .where('songs.aNameOfASong', '==', true)
  .get()
  .then(function(querySnapshot) {
    querySnapshot.forEach(function(doc) {
      console.log(doc.id, " => ", doc.data());
    });
   })
   .catch(function(error) {
     console.log("Error getting documents: ", error);
    });

Solution 4:

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

Solution 5:

NEW UPDATE July 8, 2019:

db.collectionGroup('Songs')
  .where('songName', isEqualTo:'X')
  .get()