Firebase'deki birden çok nerede cümlesine dayalı sorgu


244
{
"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"
    }
  }  
 }

Ben bir Firebase acemi oldum. Nasıl yukarıdaki verilerden bir sonuç alabilir nerede genre = 'comedy'VElead = 'Jack Nicholson' ?

Hangi seçeneklerim var?

Yanıtlar:


238

Firebase'in Sorgu API'sını kullanarak şunları denemek isteyebilirsiniz:

// !!! 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()); 
  });

Ancak Firebase'den @RobDiMarco'nun yorumlarda belirttiği gibi:

birden fazla orderBy()çağrı hata verir

Yani yukarıdaki kodum çalışmaz .

İşe yarayacak üç yaklaşım biliyorum.

1. sunucuda en filtre, istemcide gerisini

Ne yapabilirsiniz yapmak birini yürütmek olduğunu orderBy().startAt()./endAt(), sunucu üzerindeki istemci üzerinde olduğunu JavaScript kodunda kalan verileri ve filtreyi aşağı çekin.

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

2. üzerinde filtrelemek istediğiniz değerleri birleştiren bir özellik ekleyin

Bu yeterince iyi değilse, kullanım durumunuza izin vermek için verilerinizi değiştirmeyi / genişletmeyi düşünmelisiniz. Örneğin: sadece bu filtre için kullandığınız tek bir özelliğe tür + yol ekleyebilirsiniz.

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

Temel olarak kendi çok sütunlu dizininizi bu şekilde oluşturuyorsunuz ve şu şekilde sorgulayabilirsiniz:

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

David East, QueryBase adında, bu tür mülklerin oluşturulmasına yardımcı olan bir kütüphane yazmıştır .

Göreli / aralık sorguları bile yapabilirsiniz, diyelim ki filmleri kategoriye ve yıla göre sorgulamaya izin vermek istiyorsunuz. Bu veri yapısını kullanırsınız:

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

Ve sonra 90'ların komedilerini sorgulayın:

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

Yalnızca yıldan daha fazla filtrelemeniz gerekiyorsa, diğer tarih parçalarını azalan sırada eklediğinizden emin olun, örn "comedy_1997-12-25". Bu şekilde, Firebase'in dize değerleri üzerinde yaptığı sözlükbilimsel sıralama kronolojik sıralama ile aynı olacaktır.

Bir özellikteki değerlerin bu birleştirilmesi ikiden fazla değerle çalışabilir, ancak bir aralık filtresini yalnızca kompozit özellikteki son değer üzerinde yapabilirsiniz.

Bunun çok özel bir çeşidi, Firebase için GeoFire kütüphanesi tarafından uygulanır . Bu kütüphane, bir konumun enlemini ve boylamını , daha sonra Firebase'de gerçek zamanlı aralık sorguları yapmak için kullanılabilen Geohash olarak adlandırır .

3. programlı olarak özel bir dizin oluşturma

Başka bir alternatif, bu yeni Sorgu API'sı eklenmeden önce yaptığımız şeyleri yapmaktır: farklı bir düğümde bir dizin oluşturmak:

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

Muhtemelen daha fazla yaklaşım var. Örneğin, bu cevap, ağaç şeklinde alternatif bir özel dizini vurgular: https://stackoverflow.com/a/34105063


3
Gelecek hafta resmi olarak yayınlandığında orderBy(), istemciler beklenmedik sonuçlar verdikleri için birden fazla çağrı hata verecektir. Testinizde tesadüfen işe yaramış olabilir, ancak bunu genel olarak çalışmak için inşa edilmemiştir (eklemeyi çok isteriz!).
Rob DiMarco

18
Bu, 2016'da Firebase V3 ile hala ilgili mi? Bunu yapmanın daha iyi yolları yok mu?
İskele

27
Neden .equalTo('comedy')bunun yerine kullanamıyoruz .startAt('comedy').endAt('comedy')?
JCarlosR

41
2018, bunun için basit bir çözüm yok mu? Bu ilişkisel veritabanındaki 101 sorgusuna benzer. David'in SQL # 8 hakkında konuştuğu video hakkındaki tüm yorumlar göz önüne alındığında, Google'ın şu ana kadar düzeltmesini beklerdim. Bazı güncellemeleri kaçırdım mı?
Yılan

