我尝试使用可变数量的读取操作来运行事务。 我把read()操作放在update()之前。
在https://cloud.google.com/firestore/docs/manage-data/transactions
上阅读Firestore文档"事务由任意数量的get()操作组成,后跟任意数量的写操作,如set(),update()或delete()"
和
使用交易时,请注意:
- 读操作必须在写操作之前进行。
- 如果当前编辑影响文档,则调用事务(事务函数)的函数可能会运行多次 交易读取。
- 交易功能不应直接修改申请状态。
但未提供实施。 当我尝试运行下面的代码时,我得到了更多时间运行事务函数,然后我获得了一个异常。 但是,如果我只尝试一次,那么一切顺利。
const reservationCol = this.db.firestore.collection('reservations');
return this.db.firestore.runTransaction(t => {
return Promise.all([
t.get(reservationCol.doc('id1')),
t.get(reservationCol.doc(('id2')))]
).then((responses) => {
let found = false;
responses.forEach(resp => {
if (resp.exists)
found = true;
});
if (!found)
{
entity.id='id1';
t.set(reservationCol.doc(entity.id), entity);
return Promise.resolve('ok');
}
else
return Promise.reject('exist');
});
});
答案 0 :(得分:3)
Firestore文档没有说明这一点,但答案隐藏在API参考中:https://cloud.google.com/nodejs/docs/reference/firestore/0.13.x/Transaction?authuser=0#getAll
您可以使用Transaction.getAll()
代替Transaction.get()
来获取多个文档。你的例子是:
const reservationCol = this.db.firestore.collection('reservations');
return this.db.firestore.runTransaction(t => {
return t.getAll(reservationCol.doc('id1'), reservationCol.doc('id2'))
.then(docs => {
const id1 = docs[0];
const id2 = docs[1];
if (!(id1.exists && id2.exists)) {
// do stuff
} else {
// throw error
}
})
}).then(() => console.log('Transaction succeeded'));
答案 1 :(得分:0)
我不知道如何用纯Typescript做到这一点,但是我能够找到一个使用Promise的JavaScript example,因此我根据自己的需要进行了调整。它似乎工作正常,但是当我快速运行函数(快速单击一个按钮)时,出现控制台错误,显示为POST https://firestore.googleapis.com/v1beta1/projects/myprojectname/databases/(default)/documents:commit 400 ()
。我不清楚这些错误是我应该担心的,还是仅由于事务重试而导致的错误。我发布了my own question about that,并希望得到一些答案。同时,这是我想到的代码:
async vote(username, recipeId, direction) {
let value;
if ( direction == 'up' ) {
value = 1;
}
if ( direction == 'down' ) {
value = -1;
}
// assemble vote object to be recorded in votes collection
const voteObj: Vote = { username: username, recipeId: recipeId , value: value };
// get references to both vote and recipe documents
const voteDocRef = this.afs.doc(`votes/${username}_${recipeId}`).ref;
const recipeDocRef = this.afs.doc('recipes/' + recipeId).ref;
await this.afs.firestore.runTransaction( async t => {
const voteDoc = await t.get(voteDocRef);
const recipeDoc = await t.get(recipeDocRef);
const currentRecipeScore = await recipeDoc.get('score');
if (!voteDoc.exists) {
// This is a new vote, so add it to the votes collection
// and apply its value to the recipe's score
t.set(voteDocRef, voteObj);
t.update(recipeDocRef, { score: (currentRecipeScore + value) });
} else {
const voteData = voteDoc.data();
if ( voteData.value == value ) {
// existing vote is the same as the button that was pressed, so delete
// the vote document and revert the vote from the recipe's score
t.delete(voteDocRef);
t.update(recipeDocRef, { score: (currentRecipeScore - value) });
} else {
// existing vote is the opposite of the one pressed, so update the
// vote doc, then apply it to the recipe's score by doubling it.
// For example, if the current score is 1 and the user reverses their
// +1 vote by pressing -1, we apply -2 so the score will become -1.
t.set(voteDocRef, voteObj);
t.update(recipeDocRef, { score: (currentRecipeScore + (value*2))});
}
}
return Promise.resolve(true);
});
}
答案 2 :(得分:0)
我遇到了同样的问题,因此决定结合使用批处理写入和“常规”读取。
该决定是基于以下事实:我需要进行许多不依赖彼此的阅读。最初,我使用的方法与上面Derrick提出的方法类似,但是事实证明,对于可能的阅读而言,这种方法是不可持续的。
该代码指示每个循环都阻塞到下一个循环。
我要做的是批处理所有读取,以与Promise.all
并行运行
这样做的缺点是您无法利用交易功能,但是由于我所偏爱的领域没有变化,因此很有意义
这是我的示例代码
const batch = firestore().batch()
const readPromises = invoiceValues.map(val => {
return orderCollection(omcId).where(<query field>, '<query operation>', <query>).get()
})
return Promise.all(readPromises).then(orderDocs => {
//Perform batch operations here
return batch.commit()
})
事实证明,这对于许多读取更有效,而且由于我感兴趣的字段不会更改,因此保持安全性