Bagaimana cara memperbarui "larik objek" dengan Firestore?

104

Saat ini saya mencoba Firestore, dan saya terjebak pada sesuatu yang sangat sederhana: "mengupdate array (alias sub dokumen)".

Struktur DB saya sangat sederhana. Sebagai contoh:

proprietary: "John Doe",
sharedWith:
  [
    {who: "[email protected]", when:timestamp},
    {who: "[email protected]", when:timestamp},
  ],

Saya mencoba (tidak berhasil) untuk memasukkan record baru ke dalam shareWitharray objek.

Saya sudah mencoba:

// With SET
firebase.firestore()
.collection('proprietary')
.doc(docID)
.set(
  { sharedWith: [{ who: "[email protected]", when: new Date() }] },
  { merge: true }
)

// With UPDATE
firebase.firestore()
.collection('proprietary')
.doc(docID)
.update({ sharedWith: [{ who: "[email protected]", when: new Date() }] })

Tidak ada yang berhasil. Kueri ini menimpa array saya.

Jawabannya mungkin sederhana, tetapi saya tidak dapat menemukannya ...

nerotulip
sumber

Jawaban:

71

Edit 13/08/2018 : Sekarang ada dukungan untuk operasi array asli di Cloud Firestore. Lihat jawaban Doug di bawah.


Saat ini tidak ada cara untuk mengupdate satu elemen array (atau menambahkan / menghapus satu elemen) di Cloud Firestore.

Kode ini di sini:

firebase.firestore()
.collection('proprietary')
.doc(docID)
.set(
  { sharedWith: [{ who: "[email protected]", when: new Date() }] },
  { merge: true }
)

