Firestore sorgu alt koleksiyonları


125

Yeni Firebase Firestore ile alt koleksiyonları sorgulayabileceğinizi okuduğumu düşündüm, ancak herhangi bir örnek göremiyorum. Örneğin Firestore kurulumumu şu şekilde yaptım:

  • Danslar [koleksiyon]
    • danceName
    • Şarkılar [koleksiyon]
      • şarkı adı

"SongName == 'X' olan tüm dansları bul" ifadesini nasıl sorgulayabilirim?


1
bu destekleniyor mu, 2020 yılı itfaiye mi?
sajanyamaha

Yanıtlar:


148

Güncelleme 2019-05-07

Bugün , koleksiyon grubu sorgularını yayınladık ve bunlar, alt koleksiyonlar arasında sorgulama yapmanızı sağlar.

Dolayısıyla, örneğin web SDK'da:

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

Bu, koleksiyon yolunun son kısmının 'Şarkılar' olduğu herhangi bir koleksiyondaki belgelerle eşleşir.

İlk sorunuz, songName == 'X' olan dansları bulmakla ilgiliydi ve bu hala doğrudan mümkün değil, ancak eşleşen her Şarkı için üstünü yükleyebilirsiniz.

Orijinal cevap

Bu henüz var olmayan bir özelliktir. Buna "koleksiyon grubu sorgusu" denir ve hangi dansın içerdiğine bakılmaksızın tüm şarkıları sorgulamanıza izin verir. Bu, desteklemeyi amaçladığımız bir şey ama ne zaman geleceği konusunda somut bir zaman çizelgesine sahip değiliz.

Bu noktada alternatif yapı, şarkıları üst düzey bir koleksiyon haline getirmek ve şarkının hangi dansı şarkının bir özelliğinin bir parçası haline getirmektir.


147
Firestore geliştirici ekibinin en kısa sürede koleksiyon sorgularını gerçekleştirmesi ÇOK daha iyi olurdu. Sonuçta, 'daha güçlü sorgular', Firestore kılavuzuna göre en önemli satış noktalarından biridir. Şu anda Firestore, tekerleksiz bir Porsche gibi.
Arne Wolframm

21
Katılıyoruz! Gün içinde sadece sınırlı sayıda saat vardır :-).
Gil Gilbert

20
Anlayamıyorum, ateş tabanı sınırlıysa, insanlar ne kadar ödüyor? Görünüşe göre Backendless'ın bile Firebase'den daha fazla işlevi var. Firebase neden bu kadar popüler? İnsanlar çıldırmış gibi görünüyor
nzackoya

15
Bu özelliğe çok ihtiyaç duyulmaktadır, aksi takdirde insanlar yerine getirmemiz gereken son tarihler olsa bile alternatifler bulmaya başlayacaktır. : P
JD-V

13
Bu özelliğe ihtiyacımız var. Kira kontratında yayınlamak için zaman çizelgesi bu hazırlıklı olmamıza yardımcı olacaktır.
sanjaya panigrahy

22

GÜNCELLEME Artık Firestore dizi içerenleri destekliyor

Bu belgelere sahip olmak

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

bu şekilde yap

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

@ Nelson.b.austin Firestore henüz buna sahip olmadığından, size düz bir yapıya sahip olmanızı öneririm, yani:

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

Bu şekilde yaparak, halledebilirsiniz:

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

Umarım bu yardımcı olur.


2
ne songName_Title3: falseişe yarar? Yanlış değilsem, sadece bize ihtiyaç olduğunu varsayarak belirli bir şarkının adına yok dansları aramak için kullanılabilecek songName_Title3: falseyapmak dances.where("songName_"+songTitle, "==", false); her olası şarkı için boole bayrakları olması her dans için lemeyece¤ini yapmazdım dönüş tür sonuçlar isim ...
epeleg

Bu harika, ancak belgeler 1MB ile sınırlıdır, bu nedenle uzun bir dizi listesi veya herhangi bir şeyi belirli bir belgeyle ilişkilendirmeniz gerekiyorsa, bu yaklaşımı kullanamazsınız.
Supertecnoboff

@Supertecnoboff Çok büyük ve uzun bir dizge listesi olması gerekiyormuş gibi görünüyor. Bu "array_contains" sorgusu ne kadar performanslıdır ve daha performanslı alternatifler nelerdir?
Jay Ordway

14

Şarkıları koleksiyon yerine bir nesne olarak saklarsanız ne olur? Her dans, bir alan olarak şarkılarla: Type Object (koleksiyon değil)

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

daha sonra aNameOfASong ile tüm dansları sorgulayabilirsiniz:

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
Bu çözüm işe yarayabilir, ancak şarkı sayısının fazla olması veya dinamik olarak artması durumunda ölçeklenebilir değildir. Bu, belge boyutunu artıracak ve okuma / yazma performansını etkileyecektir. Bununla
Hanif

14

GÜNCELLEME 2019

Firestore, Koleksiyon Grubu Sorguları yayınladı. Gil'in yukarıdaki yanıtına veya resmi Koleksiyon Grubu Sorgu Belgelerine bakın


Önceki Cevap

