function sellStock(){ db.collection("Stocks") .where("ticker", "==", props.name) .get() .then((querySnapshot) => { if(!querySnapshot.empty){ querySnapshot.forEach(function(doc){ db.collection("myStocks") .doc(doc.id) .update({ shares: doc.data().shares - amount })"acciones" será la cantidad anterior. "cantidad" será la cantidad de acciones que queremos vender.
Prueba de esta manera:
.update({ shares: (doc.data().shares - amount) >= 0 ? (doc.data().shares - amount) : 0 })Actualizado después de la discusión en los comentarios.
Hay un aspecto importante a considerar con su lógica de negocios: ¿Necesita ejecutar una operación atómica en varios documentos? Ejemplo: Estás restando el valor de cantidad al valor de acciones, ya que es una operación de venta pero supongo que en otro lugar (en otro documento) también estás sumando algún valor, por ejemplo en la cuenta bancaria del vendedor.
En tal caso, debe usar una Transacción : "si una transacción lee documentos y otro cliente modifica cualquiera de esos documentos, Cloud Firestore vuelve a intentar la transacción". Debe incluir en la Transacción todos los documentos que deben bloquearse mientras la operación está en curso (es decir, todos los documentos que están involucrados en la operación, los que resta y los que agrega).
Sin embargo, dado que desea actualizar varios documentos, devueltos por una consulta, no puede usar una Transacción de uno de los SDK móviles/web (por ejemplo, iOS, Android, Web/JavaScript), porque los SDK móviles/web usan controles de concurrencia optimistas para resolver la contienda de datos .
Lo que puede hacer es usar uno de los SDK de Admin, como el de Node.js, ya que usa controles de concurrencia pesimistas y, por lo tanto, ofrece la posibilidad de ejecutar una Transacción en una consulta (vea que puede pasar una consulta al get() método). Así que podrías hacer eso en una Callable Cloud Function .
Aquí hay un ejemplo de una Transacción que actualizará atómicamente todos los documentos en los que resta. Dado que no compartió toda la lógica comercial (no sabemos cuáles son los documentos que necesita actualizar agregando un valor), es un poco difícil profundizar en el ejemplo.
exports.updateTickers = functions.https.onCall((data, context) => { // get the value of the filter (ie props.name) via the data Object const filter = data.filter; const amount = data.amount; const db = admin.firestore(); return db.runTransaction(transaction => { let queryRef = db.collection("Stocks").where("ticker", "==", filter); return transaction.get(queryRef) .then((querySnapshot) => { querySnapshot.forEach((doc) => { const currentValue = doc.get('amount'); if (currentValue - amount > 0) ( transaction.update(doc.ref, { likes: currentValue - amount }) ) }); }); }) .then(() => { return { result: "Amounts update successful" } }) });La única forma segura de evitar que el recuento de shares caiga por debajo de 0 es hacer cumplir eso en las reglas de seguridad:
allow write: if request.resource.data.shares >= 0;Cualquier otro método puede ser eludido por un usuario malintencionado que utilice los datos de configuración de su proyecto con su código.
Con esto en su lugar, la forma más simple (y rápida) de restar la amount del recuento de shares es con una operación de incremento atómico :
db.collection("myStocks") .doc(doc.id) .update({ shares: firebase.firestore.FieldValue.increment(-1 * amount) })