Google Firestore - tek bir gidiş-dönüş seferinde birden çok kimlikle belge nasıl alınır?


108

Firestore'a tek bir gidiş dönüş (ağ araması) içinde kimlik listesine göre birden fazla belge almanın mümkün olup olmadığını merak ediyorum.


4
Gidiş dönüşlerin uygulamanızda performans sorunlarına neden olduğunu varsayıyorsunuz. Ben bunu varsaymazdım. Firebase, istekleri ardışık düzenlediği için bu gibi durumlarda iyi performans gösterme geçmişine sahiptir . Firestore'un bu senaryoda nasıl davrandığını kontrol etmemiş olsam da, var olduğunu varsaymadan önce bir performans sorununun kanıtını görmek isterim.
Frank van Puffelen

2
Diyelim ben belgelere ihtiyaç demek a, b, cbir şeyler yapmak. Her üçünü de paralel olarak ayrı isteklerle rica ediyorum. a100ms balır, 150ms csürer ve 3000ms sürer. Sonuç olarak, görevi yapmak için 3000ms beklemem gerekiyor. Olacak maxbunların. Alınacak belge sayısı çok olduğunda daha riskli olacak. Ağ durumuna bağlı olarak, bunun bir sorun olabileceğini düşünüyorum.
Joon

1
SELECT * FROM docs WHERE id IN (a,b,c)Yine de hepsini tek seferde göndermek aynı miktarda zaman almaz mı? Aradaki farkı görmüyorum, çünkü bağlantı bir kez kuruluyor ve geri kalanı bunun üzerinden geçiyor. Süre (bağlantının ilk kurulmasından sonraki), her iki yaklaşım için de aynı olan tüm belgelerin + 1 gidiş-dönüş yükleme süresidir. Sizin için farklı davranırsa, bir örnek paylaşabilir misiniz (bağlantılı sorumdaki gibi)?
Frank van Puffelen

Sanırım seni kaybettim Ardışık düzen olduğunu söylediğinizde, Firestore'un sorguları veritabanına tek bir gidiş dönüşte sunucularına otomatik olarak gruplayıp gönderdiğini mi kastediyorsunuz?
Joon

1
Evet, cevabınızı okudum, ancak birden fazla ağ araması mı yoksa yalnızca bir ağ araması mı olacağı hala belirsiz. Aynı anda sorgu yapan tek bir ağ araması yerine n, nöğeler için paralel ağ aramaları olacak gibi görünüyor n.
Joon

Yanıtlar:


105

Düğümün içindeyseniz:

https://github.com/googleapis/nodejs-firestore/blob/master/dev/src/index.ts#L978

/**
* Retrieves multiple documents from Firestore.
*
* @param {...DocumentReference} documents - The document references
* to receive.
* @returns {Promise<Array.<DocumentSnapshot>>} A Promise that
* contains an array with the resulting document snapshots.
*
* @example
* let documentRef1 = firestore.doc('col/doc1');
* let documentRef2 = firestore.doc('col/doc2');
*
* firestore.getAll(documentRef1, documentRef2).then(docs => {
*   console.log(`First document: ${JSON.stringify(docs[0])}`);
*   console.log(`Second document: ${JSON.stringify(docs[1])}`);
* });
*/

Bu, özellikle sunucu SDK'sı içindir

GÜNCELLEME: "Cloud Firestore [istemci tarafı sdk] Artık IN Sorgularını Destekliyor!"

https://firebase.googleblog.com/2019/11/cloud-firestore-now-supports-in-queries.html

myCollection.where(firestore.FieldPath.documentId(), 'in', ["123","456","789"])


31
Bu yöntemi dinamik olarak oluşturulmuş belge referansları dizisiyle çağırmak isteyen herkes için, bunu şu şekilde yapabilirsiniz: firestore.getAll (... arrayOfReferences) .then ()
Horea

1
Üzgünüm @KamanaKisinga ... Neredeyse bir yıldır herhangi bir firebase işi yapmadım ve şu anda gerçekten yardımcı olamıyorum (hey bak, bu cevabı bir yıl önce bugün gönderdim!)
Nick Franceschina

2
İstemci tarafındaki SDK'lar artık bu işlevi de sunuyor. bir örnek için jeodonara'nın cevabına bakın: stackoverflow.com/a/58780369
Frank van Puffelen

8
uyarı: giriş filtresi şu anda 10 öğe ile sınırlıdır. Yani muhtemelen prodüksiyona başlamak üzereyken işe yaramadığını anlayacaksınız.
Martin Cremer

9
aslında kullanmak zorunda firebase.firestore.FieldPath.documentId()değil'id'
Maddocks

24

Bu işlevi kısa süre önce duyurdular: https://firebase.googleblog.com/2019/11/cloud-firestore-now-supports-in-queries.html .

Şimdi gibi sorgular kullanabilirsiniz, ancak giriş boyutunun 10'dan büyük olamayacağını unutmayın.

userCollection.where('uid', 'in', ["1231","222","2131"])