Gil Gilbert'in belirttiği gibi, koleksiyon grubu sorguları şu anda işlerin içinde gibi görünüyor . Bu arada, kök düzeyindeki koleksiyonları kullanmak ve bu koleksiyonlar arasında belge UID'lerini kullanarak bağlantı kurmak muhtemelen daha iyidir.

Zaten bilmeyenler için Jeff Delaney, AngularFirebase'de Firebase (ve Angular) ile çalışan herkes için bazı inanılmaz kılavuzlara ve kaynaklara sahiptir .

Firestore NoSQL İlişkisel Veri Modelleme - Burada, NoSQL ve Firestore DB yapılandırmasının temellerini inceliyor

Firestore ile Örnek Olarak Gelişmiş Veri Modelleme - Bunlar aklınızda tutmanız gereken daha gelişmiş tekniklerdir. Firestore becerilerini bir sonraki seviyeye taşımak isteyenler için harika bir okuma


7

YENİ GÜNCELLEME 8 Temmuz 2019:

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

3

Her zaman şu şekilde arama yapabilirsiniz: -

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

Sorgu sınırlamaları

Cloud Firestore, aşağıdaki sorgu türlerini desteklemez:

  1. Farklı alanlarda aralık filtreleri olan sorgular.

  2. Birden çok koleksiyon veya alt koleksiyonda tekli sorgular. Her sorgu tek bir belge koleksiyonunda çalışır. Veri yapınızın sorgularınızı nasıl etkilediği hakkında daha fazla bilgi için bkz . Veri Yapısı Seçme .

  3. Mantıksal VEYA sorgular. Bu durumda, her bir OR koşulu için ayrı bir sorgu oluşturmalı ve sorgu sonuçlarını uygulamanızda birleştirmelisiniz.

  4. ! = Cümlesine sahip sorgular. Bu durumda, sorguyu büyüktür ve küçüktür sorgusu olarak bölmelisiniz. Örneğin, ("yaş", "! =", "30") sorgu cümlesi desteklenmese de, iki sorguyu birleştirerek aynı sonuç kümesini elde edebilirsiniz; "," 30 ") ve biri (" yaş ","> ", 30) cümlesiyle birlikte.


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

Düz bir veri yapısı kullanmak daha iyi olabilir.
Dokümanlar , bu sayfadaki farklı veri yapılarının artılarını ve eksilerini belirtir .

Özellikle alt koleksiyonlu yapıların sınırlamaları hakkında:

Alt koleksiyonları kolayca silemez veya alt koleksiyonlar arasında bileşik sorgular gerçekleştiremezsiniz.

Düz bir veri yapısının sözde avantajlarıyla çelişir:

Kök düzeyinde koleksiyonlar, her koleksiyonda güçlü sorgulama ile birlikte en fazla esnekliği ve ölçeklenebilirliği sunar.


1

Bir çözüm buldum. Lütfen bunu kontrol edin.

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

Ardından, console.firebase.google.com adresinden bulut yangın deponuzdaki Veriler, Kurallar, Dizinler, Kullanım sekmelerini görebilirsiniz. Son olarak, dizinler sekmesinde dizinleri ayarlamalısınız.görüntü açıklamasını buraya girin

Koleksiyon kimliğini ve bazı alan değerlerini buraya girin. Ardından, toplama grubu seçeneğini seçin. Tadını çıkar. Teşekkürler


Bu soruya cevap vermiyor. Yukarıda bahsedilen sorgu, songName = 'X' olan tüm Şarkıları getirir. Bu, songName = 'X' olan dansları sağlamaz.
sachin rathod

0

Burada Observables ve AngularFire paketleyiciyle çalışıyorum ama işte bunu nasıl başardım.

Biraz çılgınca, hala gözlemlenebilirler hakkında bilgi alıyorum ve muhtemelen abarttım. Ama güzel bir egzersizdi.

Bazı açıklamalar (bir RxJS uzmanı değil):

  • songId $, kimlikleri yayacak bir gözlemlenebilir
  • dance $, bu kimliği okuyan ve sonra yalnızca ilk değeri alan bir gözlemlenebilirdir.
  • daha sonra tüm örneklerini bulmak için tüm şarkıların collectionGroup'unu sorgular.
  • Örneklere bağlı olarak, ana Danslara geçer ve kimliklerini alır.
  • Artık verilerini almak için onları sorgulamamız gereken tüm Dans kimliklerine sahip olduğumuza göre. Ancak iyi performans göstermesini istedim, bu yüzden tek tek sorgulamak yerine onları 10'luk kovalar halinde topluyorum (bir insorgu için maksimum açısal alan gerekir .
  • N kova ile sonuçlanırız ve değerlerini almak için firestore'da N sorgu yapmamız gerekir.
  • firestore'da sorguları yaptığımızda, yine de verileri gerçekten çözümlememiz gerekir.
  • ve son olarak, içindeki tüm Dansları içeren tek bir dizi elde etmek için tüm sorgu sonuçlarını birleştirebiliriz.
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();

Kodumu kopyaladım ve değişken adlarını sizinkiyle değiştirdim, herhangi bir hata olup olmadığından emin değilim, ancak benim için iyi çalıştı. Herhangi bir hata bulursanız bize bildirin veya belki sahte bir yangın deposu ile test etmenin daha iyi bir yolunu önerebilirsiniz.

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.