javascript - data - firestore push to array
¿Cómo actualizar una "matriz de objetos" con Firestore? (8)
Actualmente estoy probando Firestore, y estoy atascado en algo muy simple: "actualizar una matriz (también conocido como un subdocumento)".
Mi estructura de base de datos es super simple. Por ejemplo:
proprietary: "John Doe"
sharedWith:
[
{who: "[email protected]", when:timestamp}
{who: "[email protected]", when:timestamp}
]
Estoy intentando (sin éxito) empujar nuevos registros en
shareWith
array de objetos.
He intentado:
// 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() }] })
Ninguno funciona Estas consultas sobrescriben mi matriz.
La respuesta puede ser simple, pero no pude encontrarla ...
Gracias
Aparte de las respuestas mencionadas anteriormente. Esto lo hará. Usando Angular 5 y AngularFire2. o use firebase.firestore () en lugar 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
});
}
Así es como lo hice funcionar. Espero que les sirva de ayuda, ya que lo veo como una solución mucho mejor. Aquí quiero cambiar el estado de la tarea de abrir (cerrar = falso) a cerrar (cerrar = verdadero), manteniendo todo lo demás igual en el objeto.
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);
}
y aquí está el servicio que realmente lo actualiza
public updateLeadData(updateData, leadId) {
const leadRef: AngularFirestoreDocument<LeadModel> = this.afs.doc(
`leads/${leadId}`);
return leadRef.update(updateData);
}
Considere a John Doe un documento en lugar de una colección
Dale una colección de cosas y cosas compartidas con otras
Luego, puede asignar y consultar las cosas compartidas de John Doe en esa colección paralela de thingsSharedWithOthers.
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 }
)
Firestore ahora tiene dos funciones que le permiten actualizar una matriz sin tener que volver a escribir todo.
Enlace: https://firebase.google.com/docs/firestore/manage-data/add-data , específicamente https://firebase.google.com/docs/firestore/manage-data/add-data#update_elements_in_an_array
Actualizar elementos en una matriz
Si su documento contiene un campo de matriz, puede usar arrayUnion () y arrayRemove () para agregar y eliminar elementos. arrayUnion () agrega elementos a una matriz, pero solo elementos que aún no están presentes. arrayRemove () elimina todas las instancias de cada elemento dado.
Para construir sobre la respuesta de Sam Stern , también hay una tercera opción que me facilitó las cosas y que usa lo que Google llama un Mapa, que es esencialmente un diccionario.
Creo que un diccionario es mucho mejor para el caso de uso que está describiendo. Por lo general, uso matrices para cosas que realmente no se actualizan demasiado, por lo que son más o menos estáticas. Pero para las cosas que se escriben mucho, específicamente los valores que deben actualizarse para los campos que están vinculados a otra cosa en la base de datos, los diccionarios resultan mucho más fáciles de mantener y trabajar.
Entonces, para su caso específico, la estructura de DB se vería así:
proprietary: "John Doe"
sharedWith:{
whoEmail1: {when: timestamp},
whoEmail2: {when: timestamp}
}
Esto le permitirá hacer lo siguiente:
var whoEmail = ''[email protected]'';
var sharedObject = {};
sharedObject[''sharedWith.'' + whoEmail + ''.when''] = new Date();
sharedObject[''merge''] = true;
firebase.firestore()
.collection(''proprietary'')
.doc(docID)
.update(sharedObject);
La razón para definir el objeto como una variable es que usando
''sharedWith.'' + whoEmail + ''.when''
''sharedWith.'' + whoEmail + ''.when''
directamente en el método establecido dará como resultado un error, al menos cuando se usa en una función de nube Node.js.
Perdón por llegar tarde a la fiesta, pero Firestore lo resolvió en agosto de 2018, así que si todavía lo buscas aquí, todos los problemas se resuelven con respecto a las matrices.
https://firebase.googleblog.com/2018/08/better-arrays-in-cloud-firestore.html https://firebase.googleblog.com/2018/08/better-arrays-in-cloud-firestore.html
array-contiene, arrayRemove, arrayUnion para verificar, eliminar y actualizar matrices. Espero eso ayude.
Puede usar una transacción ( https://firebase.google.com/docs/firestore/manage-data/transactions ) para obtener la matriz, presionarla y luego actualizar el documento:
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);
});
Editar 13/08/2018 : ahora hay soporte para operaciones de matriz nativas en Cloud Firestore. Vea la respuesta de Doug a continuación.
Actualmente no hay forma de actualizar un solo elemento de matriz (o agregar / eliminar un solo elemento) en Cloud Firestore.
Este código aquí:
firebase.firestore()
.collection(''proprietary'')
.doc(docID)
.set(
{ sharedWith: [{ who: "[email protected]", when: new Date() }] },
{ merge: true }
)
Esto dice configurar el documento en
proprietary/docID
modo que
sharedWith = [{ who: "[email protected]", when: new Date() }
pero no afecte a las propiedades existentes del documento.
Es muy similar a la llamada a
update()
que proporcionó, sin embargo, la llamada a
set()
crea el documento si no existe, mientras que la llamada a
update()
fallará.
Entonces tiene dos opciones para lograr lo que desea.
Opción 1: establecer toda la matriz
Llame a
set()
con todo el contenido de la matriz, lo que requerirá leer primero los datos actuales de la base de datos.
Si le preocupan las actualizaciones simultáneas, puede hacer todo esto en una transacción.
Opción 2: usar una subcolección
Puede hacer
sharedWith
una subcolección del documento principal.
Luego, agregar un solo elemento se vería así:
firebase.firestore()
.collection(''proprietary'')
.doc(docID)
.collection(''sharedWith'')
.add({ who: "[email protected]", when: new Date() })
Por supuesto, esto viene con nuevas limitaciones.
No podrá consultar documentos en función de con quién se comparten, ni podrá obtener el documento y todos los datos de
sharedWith
en una sola operación.