Is it possible to unify in a single instruction these two Firestore document set/update?
await batchArray[batchIndex].set(finref, doc2.data());
await batchArray[batchIndex].update(finref, {"esito" : 1, "timestamp": currentTime});
Where "finref" is a document reference and doc2 is a DocumentSnapshot
You can merge those two objects using spread syntax and pass it to single command.
await batchArray[batchIndex].set(finref, {
...doc2.data(),
...{
"esito": 1,
"timestamp": currentTime
}
});
If you want to perform both operations at once, then you can execute multiple write operations as a single batch that contains any combination of set(), update(), or even delete() operations. A batch of writes completes atomically, meaning that all operations will succeed or all will fail.
As you can see in the docs, the first argument is always a document reference. If you already have a document snapshot, then you need to get the document reference out of that object in order to commit the batch.
Edit:
If you try to update a document, that doesn't exist, indeed you'll get an error that says "No document to update". In that case, you should consider using set() with merge: true. This means that, if the document does not exist, then it will be created, and if the document does exist, the data will be merged into the existing document.
Related
I use mongodb (with mongoose) and I have a case where I find a document, run a bunch of complicated conditional checks and then update the document.
This is fine but now I want to ensure that the document I'm updating in the end hasn't been updated by a different update query while my conditions have been running.
Can I create a lock or somehow contain all these actions within a transaction?
You add a simple field editor and make sure each process has ownership of the document when it's time to update.
Here is a simple example:
let processId = uniqueID;
//if doc is none then a different process 'owns' it. need to decide on behaviour.
let doc = await collection.findOneAndUpdate({_id: docId, editor: {$exists: false}}, {$set: {editor: processId}})
### do calculations. ###
let newValue = calculatedValue;
let newDoc = await collection.findOneAndUpdate({_id: docId, editor: processId}, {$unset: {editor: ""}})
I feel that using transactions as suggjested in the comments is an overkill, from the docs:
In MongoDB, an operation on a single document is atomic
And
For situations that require atomicity of reads and writes to multiple documents , MongoDB supports multi-document transactions.
Transactions are meant for more complex situations than this, and I feel this overhead is not needed as a simple solution suffices.
I need to be able to tell if a set(), delete(), or update() operation on a document has succeeded before I do a transaction operation to increment or decrement a counter.
I've tried to print what set(), delete() and update() return, but it always just returns "seconds" and "nanos" whether or not the operation succeeded or not. I've tried to do operations on document IDs that don't exist, or collections that don't exist, but it always just returns the same thing with no indication if it did anything or not.
collection.("some_col").document("SoM3DoC").delete()
collection.("some_other_col").document("SoM30tH3RDoC").collection("some_col_ref").document("SoM3DoC").delete()
Then, ONLY if the above succeeded (the document existed and was deleted):
some_transaction(transaction, collection.("some_other_col").document("SoM30tH3RDoC")) # decrement a counter in this doc
I'm expecting that the operation methods would either throw an error if it couldn't complete the operation or return some message to indicate it but I can't seem to get any response. I even tried starting with some random collection like collection.("asdfsergreasg").document... but there's still no response.
The API documentation indicates what to expect from various operations, so you should use that as a reference. Some operations on documents and collections that don't exist don't yield errors. Some do. For example, calling get on a document that doesn't exist isn't an error, but the returned object will be clear that no document exists. However, calling update on a document that doesn't exist should raise an error.
I have a cluster of node worker servers that handle hitting an api and inserting data into a mongo db. The problem I am having is that one of these functions appears to every so often insert two copies of the same document. It checks if the document has already been created with a query like so:
gameDetails.findOne({ gameId: gameId }, function(err, gameCheck) {
if (!gameCheck) { //insert the document };
How can I ensure that this function always is only running one instance at a time. Alternatively, if I have not deduced the actual root problem, what could cause a mongo query like this to sometimes result in multiple of the same document, containing the same gameId, to be inserting?
findOne is being called multiple times before the document has had time to be inserted, i.e. something like the following is happening:
findThenInsert()
findThenInsert()
findThenInsert()
// findOne returns null, insert called
// findOne returns null, insert called
// document gets inserted
// findOne returns a gameCheck
// document gets inserted
You should use a unique index to prevent duplicates. Then, your node instances could optimistically call insert straight away, and simply handle the error if they were too late, which is similar to your 'if found do nothing' logic.
Alternatively if you don't mind the document being updated each time, you can use the upsert method, which is atomic:
db.collection.update(query, update, {upsert: true})
Also see
MongoDB atomic "findOrCreate": findOne, insert if nonexistent, but do not update
When I db.collection('example').update({"a":1},{"$set":{"b":2}},{multi:true},function(e,r){
I get r:
{
n:3,
nModified:3,
ok:1
}
This works, I can see If I look at my db that I have successfully updated 3 documents but where are my results?
Quoted from https://mongodb.github.io/node-mongodb-native/markdown-docs/insert.html
callback is the callback to be run after the records are updated. Has three parameters, the first is an error object (if error occured), the second is the count of records that were modified, the third is an object with the status of the operation.
I've tried with 3 outputs in the callback but, then I just get null as a result
db.collection('example').update({"a":1},{"$set":{"b":2}},{multi:true},function(e,n,r){
My documents have been successfully updated but r is null!
I am expecting for this to return my updated documents
It doesn't look like this operation ever does, so how can I manullay return the documents that got changed?
You can use findAndModify to get the updated document in the result. It's callback has 2 parameters:
1- error
2- Updated document
I am not sure this would work for you, but check [documentation]: https://mongodb.github.io/node-mongodb-native/markdown-docs/insert.html#find-and-modify for more info.
To get the updated documents in the returned result, you'll need to use the db.collection.bulkWrite method instead.
Before saving a new document to a mongodb collection via nodejs in my MongoLab database, I'm using model.count to check certain fields to prevent a duplicate entry:
MyModel.count({field1: criteria1, field2: criteria2}, function (err, count) {
if (count == 0) {
// Create new document and call .save()
}
});
However, during testing I'm noticing many duplicates (inconsistent in number across test runs) in the collection after the process finishes, although not as many as if I did not do the .count() check.
Since the MyModel.count() statement is embedded in a callback being repeatedly called whenever the 'readable' event is emitted by one of several ReadStreams, I suspect there is an async issue caused by rapid writes to the collection. Specifically, two or more identical and nearly simultaneous calls to MyModel.count return a count of 0, and end up each creating and saving (identical) documents to the collection.
Does this sound probable? And if so how can I enforce uniqueness of document writes without setting timeouts or using a synchronous pattern?
As Peter commented, the right way to enforce uniqueness is to create a unique index on the collection over those fields and then handle the code: 11000 insert error to recover from attempts at creating duplicates.
You can add the index via your schema before you create the model from it:
mySchema.index({field1: 1, field2: 1}, {unique: true});