web-dev-qa-db-ja.com

Firebaseの複数のwhere句に基づくクエリ

{
"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の初心者です。 where genre = 'comedy' AND lead = 'Jack Nicholson'のデータから結果を取得する方法を教えてください。

どんな選択肢がありますか?

209
47d_

Firebase's Query 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.サーバー上で最もフィルターをかけ、残りはクライアント上で行います

あなたがcanすることは、サーバー上で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が文字列値に対して行う辞書順の順序は、年代順と同じになります。

このようにプロパティ内の値を組み合わせると、3つ以上の値を扱うことができますが、コンポジットプロパティの最後の値に対して範囲フィルタしか実行できません。

これの非常に特別な変形はFirebaseの GeoFireライブラリ によって実装されています。このライブラリは、場所の緯度と経度をいわゆる Geohash にまとめたものです。これを使用して、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

206

私はあなたが複数の値で注文することを可能にする個人的なライブラリを書きました。

Querybaseに会いましょう!

Querybaseは、Firebaseデータベース参照と、インデックスを付けたいフィールドの配列を受け取ります。新しいレコードを作成すると、複数のクエリを可能にするキーの生成が自動的に処理されます。注意点は、それがストレート等価をサポートするだけであるということです。

const databaseRef = firebase.database().ref().child('people');
const querybaseRef = querybase.ref(databaseRef, ['name', 'age', 'location']);

// Automatically handles composite keys
querybaseRef.Push({ 
  name: 'David',
  age: 27,
  location: 'SF'
});

// Find records by multiple fields
// returns a Firebase Database ref
const queriedDbRef = querybaseRef
 .where({
   name: 'David',
   age: 27
 });

// Listen for realtime updates
queriedDbRef.on('value', snap => console.log(snap));
37
David East
var ref = new Firebase('https://your.firebaseio.com/');

Query query = ref.orderByChild('genre').equalTo('comedy');
query.addValueEventListener(new ValueEventListener() {
    @Override
    public void onDataChange(DataSnapshot dataSnapshot) {
        for (DataSnapshot movieSnapshot : dataSnapshot.getChildren()) {
            Movie movie = dataSnapshot.getValue(Movie.class);
            if (movie.getLead().equals('Jack Nicholson')) {
                console.log(movieSnapshot.getKey());
            }
        }
    }

    @Override
    public void onCancelled(FirebaseError firebaseError) {

    }
});
2
Andrew Lam
ref.orderByChild("lead").startAt("Jack Nicholson").endAt("Jack Nicholson").listner....

これはうまくいくでしょう。

0
jaleel