Firestoreクエリサブコレクション


125

新しいFirebase Firestoreでサブコレクションをクエリできると読んだと思いましたが、例がありません。たとえば、Firestoreは次のように設定されています。

  • ダンス[コレクション]
    • danceName
    • 曲[コレクション]
      • 曲名

「songName == 'X'であるすべてのダンスを検索する」をクエリするにはどうすればよいですか


1
これはまだ2020年にサポートされていますか?
sajanyamaha

回答:


148

アップデート2019-05-07

本日、コレクショングループクエリをリリースしました。これにより、サブコレクション全体でクエリを実行できます。

したがって、たとえばWeb SDKでは次のようになります。

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

これは、コレクションパスの最後の部分が「曲」であるすべてのコレクションのドキュメントに一致します。

あなたの元の質問は、songName == 'X'であるダンスを見つけることでしたが、これはまだ直接不可能ですが、一致する各ソングについて、親をロードできます。

元の答え

これはまだ存在しない機能です。これは「コレクショングループクエリ」と呼ばれ、どのダンスに含まれているかに関係なく、すべての曲をクエリできます。これは私たちがサポートするつもりですが、いつ来るかについての具体的なタイムラインはありません。

この時点での代替構造は、曲をトップレベルのコレクションにして、どのダンスが曲の一部であるかを曲のプロパティの一部にすることです。


147
Firestore開発チームがサブコレクションクエリをできるだけ早く実装した方がはるかに優れています。結局のところ、Firestoreのマニュアルによると、「より強力なクエリ」は主要なセールスポイントの1つです。今のところ、Firestoreは車輪のないポルシェのようなものです。
Arne Wolframm

21
賛成です!1日の時間は限られています:-)。
ギルギルバート

20
火の基地が限られている場合、人々は何を支払うのかわかりません。バックエンドレスでもFirebaseよりも多くの機能を持っているようです。そして、なぜFirebaseはそれほど人気が​​あるのですか?人々は夢中になっているようです
nzackoya 2017年

15
この機能は非常に必要です。そうしないと、期限が迫っていても、他の人が代替案を見つけ始めます。:P
JD-V

13
この機能が必要です。少なくともこれをリリースするタイムラインは、私たちが準備するのに役立ちます。
sanjaya panigrahy

22

UPDATE Now Firestoreは配列コンテナをサポートします

これらの文書を持っている

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

このようにしてください

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

@ Nelson.b.austin firestoreにはまだないので、フラットな構造にすることをお勧めします。

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

そのようにして、あなたはそれを成し遂げることができます:

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

これがお役に立てば幸いです。


2
何にsongName_Title3: false役立ちますか?私が間違っていない場合は、特定の曲名のないダンスを検索するためにのみ使用できます。そのような結果songName_Title3: falsedances.where("songName_"+songTitle, "==", false); 返す必要があると仮定すると、すべてのダンスがすべての可能な曲にブールフラグを持つことは意味がありません。名前...
epeleg

これはすばらしいですが、ドキュメントは1MBに制限されているため、文字列の長いリストなどを特定のドキュメントに関連付ける必要がある場合は、このアプローチを使用できません。
Supertecnoboff

@Supertecnoboffそれは文字列のひどく大きくて長いリストでなければならないようです。この "array_contains"クエリのパフォーマンスはどの程度ですか。
ジェイオードウェイ

14

曲をコレクションではなくオブジェクトとして保存するとどうなりますか?各ダンスのように、歌をフィールドとして:タイプObject(コレクションではない)

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

次に、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);
    });

3
このソリューションは機能しますが、曲数が多い場合や動的に増加する可能性がある場合は、拡張性がありません。これにより、ドキュメントのサイズが大きくなり、読み取り/書き込みのパフォーマンスに影響します。これについて詳しくは、以下にリンクされているFirebaseのドキュメントをご覧ください(ページの最後のセクション「制限事項」を参照)firebase.google.com/docs/firestore/solutions/arrays
Nouman Hanif

14

アップデート2019

Firestoreはコレクショングループクエリをリリースしました。上記のギルの回答または公式のコレクショングループクエリドキュメントを参照してください


前の回答