Ini mengatakan untuk mengatur dokumen proprietary/docIDsedemikian rupa sharedWith = [{ who: "[email protected]", when: new Date() }tetapi tidak mempengaruhi properti dokumen yang sudah ada. Ini sangat mirip dengan update()panggilan yang Anda berikan namun set()panggilan dengan membuat dokumen jika tidak ada sementara update()panggilan akan gagal.

Jadi, Anda memiliki dua opsi untuk mencapai apa yang Anda inginkan.

Opsi 1 - Atur seluruh larik

Panggil set()dengan seluruh isi larik, yang akan membutuhkan pembacaan data saat ini dari DB terlebih dahulu. Jika Anda khawatir tentang pembaruan bersamaan, Anda dapat melakukan semua ini dalam satu transaksi.

Opsi 2 - Gunakan subkoleksi

Anda bisa membuat sharedWithsubkoleksi dari dokumen utama. Kemudian menambahkan satu item akan terlihat seperti ini:

firebase.firestore()
  .collection('proprietary')
  .doc(docID)
  .collection('sharedWith')
  .add({ who: "[email protected]", when: new Date() })

Tentu hal ini hadir dengan batasan baru. Anda tidak akan bisa menanyakan dokumen berdasarkan dengan siapa mereka dibagikan, Anda juga tidak bisa mendapatkan dokumen dan semua sharedWithdata dalam satu operasi.

Sam Stern
sumber
9
Ini sangat membuat frustasi ... tapi terima kasih telah memberi tahu saya bahwa saya tidak akan gila.
ItJustWerks
52
ini adalah kelemahan besar, Google harus memperbaikinya secepatnya.
Sajith Mantharath
3
Jawaban @ DougGalante menunjukkan ini telah diperbaiki. Gunakan arrayUnionmetode ini.
quicklikerabbit
152

Firestore sekarang memiliki dua fungsi yang memungkinkan Anda memperbarui array tanpa menulis ulang semuanya.

Tautan: https://firebase.google.com/docs/firestore/manage-data/add-data , khususnya https://firebase.google.com/docs/firestore/manage-data/add-data#update_elements_in_an_array

Perbarui elemen dalam larik

Jika dokumen Anda berisi bidang array, Anda bisa menggunakan arrayUnion () dan arrayRemove () untuk menambah dan menghapus elemen. arrayUnion () menambahkan elemen ke array tetapi hanya elemen yang belum ada. arrayRemove () menghapus semua instance dari setiap elemen yang diberikan.

Doug Galante
sumber
58
Apakah ada cara untuk memperbarui indeks tertentu dari larik?
Artur Carvalho
1
Bagaimana cara menggunakan fitur update array ini dengan "react-native-firebase"? (Saya tidak dapat menemukan ini di dokumen resmi react-native-firebase)
kernelman
4
@ArturCarvalho Tidak, alasan mengapa dijelaskan dalam video ini youtube.com/…
Adam
3
bagi yang perlu melakukannya pada klien, gunakan "import * as firebase dari 'firebase / app';" lalu "firebase.firestore.FieldValue.arrayUnion (NEW_ELEMENT)"
michelepatrassi
1
Hebat jika ada cara untuk mengupdate item dalam array dengan id tertentu. Seperti arrayUnion tetapi dengan merge: true. Saat ini dibutuhkan 2 operasi untuk menghapus item array dan kemudian menambahkannya lagi dengan data baru.
MadMac
15

Anda dapat menggunakan transaksi ( https://firebase.google.com/docs/firestore/manage-data/transactions ) untuk mendapatkan larik, mendorongnya, lalu memperbarui dokumen:

    const booking = { some: "data" };
    const userRef = this.db.collection("users").doc(userId);

    this.db.runTransaction(transaction => {
        // This code may get re-run multiple times if there are conflicts.
        return transaction.get(userRef).then(doc => {
            if (!doc.data().bookings) {
                transaction.set({
                    bookings: [booking]
                });
            } else {
                const bookings = doc.data().bookings;
                bookings.push(booking);
                transaction.update(userRef, { bookings: bookings });
            }
        });
    }).then(function () {
        console.log("Transaction successfully committed!");
    }).catch(function (error) {
        console.log("Transaction failed: ", error);
    });
Gabriel McCallin
sumber
1
Di pernyataan if Anda harus mengubahnya menjadi ini, karena Anda kehilangan documentReferenceadd userRefseperti yang ditunjukkan: transaction.set(userRef, { bookings: [booking] });
Ilir Hushi
8

Berikut adalah contoh terbaru dari dokumentasi Firestore:

firebase.firestore.FieldValue. ArrayUnion

var washingtonRef = db.collection("cities").doc("DC");

// Atomically add a new region to the "regions" array field.
washingtonRef.update({
    regions: firebase.firestore.FieldValue.arrayUnion("greater_virginia")
});

// Atomically remove a region from the "regions" array field.
washingtonRef.update({
    regions: firebase.firestore.FieldValue.arrayRemove("east_coast")
});
Veeresh Devireddy
sumber
@nifCody, ini memang akan menambahkan elemen string baru "Greater_virginia" ke array "region" yang ada. Saya telah mengujinya dengan sukses, dan jelas tidak menambahkan "objek". Ini sinkron dengan pertanyaan seperti yang dinyatakan: "push new records".
Veeresh Devireddy
3

Untuk membangun jawaban Sam Stern , ada juga opsi ketiga yang membuat segalanya lebih mudah bagi saya dan itu menggunakan apa yang disebut Google sebagai Peta, yang pada dasarnya adalah kamus.

Menurut saya kamus jauh lebih baik untuk kasus penggunaan yang Anda gambarkan. Saya biasanya menggunakan array untuk hal-hal yang tidak terlalu banyak diperbarui, jadi mereka kurang lebih statis. Tetapi untuk hal-hal yang banyak ditulis, khususnya nilai yang perlu diperbarui untuk bidang yang ditautkan ke sesuatu yang lain dalam database, kamus terbukti jauh lebih mudah untuk dipelihara dan dikerjakan.

Jadi untuk kasus spesifik Anda, struktur DB akan terlihat seperti ini:

proprietary: "John Doe"
sharedWith:{
  whoEmail1: {when: timestamp},
  whoEmail2: {when: timestamp}
}

Ini akan memungkinkan Anda untuk melakukan hal berikut:

var whoEmail = '[email protected]';

var sharedObject = {};
sharedObject['sharedWith.' + whoEmail + '.when'] = new Date();
sharedObject['merge'] = true;

firebase.firestore()
.collection('proprietary')
.doc(docID)
.update(sharedObject);

Alasan untuk mendefinisikan objek sebagai variabel adalah bahwa menggunakan 'sharedWith.' + whoEmail + '.when'langsung dalam metode set akan menghasilkan kesalahan, setidaknya saat menggunakannya dalam fungsi cloud Node.js.

Horea
sumber
2

Selain jawaban yang disebutkan di atas. Ini akan berhasil. Menggunakan Angular 5 dan AngularFire2. atau gunakan firebase.firestore () sebagai ganti this.afs

  // say you have have the following object and 
  // database structure as you mentioned in your post
  data = { who: "[email protected]", when: new Date() };

  ...othercode


  addSharedWith(data) {

    const postDocRef = this.afs.collection('posts').doc('docID');

    postDocRef.subscribe( post => {

      // Grab the existing sharedWith Array
      // If post.sharedWith doesn`t exsit initiated with empty array
      const foo = { 'sharedWith' : post.sharedWith || []};

      // Grab the existing sharedWith Array
      foo['sharedWith'].push(data);

      // pass updated to fireStore
      postsDocRef.update(foo);
      // using .set() will overwrite everything
      // .update will only update existing values, 
      // so we initiated sharedWith with empty array
    });
 }  
Jassi
sumber
1

Pertimbangkan John Doe sebagai dokumen daripada sebuah koleksi

Berikan koleksi hal dan halSharedWithOthers

Kemudian Anda dapat memetakan dan menanyakan hal-hal yang dibagikan John Doe dalam koleksi thingsSharedWithOthers paralel itu.

proprietary: "John Doe"(a document)

things(collection of John's things documents)

thingsSharedWithOthers(collection of John's things being shared with others):
[thingId]:
    {who: "[email protected]", when:timestamp}
    {who: "[email protected]", when:timestamp}

then set thingsSharedWithOthers

firebase.firestore()
.collection('thingsSharedWithOthers')
.set(
{ [thingId]:{ who: "[email protected]", when: new Date() } },
{ merge: true }
)
Richard Taylor-Kenny
sumber
0

Jika ada yang mencari solusi sdk Java firestore untuk menambahkan item dalam bidang array:

List<String> list = java.util.Arrays.asList("A", "B");
Object[] fieldsToUpdate = list.toArray();
DocumentReference docRef = getCollection().document("docId");
docRef.update(fieldName, FieldValue.arrayUnion(fieldsToUpdate));

Untuk menghapus item dari pengguna array: FieldValue.arrayRemove()

A_01
sumber
0
addToCart(docId: string, prodId: string): Promise<void> {
    return this.baseAngularFirestore.collection('carts').doc(docId).update({
        products:
        firestore.FieldValue.arrayUnion({
            productId: prodId,
            qty: 1
        }),
    });
}
Blazet
sumber
1
Tolong jelaskan jawaban Anda.
Jenea Vranceanu
Memang, harap baca stackoverflow.com/help/how-to-answer
jasie