Comment mettre à jour un" tableau d'objets " avec Firestore?

je suis en train d'essayer Firestore, et je suis coincé à quelque chose de très simple: "updating an array (alias a subdocument)".

ma structure de base de données est très simple. Par exemple:

proprietary: "John Doe"
sharedWith:
  [
    {who: "first@test.com", when:timestamp}
    {who: "another@test.com", when:timestamp}
  ]

je suis en train d'essayer (sans succès) pour pousser de nouveaux records dans shareWith tableau d'objets.

j'ai essayé:

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

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

aucun ne fonctionne. Ces requêtes écrasent mon tableau.

la réponse peut être simple, mais je n'ai pas pu trouver il...

Merci

23
demandé sur Frank van Puffelen 2017-10-15 20:00:35

7 réponses

Modifier 08/13/2018: il y a maintenant un support pour les opérations natives array dans Cloud Firestore. Voir réponse de Doug ci-dessous.


il n'y a actuellement aucun moyen de mettre à jour un seul élément de tableau (ou d'Ajouter/Supprimer un seul élément) dans Cloud Firestore.

Ce code ici:

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

ceci dit de mettre le document à proprietary/docID tels que sharedWith = [{ who: "third@test.com", when: new Date() } mais sans affecter les propriétés des documents existants. Il est très similaire au update() appel que vous avez fourni cependant le set() appel à créer le document s'il n'existe pas alors que le update() appel échouera.

vous avez donc deux options pour obtenir ce que vous voulez.

Option 1-Définissez le tableau entier

Appel set() avec tout le contenu du tableau, ce qui nécessitera la lecture des données courantes de la base de données en premier. Si vous êtes préoccupé par les mises à jour simultanées, vous pouvez faire tout cela dans un transaction.

Option 2-Utiliser une sous-collecte

Vous pourriez faire sharedWith une sous-collection du document principal. Puis ajouter un seul élément ressemblerait à ceci:

firebase.firestore()
  .collection('proprietary')
  .doc(docID)
  .collection('sharedWith')
  .add({ who: "third@test.com", when: new Date() })

bien sûr, cela vient avec de nouvelles limites. Vous ne seriez pas en mesure de requête les documents basés sur qui ils sont partagés, et vous ne seriez pas en mesure de va chercher le Toubib et tout le monde!--8--> données en une seule opération.

23
répondu Sam Stern 2018-08-13 21:38:41

Firestore a maintenant deux fonctions qui vous permettent de mettre à jour un tableau sans réécrire toute la chose.

Lien: https://firebase.google.com/docs/firestore/manage-data/add-data, plus précisément https://firebase.google.com/docs/firestore/manage-data/add-data#update_elements_in_an_array

mise à Jour des éléments dans un tableau

si votre document contient un champ array, vous pouvez utiliser arrayUnion () et arrayRemove() pour ajouter et supprimer des éléments. arrayUnion () ajoute des éléments à un tableau mais seulement des éléments qui ne sont pas déjà présents. arrayRemove() supprime toutes les occurrences de chaque élément.

23
répondu Doug Galante 2018-09-05 08:15:55

Vous pouvez utiliser une transaction (https://firebase.google.com/docs/firestore/manage-data/transactions) pour obtenir le tableau, appuyez dessus et mettez à jour le document:

    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);
    });
7
répondu Gabriel McCallin 2018-03-01 21:05:01

construire la réponse de Sam Stern, il y a aussi un 3e option ce qui a rendu les choses plus faciles pour moi et qui utilise ce que Google appelle une carte, qui est essentiellement un dictionnaire.

je pense qu'un dictionnaire est beaucoup mieux pour le cas d'utilisation que vous décrivez. J'utilise habituellement des tableaux pour des trucs qui ne sont pas vraiment mis à jour trop, donc ils sont plus ou moins statiques. Mais pour les choses qui s'écrivent beaucoup, spécifiquement les valeurs qui doivent être mises à jour pour les champs qui sont liés à quelque chose d'autre dans la base de données, les dictionnaires s'avérer beaucoup plus facile à maintenir et à travailler avec.

donc pour votre cas spécifique, la structure de base de données ressemblerait à ceci:

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

Cela vous permettra de faire ce qui suit:

var whoEmail = 'first@test.com';

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

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

la raison pour définir l'objet comme une variable est qu'en utilisant 'sharedWith.' + whoEmail + '.when' directement dans la méthode set d'entraîner une erreur, au moins lors de l'utilisation dans un Nœud.js fonction cloud.

2
répondu Horea 2018-03-08 11:15:53

C'est comme ça que je l'ai fait fonctionner. J'espère que ça vous aidera tous puisque je vois ça comme une meilleure solution. Ici je veux changer l'état d'une tâche d'ouvrir(fermer=false) pour fermer (close=true), tout en gardant tout de même dans l'objet.

closeTask(arrayIndex) {
        let tasks = this.lead.activityTasks;

        const updatedTask = {
          name: tasks[arrayIndex].name,
          start: tasks[arrayIndex].start,
          dueDate:tasks[arrayIndex].dueDate,
          close: true, // This is what I am changing.
        };
        tasks[arrayIndex] = updatedTask;

        const data = {
          activityTasks: tasks
        };

        this.leadService.updateLeadData(data, this.lead.id);
    }

et voici le service qui le met réellement à jour

 public updateLeadData(updateData, leadId) {
    const leadRef: AngularFirestoreDocument<LeadModel> = this.afs.doc(
      `leads/${leadId}`);

return leadRef.update(updateData);
}
1
répondu Leandrit Ferizi 2018-05-19 10:07:55

Envisager de John Doe, un document plutôt que d'une collection

Donner une collection de choses et thingsSharedWithOthers

alors vous pouvez cartographier et interroger les choses partagées de John Doe dans ce thingssharedwithothers collection.

proprietary: "John Doe"(a document)

things(collection of John's things documents)

thingsSharedWithOthers(collection of John's things being shared with others):
[thingId]:
    {who: "first@test.com", when:timestamp}
    {who: "another@test.com", when:timestamp}

then set thingsSharedWithOthers

firebase.firestore()
.collection('thingsSharedWithOthers')
.set(
{ [thingId]:{ who: "third@test.com", when: new Date() } },
{ merge: true }
)
1
répondu Richard Taylor-Kenny 2018-07-15 16:46:42

Autre que les réponses mentionnées ci-dessus. Cela permettra de le faire. en utilisant Angular 5 et AngularFire2. ou utilisez firebase.firestore () au lieu de ceci.afs

  // say you have have the following object and 
  // database structure as you mentioned in your post
  data = { who: "third@test.com", 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
    });
 }  
0
répondu Jassi 2017-11-14 12:56:17