Reputation: 1909
Using a firebase function, I'm polling an API every x minutes for a set of items which I keep my firestore. Say each item look like this:
{ name: "John", id: 1}
If this is a new document, I would like to store it in firestore with a state
field with value "New":
[1]: { name: "John", state: "New"}
However, if I've seen this document before (its in the firestore with that id), I would like to update the data inside, but leave the state untouched, i.e
Before:
[1]: { name: "John", state: "Processed"}
After:
[1]: {name: "UpdatedName", state: "Processed"}
How can I achieve this? For documents that already exist I can achieve this using the mergeFields
SetOption so exclude the state field - but unfortunately brand new documents that arrive then are not set with state: "New"
.
The other option is to check with firebase if the id exists for each document, but this seemed less than ideal and was awkward to achieve (I ended up making multiple queries in a foreach loop). This is roughly my code:
const batch = db.batch();
response.data.items.forEach(item => {
const document = {
name: item.name
state: "New"
};
batch.set(
db.collection(collectionPath).doc(item.id),
document,
{
mergeFields: Object.keys(document).filter(
field => field !== 'state'
),
}
);
});
return batch.commit();
Upvotes: 2
Views: 496
Reputation: 599631
Since the new state of the document on the existing state (or lack thereof) of the document, you'll need to execute a transaction here.
let docRef = db.collection(collectionPath).doc(item.id);
let transaction = db.runTransaction(t => {
return t.get(docRef)
.then(doc => {
if (doc.exists()) {
t.update(docRef, { state: "Processed" });
}
else {
t.set(docRef, { name: "John", state: "New" });
}
});
}).then(result => {
console.log('Transaction success!');
}).catch(err => {
console.log('Transaction failure:', err);
});
Upvotes: 2