2017-10-15 3 views
6

Ich versuche derzeit Firestore, und ich bin auf etwas sehr einfach fest: "Aktualisieren eines Arrays (aka ein Filialdokument)".Wie aktualisiert man ein "Array von Objekten" mit Firestore?

Meine DB Struktur ist super einfach. Zum Beispiel:

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

Ich versuche (ohne Erfolg) neue Datensätze in shareWith Array von Objekten zu schieben.

Ich habe versucht:

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

Keine funktioniert. Diese Abfragen überschreiben mein Array.

Die Antwort könnte einfach sein, aber ich could'nt es finden ...

Dank

Antwort

8

Es gibt derzeit keine Möglichkeit, ein einzelnes Array-Element (oder Hinzufügen/Entfernen eines einzelnen Elements) zu aktualisieren in Cloud Firestore.

Dieser Code:

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

Dies sagt das Dokument an proprietary/docID so dass sharedWith = [{ who: "[email protected]", when: new Date() } gesetzt aber nicht alle vorhandenen Dokumenteigenschaften beeinflussen. Es ist sehr ähnlich zu dem update() Anruf Sie jedoch die set() Aufruf mit dem Dokument erstellen, wenn es nicht existiert, während der update() Aufruf wird fehlschlagen.

So haben Sie zwei Möglichkeiten, um zu erreichen, was Sie wollen.

Option 1 - Satz das gesamte Array

set() Anruf mit dem gesamten Inhalt des Arrays, die die aktuellen Daten aus der ersten Lese DB erfordern. Wenn Sie über gleichzeitige Aktualisierungen besorgt sind, können Sie dies alles in einer Transaktion tun.

Option 2 - Verwenden Sie eine Untersammlung

Sie sharedWith eine Untersammlung des Hauptdokuments machen könnte. Dann ein einzelnes Element hinzugefügt würde wie folgt aussehen:

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

Natürlich kommt diese mit neuen Grenzen. Sie wären nicht in der Lage, Dokumente abzufragen, basierend auf wem sie geteilt werden, noch würden Sie in der Lage sein, das Dokument und alle sharedWith Daten in einem einzigen Vorgang zu erhalten.

+0

Das ist so frustrierend ... aber danke, dass du mich wissen lässt, dass ich nicht verrückt werde. – ItJustWerks

+8

das ist ein großer Nachteil, Google muss es so schnell wie möglich beheben. –

1

Andere als die oben genannten Antworten. Das wird es tun. Verwendung von Angular 5 und AngularFire2. oder verwenden Sie stattdessen firebase.firestore().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 
    }); 
} 
2

Sie eine Transaktion verwenden können (https://firebase.google.com/docs/firestore/manage-data/transactions) das Array zu erhalten, auf sie schieben und dann das Dokument aktualisieren:

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

auf Sam Stern's answer zu bauen, gibt es auch eine dritte Option was mir die Dinge leichter gemacht hat und das ist, was Google eine Karte nennt, die im Grunde ein Wörterbuch ist.

Ich denke, ein Wörterbuch ist viel besser für den Anwendungsfall, den Sie beschreiben. Ich verwende normalerweise Arrays für Dinge, die nicht wirklich zu sehr aktualisiert werden, also sind sie mehr oder weniger statisch. Aber für Dinge, die viel geschrieben werden, speziell Werte, die für Felder aktualisiert werden müssen, die mit etwas anderem in der Datenbank verknüpft sind, erweisen sich Wörterbücher als viel einfacher zu pflegen und zu handhaben.

Also für Ihre speziellen Fall würde die DB-Struktur wie folgt aussehen:

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

Dies ermöglicht es Ihnen, Folgendes zu tun:

var whoEmail = '[email protected]'; 

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

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

Der Grund für die Definition des Objekts als Variable ist, dass die Verwendung von 'sharedWith.' + whoEmail + '.when' direkt in der set-Methode zu einem Fehler führt, zumindest wenn es in einer Node.js Cloud-Funktion verwendet wird.

Verwandte Themen