10
@Snake Şubat 2019 ve sorun hala çözülmedi .... Google çok yavaş.
Supertecnoboff

48

Sunucudaki tüm siparişlerle, birden fazla değerle sipariş vermenizi sağlayan kişisel bir kütüphane yazdım.

Querybase ile tanışın!

Sorgu tabanı bir Firebase veritabanı başvurusunu ve dizine eklemek istediğiniz bir dizi alanı alır. Yeni kayıtlar oluşturduğunuzda, otomatik olarak çoklu sorgulamaya izin veren anahtarların oluşturulması işlenir. Uyarı, sadece düz eşdeğeri (en az veya daha fazla) desteklemesidir.

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

Kitaplığınız için kullanılabilir herhangi bir TypeScript tanımı var mı?
Levi Fuller

1
TS dilinde yazılmış! Yani sadece ithalat :)
David East

7
veritabanını neden bu kadar kısıtlayıcı hale getirdiklerini biliyor musunuz?
Ced

1
IOS Swift / Android sürümü yapmak için herhangi bir seçenek
mding5692

1
David, herhangi bir android / Java eşdeğeri?
Yılan

3
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) {

    }
});

3
DataSnapshot nesnesinde getLead () yöntemi yoktur.
Parag Kadam

3
Film nesnesine dönüştürdü ve bir getLead () yöntemi olduğunu varsayarak.
Kim G Pham

1
Çok teşekkür ederim.
Cümlelerin

1

Frank'in cevabı iyi, ancak Firestore array-containsyakın zamanda tanıtıldı ve bu da VE sorgularını kolaylaştırıyor.

Size filtersfiltre eklemek için bir alan oluşturabilirsiniz . İstediğiniz kadar değer ekleyebilirsiniz. Örneğin komedi ve Jack Nicholson'a göre filtrelemek için değeri ekleyebilirsiniz comedy_Jack Nicholsonancak komedi ve 2014'e göre de isterseniz comedy_2014daha fazla alan oluşturmadan değeri ekleyebilirsiniz .

{
"movies": {
    "movie1": {
        "genre": "comedy",
        "name": "As good as it gets",
        "lead": "Jack Nicholson",
        "year": 2014,
        "filters": [
          "comedy_Jack Nicholson",
          "comedy_2014"
        ]
    }
  }  
}

1

Firebase, birden çok koşulla sorgulamaya izin vermez. Ancak, bunun için bir yol buldum:

İlk filtrelenmiş verileri veritabanından indirmeli ve bir dizi listesinde depolamalıyız.

                Query query = databaseReference.orderByChild("genre").equalTo("comedy");
                databaseReference.addValueEventListener(new ValueEventListener() {
                    @Override
                    public void onDataChange(@NonNull DataSnapshot dataSnapshot) {

                        ArrayList<Movie> movies = new ArrayList<>();
                        for (DataSnapshot dataSnapshot1 : dataSnapshot.getChildren()) {
                            String lead = dataSnapshot1.child("lead").getValue(String.class);
                            String genre = dataSnapshot1.child("genre").getValue(String.class);

                            movie = new Movie(lead, genre);

                            movies.add(movie);

                        }

                        filterResults(movies, "Jack Nicholson");

                        }

                    }

                    @Override
                    public void onCancelled(@NonNull DatabaseError databaseError) {

                    }
                });

İlk filtrelenmiş verileri veritabanından aldıktan sonra, arka ucumuzda daha fazla filtre yapmamız gerekir.

public void filterResults(final List<Movie> list,  final String genre) {
        List<Movie> movies = new ArrayList<>();
        movies = list.stream().filter(o -> o.getLead().equals(genre)).collect(Collectors.toList());
        System.out.println(movies);

        employees.forEach(movie -> System.out.println(movie.getFirstName()));
    }

-1
ref.orderByChild("lead").startAt("Jack Nicholson").endAt("Jack Nicholson").listner....

Bu çalışacak.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.