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