ギル・ギルバートが述べたように、コレクション・グループのクエリが現在作業中のようです。それまでの間は、ルートレベルのコレクションを使用して、ドキュメントUIDを使用してこれらのコレクションをリンクすることをお勧めします。

まだ知らない人のために、Jeff Delaneyは、AngularFirebaseでFirebase(およびAngular)を操作する人のための素晴らしいガイドとリソースをいくつか用意しています。

Firestore NoSQL Relational Data Modeling-ここでは、NoSQLとFirestore DBの構造化の基本を分析しています

例によるFirestoreを使用した高度なデータモデリング -これらは、心に留めておくためのより高度な手法です。Firestoreのスキルを次のレベルに引き上げたい方におすすめの読み物


7

新規更新2019年7月8日:

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

3

あなたはいつもこのように検索できます:-

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;
      }
    })
);

3

クエリの制限

Cloud Firestoreは、次のタイプのクエリをサポートしていません。

  1. さまざまなフィールドで範囲フィルターを使用したクエリ。

  2. 複数のコレクションまたはサブコレクションにわたる単一のクエリ。各クエリは、ドキュメントの単一のコレクションに対して実行されます。データ構造がクエリに与える影響の詳細については、「データ構造選択する」を参照してください 。

  3. 論理ORクエリ。この場合、OR条件ごとに個別のクエリを作成し、クエリ結果をアプリにマージする必要があります。

  4. !=句を使用したクエリ。この場合、クエリを大なりクエリと小なりクエリに分割する必要があります。たとえば、クエリ句where( "age"、 "!="、 "30")はサポートされていませんが、where( "age"、 "< "、" 30 ")およびwhere(" age "、"> "、30)の句を含むもの。


2
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);
        });

1

フラットなデータ構造を使用した方がよい場合があります。
ドキュメントは、このページのさまざまなデータ構造の長所と短所を指定します

具体的には、サブコレクションを含む構造の制限について:

サブコレクションを簡単に削除したり、サブコレクション間で複合クエリを実行したりすることはできません。

フラットデータ構造の利点とは対照的です。

ルートレベルのコレクションは、各コレクション内の強力なクエリとともに、最も柔軟性とスケーラビリティを提供します。


1

解決策を見つけました。こちらをご確認ください。

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

そして、あなたはconsole.firebase.google.comからクラウドファイアストアのデータ、ルール、インデックス、使用法タブを見ることができます。最後に、[インデックス]タブでインデックスを設定する必要があります。ここに画像の説明を入力してください

ここにコレクションIDといくつかのフィールド値を入力します。次に、コレクショングループオプションを選択します。楽しめ。ありがとう


これは質問には答えません。上記のクエリは、songName = 'X'のすべての曲をフェッチするだけです。これは、songName = 'X'であるダンスを提供しません。
sachin rathod

0

ここではObservablesとAngularFireラッパーを使用していますが、ここでどうやってそれを実現できたかを示します。

それはちょっとおかしいです、私はまだ観測可能物について学んでいて、多分それをやり過ぎました。しかし、それは素晴らしい練習でした。

いくつかの説明(RxJSの専門家ではありません):

  • songId $は、IDを発行するオブザーバブルです
  • dance $は、そのIDを読み取り、最初の値のみを取得するオブザーバブルです。
  • 次に、すべての曲のcollectionGroupを照会して、そのすべてのインスタンスを検索します。
  • インスタンスに基づいて、それは親Dancesに移動し、IDを取得します。
  • これで、すべてのダンスIDを取得したので、データを取得するためにそれらをクエリする必要があります。しかし、私はそれをうまく実行したかったので、1つずつクエリを実行するのではなく、10のバケットにバッチ処理します(inクエリの最大角度がかかります)。
  • 最終的にNバケットになり、それらの値を取得するためにFirestoreでNクエリを実行する必要があります。
  • firestoreでクエリを実行した後も、実際にそこからデータを解析する必要があります。
  • 最後に、すべてのクエリ結果をマージして、すべてのダンスを含む単一の配列を取得できます。
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();

コードをコピーして貼り付け、変数名をあなたの名前に変更しました。エラーがあるかどうかはわかりませんが、問題なく動作しました。エラーを見つけた場合、またはいくつかの模擬暖炉でテストするためのより良い方法を提案できる場合は、お知らせください。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.