Nereden ziyade bir whereIn sorgusu var. Ayrıca, belirli bir koleksiyona ait olan belgelerin kimlikleri listesinden birden çok belge için nasıl sorgu tasarlayacağımı bilmiyorum. Lütfen yardım et.
derleme hatası

17
@Compileerrorend bunu deneyebilir misin? db.collection('users').where(firebase.firestore.FieldPath.documentId(), 'in',["123","345","111"]).get()
jeadonara

özellikle için teşekkür ederimfirebase.firestore.FieldPath.documentId()
Ivan Chernykh

@jeadonara girdi dizisinin 10'dan büyük olması durumunda ne kullanmalıyım?
Ramesh Vishnoi

1
@RameshVishnoi Promise.all () ( developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/… ) kullanabilirsiniz.
jeadonara

16

Pratikte firestore.getAll böyle kullanırsınız

async getUsers({userIds}) {
    const refs = userIds.map(id => this.firestore.doc(`users/${id}`))
    const users = await this.firestore.getAll(...refs)
    console.log(users.map(doc => doc.data()))
}

veya söz dizimi ile

getUsers({userIds}) {
    const refs = userIds.map(id => this.firestore.doc(`users/${id}`))
    this.firestore.getAll(...refs).then(users => console.log(users.map(doc => doc.data())))
}

7
bu gerçekten seçilen cevap olmalı çünkü 10'dan fazla kimlik kullanmanıza izin veriyor
sshah98

Bu işe yaradı! Teşekkürler. Bununla ilgili belgeler nerede? getAll'ı aradım ve hiçbir yerde bulamadım.
TravRob

10

Hayır, şu anda Cloud Firestore SDK'yı kullanarak birden çok okuma isteğini gruplamanın bir yolu yoktur ve bu nedenle, tüm verileri aynı anda okuyabileceğinizi garanti etmenin bir yolu yoktur.

Ancak Frank van Puffelen'in yukarıdaki yorumlarda söylediği gibi bu, 3 belgenin getirilmesinin bir belgeyi getirmekten 3 kat daha yavaş olacağı anlamına gelmez. Burada bir sonuca varmadan önce kendi ölçümlerinizi yapmanız en iyisidir.


1
Mesele şu ki, Firestore'a geçmeden önce Firestore'un performansının teorik sınırlarını bilmek istiyorum. Geçiş yapmak istemiyorum ve sonra kullanım durumum için yeterince iyi olmadığını anlıyorum.
Joon

2
Merhaba, burada bir de küstahlık var. Diyelim ki arkadaşımın tüm kimliklerinin listesini sakladım ve sayı 500. Listeyi 1 okuma maliyetiyle alabilirim, ancak Adlarını ve fotoğraf URL'lerini görüntülemek için bana 500 okumaya mal olacak.
Tapas Mukherjee

1
500 belge okumaya çalışıyorsanız, 500 okuma gerektirir. 500 belgenin tümünden ihtiyacınız olan bilgileri tek bir ekstra belgede birleştirirseniz, yalnızca bir okuma alır. Buna, Cloud Firestore dahil olmak üzere çoğu NoSQL veritabanında veri çoğaltma türü denen şey oldukça normaldir.
Frank van Puffelen

1
@FrankvanPuffelen Örneğin, mongoDb'de, bu stackoverflow.com/a/32264630/648851 gibi ObjectId kullanabilirsiniz .
Sitian Liu

2
@FrankvanPuffelen'in dediği gibi, veri çoğaltma NoSQL veritabanında oldukça yaygındır. Burada kendinize bu verilerin ne sıklıkla okunması gerektiğini ve ne kadar güncel olması gerektiğini sormalısınız. 500 kullanıcı bilgisini saklarsanız, adını + fotoğrafını + kimliğini söyleyelim, onları tek bir okumada alabilirsiniz. Ancak bunlara güncel olarak ihtiyacınız varsa, muhtemelen bir kullanıcı adını / fotoğrafını her güncellediğinde bu referansları güncellemek için bir bulut işlevi kullanmanız gerekir, bu nedenle bir bulut işlevi çalıştırır + bazı yazma işlemleri yapar. "Doğru" / "daha iyi" uygulama yoktur, sadece sizin kullanım durumunuza bağlıdır.
schankam

10

Bunun gibi bir işlevi kullanabilirsiniz:

function getById (path, ids) {
  return firestore.getAll(
    [].concat(ids).map(id => firestore.doc(`${path}/${id}`))
  )
}

Tek bir kimlik ile çağrılabilir:

getById('collection', 'some_id')

veya bir dizi kimlik:

getById('collection', ['some_id', 'some_other_id'])

6

Flutter kullanıyorsanız, aşağıdakileri yapabilirsiniz:

Firestore.instance.collection('your collection name').where(FieldPath.documentId, whereIn:[list containing multiple document IDs]).getDocuments();

Bu, List<DocumentSnapshot>kendinizi uygun hissettiğinizde yineleyebileceğiniz bir Gelecek döndürür .


5

Kuşkusuz bunu yapmanın en iyi yolu, Firestore'un gerçek sorgusunu bir Bulut İşlevinde uygulamaktır? O zaman istemciden Firebase'e yalnızca tek bir gidiş-dönüş görüşmesi olur, ki bu sizin istediğiniz gibi görünüyor.

