web-dev-qa-db-fra.com

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

J'essaie actuellement Firestore, et je suis bloqué à quelque chose de très simple: "mettre à jour un tableau (aka un sous-document)".

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

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

J'essaie (sans succès) d'insérer de nouveaux enregistrements dans le tableau shareWith d'objets.

J'ai essayé:

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

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

La réponse pourrait être simple, mais je ne pouvais pas la trouver ...

Merci

33
nerotulip

Edit 13/08/2018: Cloud Firestore prend désormais en charge les opérations de tableau natif. Voir la réponse de Doug ci-dessous.


Il n'existe 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: "[email protected]", when: new Date() }] },
  { merge: true }
)

Cela signifie de définir le document sur proprietary/docID tel que sharedWith = [{ who: "[email protected]", when: new Date() } mais de ne pas affecter les propriétés de document existantes. Il est très similaire à l'appel update() que vous avez fourni, mais l'appel set() avec créer le document s'il n'existe pas alors que l'appel update() échouera.

Donc, vous avez deux options pour réaliser ce que vous voulez.

Option 1 - Définit le tableau entier

Appelez set() avec tout le contenu du tableau, ce qui nécessitera de lire les données actuelles à partir de la base de données. Si vous êtes préoccupé par les mises à jour simultanées, vous pouvez faire tout cela dans une transaction.

Option 2 - Utiliser une sous-collection

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

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

Bien sûr, cela vient avec de nouvelles limitations. Vous ne pourrez pas interroger les documents En fonction de qui ils sont partagés, ni ne pourrez obtenir Obtenir la doc et toutes les données sharedWith en une seule opération.

34
Sam Stern

Firestore a maintenant deux fonctions qui vous permettent de mettre à jour un tableau sans avoir à tout réécrire.

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

Mettre à jour des éléments dans un tableau

Si votre document contient un champ de tableau, 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 instances de chaque élément donné.

47
Doug Galante

Vous pouvez utiliser une transaction ( https://firebase.google.com/docs/firestore/manage-data/transactions ) pour obtenir le tableau, accédez-y, puis 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);
    });
9
Gabriel McCallin

Désolé, nous sommes partis tard pour faire la fête, mais Firestore a résolu le problème en août 2018, donc si vous recherchez toujours cela ici, tous les problèmes concernant les tableaux sont résolus.

https://firebase.googleblog.com/2018/08/better-arrays-in-cloud-firestore.htmlbillet de blog officiel

tableau-contient, tableauRemove, tableauUnion pour vérifier, supprimer et mettre à jour des tableaux. J'espère que ça aide.

3
Guru

Pour construire sur la réponse de Sam Stern , il existe également une option 3rd qui m'a facilité la tâche et consiste à utiliser ce que Google appelle une carte, qui est essentiellement un dictionnaire.

Je pense qu'un dictionnaire est de loin préférable au cas d'utilisation que vous décrivez. J'utilise généralement des tableaux pour les éléments qui ne sont pas vraiment mis à jour, ils sont donc plus ou moins statiques. Mais pour les éléments très écrits, en particulier les valeurs à mettre à jour pour les champs liés à un élément de la base de données, les dictionnaires s'avèrent beaucoup plus faciles à gérer et à utiliser. 

Donc, dans votre cas particulier, la structure de la 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 = '[email protected]';

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

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

La raison pour laquelle l'objet est défini en tant que variable est que l'utilisation de 'sharedWith.' + whoEmail + '.when' directement dans la méthode set entraîne une erreur, du moins lors de son utilisation dans une fonction cloud Node.js.

2
Horea

C'est comme ça que je l'ai fait fonctionner. J'espère que cela vous aidera à tous car je le vois comme une solution plus efficace ..____ Ici, je veux changer le statut de la tâche d'ouvert (fermer = faux) à fermer (fermer = vrai), tout en gardant le même contenu 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 effectivement à jour

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

return leadRef.update(updateData);
}
1
Leandrit Ferizi

Considérez John Doe comme un document plutôt que comme une collection

Donnez-lui une collection de choses et de chosesSharedWithOthers

Ensuite, vous pouvez mapper et interroger les éléments partagés de John Doe dans cette collection parallèle chosesSharedWithOthers. 

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 }
)
1

Autre que les réponses mentionnées ci-dessus. Cela le fera . Utilisation de Angular 5 et AngularFire2. ou utilisez firebase.firestore () au lieu de 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
    });
 }  
0
Jassi

Si quelqu'un recherche Java firestore sdk solution pour ajouter des éléments dans un champ de tableau:

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

Pour supprimer des éléments de l'utilisateur du tableau: FieldValue.arrayRemove()

0
A_01