1. ホーム
  2. firebase

[解決済み】Firebaseで複数のwhere句に基づくクエリ

2022-04-15 10:28:01

質問

{
  "movies": {
    "movie1": {
      "genre": "comedy",
      "name": "As good as it gets",
      "lead": "Jack Nicholson"
    },
    "movie2": {
      "genre": "Horror",
      "name": "The Shining",
      "lead": "Jack Nicholson"
    },
    "movie3": {
      "genre": "comedy",
      "name": "The Mask",
      "lead": "Jim Carrey"
    }
  }
}

Firebase初心者です。上記のデータから結果を取り出すにはどうしたらいいでしょうか? ここで genre = 'comedy' AND lead = 'Jack Nicholson' ?

どのようなオプションがありますか?

解決方法は?

使用方法 FirebaseのクエリAPI を見ると、これを試したくなるかもしれません。

// !!! THIS WILL NOT WORK !!!
ref
  .orderBy('genre')
  .startAt('comedy').endAt('comedy')
  .orderBy('lead')                  // !!! THIS LINE WILL RAISE AN ERROR !!!
  .startAt('Jack Nicholson').endAt('Jack Nicholson')
  .on('value', function(snapshot) { 
      console.log(snapshot.val()); 
  });

しかし、Firebaseの@RobDiMarcoがコメントで言っているように。

複数 orderBy() を呼び出すと、エラーが発生します。

そこで 上記の私のコードは動作しません .

うまくいくアプローチを3つ知っています。

1. サーバでほとんどをフィルタリングし、残りをクライアントで実行する。

何を できる を実行することです。 orderBy().startAt()./endAt() サーバで残りのデータを取得し、クライアントのJavaScriptコードでフィルタリングします。

ref
  .orderBy('genre')
  .equalTo('comedy')
  .on('child_added', function(snapshot) { 
      var movie = snapshot.val();
      if (movie.lead == 'Jack Nicholson') {
          console.log(movie);
      }
  });

2. フィルタリングしたい値を組み合わせたプロパティを追加します。

もしそれが十分でないなら、あなたのユースケースを可能にするために、データを修正/拡張することを検討すべきです。例えば、ジャンル+リードを一つのプロパティに詰め込んで、このフィルタに使用することができます。

"movie1": {
    "genre": "comedy",
    "name": "As good as it gets",
    "lead": "Jack Nicholson",
    "genre_lead": "comedy_Jack Nicholson"
}, //...

この方法では、基本的に自分自身の複数列インデックスを構築し、それを使ってクエリを実行することができます。

ref
  .orderBy('genre_lead')
  .equalTo('comedy_Jack Nicholson')
  .on('child_added', function(snapshot) { 
      var movie = snapshot.val();
      console.log(movie);
  });

David Eastが書いた QueryBaseと呼ばれるライブラリは、このようなプロパティを生成するのに役立ちます。 .

例えば、映画をカテゴリー別、年別に問い合わせることができるとします。このデータ構造を使用します。

"movie1": {
    "genre": "comedy",
    "name": "As good as it gets",
    "lead": "Jack Nicholson",
    "genre_year": "comedy_1997"
}, //...

と90年代のコメディをクエリします。

ref
  .orderBy('genre_year')
  .startAt('comedy_1990')
  .endAt('comedy_2000')
  .on('child_added', function(snapshot) { 
      var movie = snapshot.val();
      console.log(movie);
  });

年だけでなく、それ以上の日付でフィルタリングする必要がある場合は、他の日付部分を降順で追加するようにします。 "comedy_1997-12-25" . こうすることで、Firebase が文字列値に対して行う辞書的順序付けが、時系列的順序付けと同じになります。

このプロパティ内の値の結合は、2つ以上の値で動作しますが、複合プロパティの最後の値に対してのみ範囲フィルタリングを行うことができます。

これの非常に特殊なバリエーションを実装したのが Firebase用GeoFireライブラリ . このライブラリは、位置の緯度と経度を組み合わせて、いわゆる ジオハッシュ これを利用して、Firebase上でリアルタイムにレンジクエリを実行することができる。

3. プログラムでカスタムインデックスを作成する

もうひとつの方法は、この新しいQuery APIが追加される前に誰もが行ってきたこと、つまり、別のノードにインデックスを作成することです。

  "movies"
      // the same structure you have today
  "by_genre"
      "comedy"
          "by_lead"
              "Jack Nicholson"
                  "movie1"
              "Jim Carrey"
                  "movie3"
      "Horror"
          "by_lead"
              "Jack Nicholson"
                  "movie2"
      

もっといろいろなアプローチがあるはずです。例えば、この回答では、ツリー型のカスタムインデックスを紹介しています。 https://stackoverflow.com/a/34105063


もし、これらのオプションのどれにも当てはまらないが、データをFirebaseに保存したいのであれば、Firebaseの持つ Cloud Firestoreデータベース .

Cloud Firestoreは1つのクエリで複数の等式フィルタを扱うことができますが、範囲フィルタは1つだけです。ボンネットの中では基本的に同じクエリモデルを使いますが、複合プロパティを自動生成してくれるようなものです。Firestoreのドキュメントを参照してください。 複合クエリ .