Yine de tüm veri erişim mantığınızı bu sunucu tarafında tutmayı gerçekten istiyorsunuz.

Dahili olarak Firebase'e muhtemelen aynı sayıda çağrı olacaktır, ancak bunların tümü, harici ağ yerine Google'ın süper hızlı ara bağlantılarında olacaktır ve Frank van Puffelen'in açıkladığı ardışık düzen ile birleştirildiğinde, bu yaklaşım.


3
Uygulamayı bir Bulut İşlevinde depolamak, karmaşık mantığa sahip olduğunuz bazı durumlarda doğru karardır, ancak bir listeyi birden çok kimliği olan bir listeyi birleştirmek istediğinizde bu muhtemelen doğru değildir. Kaybettiğiniz şey, istemci tarafı önbelleğe alma ve normal aramalardan standartlaştırılmış dönüş biçimlendirmesidir. Bu, yaklaşımı kullandığımda uygulamalarımda bazı durumlarda çözdüğünden daha fazla performans sorununa neden oldu.
Yeremya

2

İşte Android SDK ile Kotlin'de böyle bir şeyi nasıl yapacağınız.
Tek bir gidiş-dönüş yolculuğu olması gerekmeyebilir, ancak sonucu etkili bir şekilde gruplandırır ve birçok iç içe geçmiş geri aramadan kaçınır.

val userIds = listOf("123", "456")
val userTasks = userIds.map { firestore.document("users/${it!!}").get() }

Tasks.whenAllSuccess<DocumentSnapshot>(userTasks).addOnSuccessListener { documentList ->
    //Do what you need to with the document list
}

Belirli belgeleri getirmenin, tüm belgeleri getirip sonucu filtrelemekten çok daha iyi olduğunu unutmayın. Bunun nedeni, Firestore'un sorgu sonuç kümesi için sizden ücret almasıdır.


1
Güzel çalışıyor, tam olarak aradığım şey!
Georgi

1

Umarım bu size yardımcı olur, benim için işe yarar.

getCartGoodsData(id) {

    const goodsIDs: string[] = [];

    return new Promise((resolve) => {
      this.fs.firestore.collection(`users/${id}/cart`).get()
        .then(querySnapshot => {
          querySnapshot.forEach(doc => {
            goodsIDs.push(doc.id);
          });

          const getDocs = goodsIDs.map((id: string) => {
            return this.fs.firestore.collection('goods').doc(id).get()
              .then((docData) => {
                return docData.data();
              });
          });

          Promise.all(getDocs).then((goods: Goods[]) => {
            resolve(goods);
          });
        });
    });
  }

0

Şu anda Firestore'da bu mümkün görünmüyor. Alexander'ın cevabının neden kabul edildiğini anlamıyorum, önerdiği çözüm sadece "kullanıcılar" koleksiyonundaki tüm belgeleri döndürüyor.

Ne yapmanız gerektiğine bağlı olarak, görüntülemeniz gereken ilgili verileri çoğaltmaya çalışmalı ve yalnızca gerektiğinde tam bir belge talep etmelisiniz.


-1

Yapabileceğiniz en iyi olan değil kullanmak Promise.allsonra beklemelisiniz müşteriniz olarak.all devam etmeden önce okur.

Okumaları yineleyin ve bağımsız olarak çözmelerine izin verin. İstemci tarafında, bu muhtemelen birkaç ilerleme yükleyici görüntüsünün bağımsız olarak değerlere çözümlenmesine sahip olan kullanıcı arayüzüne indirgenir. Ancak bu, tüm müşteriyi şu tarihe kadar dondurmaktan iyidir:.all okumalar .

Bu nedenle, tüm eşzamanlı sonuçları hemen görünüme aktarın, ardından eşzamansız sonuçların çözülürken ayrı ayrı gelmesine izin verin. Bu küçük bir ayrım gibi görünebilir, ancak müşterinizin İnternet bağlantısı zayıfsa (şu anda bu kafede olduğu gibi), tüm müşteri deneyimini birkaç saniye dondurmak büyük olasılıkla 'bu uygulama berbat' deneyimiyle sonuçlanacaktır.


4
Eşzamansızdır, kullanmak için birçok kullanım durumu vardır Promise.all... herhangi bir şeyi "dondurması" gerekmez
Ryan Taylor

Tüm verilerinizi yüklemeniz gereken birkaç kullanım durumu vardır, bu nedenle bekleme (uygun bir mesaja sahip bir döndürücü gibi, söylediğiniz gibi herhangi bir kullanıcı arayüzünü "dondurmaya" gerek yoktur) Promise.all tarafından tamamen gerekli olabilir. Bu gerçekten burada ne tür ürünler inşa ettiğinize bağlı. Bu tür yorumlar benim görüşüme göre hiç alakasız ve içinde "en iyi" sözler olmamalı. Bu, gerçekten karşılaşabileceğiniz her farklı kullanım durumuna ve uygulamanızın kullanıcı için ne yaptığına bağlıdır.
schankam
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.