How to efficiently bulk insert and update mongodb document values from an array? - node.js

I have a Tags collection which contains documents of the following structure:
{
word:"movie", //tag word
count:1 //count of times tag word has been used
}
I am given an array of new tags that need to be added/updated in the Tags collection:
["music","movie","book"]
I can update the counts all Tags currently existing in the tags collection by using the following query:
db.Tags.update({word:{$in:["music","movies","books"]}}, {$inc:{count:1}}), true, true);
While this is an effective strategy to update, I am unable to see which tag values were not found in the collection, and setting the upsert flag to true did not create new documents for the unfound tags.
This is where I am stuck, how should I handle the bulk insert of "new" values into the Tags collection?
Is there any other way I could better utilize the update so that it does upsert the new tag values?
(Note: I am using Node.js with mongoose, solutions using mongoose/node-mongo-native would be nice but not necessary)
Thanks ahead

The concept of using upsert and the $in operator simultaneously is incongruous. This simply will not work as there is no way to different between upsert if *any* in and upsert if *none* in.
In this case, MongoDB is doing the version you don't want it to do. But you can't make it change behaviour.
I would suggest simply issuing three consecutive writes by looping through the array of tags. I know that's it's annoying and it has a bad code smell, but that's just how MongoDB works.

Related

how can i find as well as update many documents?

I want to update the results of find query on certain conditions.now what i was thinking it that will mongodb will search whole collection for find and update or use pointer from the previous find query.I just wanted to optimism my queries that's why i was thinking about it.so is there anyway to achieve this?
update:I also want the documents.
ex-collection.find({conditions}).foreach({some condition based on which update will be called})
now what i want is that update query which will be called from foreach function uses pointer from previous find query rather than searching through the collection again.
my point is when we first use find query we search the collection and a cursor is returned which is a pointer to collection in memory.now that we have that pointer why can't we use that to update the document rather than again searching the collection and then updating it.
If you want to keep your code you can use:
collection.find({conditions}).foreach((doc) => {
if (some_conditions) {
return collection.findOneAndUpdate({_id: doc._id}, {$set: {updated_fields});
}
})
but as mentioned in the comments i'm not sure exactly what conditions need to be met but you probably can just use the update method to save time.

How to modify array single subdocument without loading that entire array in-memory?

I'm using Node.js v8.12.0, MongoDB v4.0.4 & Mongoose v5.3.1.
I want to update an embedded array subdocument without changing its position in array index. And without loading the entire array in memory because that array might get very big in future.
I spent a lot of time searching how to achieve this but without luck.
I've tried to use Mongo's $elemMatch to load the document with only single relevant array subdocument, but that wouldn't let me save the subdocument in any practical way.
I've also looked into Mongoose array set method, but for this you need to have subdocument array index, so this mean I need to load the entire array into a memory and then find subdocument index.
Is there any way to achieve what I want? Ideally via Mongoose abstractions, so all Mongoose middleware like validations, plugins, etc. kick in.
Actually found the answer right after submitting this question.
For anyone looking it up in future, you can use special $ operator in combination with $set. (I'm not sure if that makes use of mongoose middlewares, though. I need to do some testing around that).
Here's some code example, which queries some subdocument using _id, and then updates that subdocument property someProperty using $set which uses special operator $ that matches queried subdocument array position/index.
var doc = await SomeModel.findOneAndUpdate(
{'someArray._id': 'xxx'},
{'$set': {'someArray.$.someProperty': 'someValue'}},
);
It's still not 100% what I'm looking for, though. Because the returned doc still has full array of subdocuments (I assume that's still loaded into memory?), and it seems that I can't use $elemMatch in combination with $set.
In my case, further down the road, I'll probably redesign the database schema so these embedded subdocuments will be their own documents with just ObjectID reference to "parent" document.
Some resources:
https://docs.mongodb.com/manual/reference/operator/update/positional/
https://mongoosejs.com/docs/api.html#query_Query-findOneAndUpdate

mongodb findAndUpdate : get original document value

We are trying to implement versioning of documents in mongodb for our project using nodejs. For this, while we perform the findOneAndUpdate command we want to find the difference between the existing document and the new values being updated into it. The returnNewDocument parameter returns the new document after updating is there any way to get the base document ( the one before update is performed). Or is there any better way to perform mongodb document versioning?

Mongoose bulk insert or update documents

I am working on a node.js app, and I've been searching for a way around using the Model.save() function because I will want to save many documents at the same time, so it would be a waste of network and processing doing it one by one.
I found a way to bulk insert. However, my model has two properties that makes them unique, an ID and a HASH (I am getting this info from an API, so I believe I need these two informations to make a document unique), so, I wanted that if I get an already existing object it would be updated instead of inserted into the schema.
Is there any way to do that? I was reading something about making concurrent calls to save the objects, using Q, however I still think this would generate an unwanted load on the Mongo server, wouldn't it? Does Mongo or Mongoose have a method to bulk insert or update like it does with insert?
Thanks in advance
I think you are looking for the Bulk.find(<query>).upsert().update(<update>) function.
You can use it this way:
bulk = db.yourCollection.initializeUnorderedBulkOp();
for (<your for statement>) {
bulk.find({ID: <your id>, HASH: <your hash>}).upsert().update({<your update fields>});
}
bulk.execute(<your callback>)
For each document, it will look for a document matching the {ID: <your id>, HASH: {your hash}} criteria. Then:
If it finds one, it will update that document using {<your update fields>}
Otherwise, it will create a new document
As you need, it will not make a connection to the mongo server on each iteration of the for loop. Instead a single call will be made on the bulk.execute() line.

Batch update with Mongoose

I'm pulling data from a RETS(XML) feed and saving it in a local MongoDB using node and mongoose.
Periodically I need to update the documents and delete the inactive ones as well as add new ones. Rather than making multiple queries to Mongo or the RETS server, I was pulling both and looping through the data.
This works fine but is there a way to save the Mongoose results back to the database with updates and inserts? Or do I need to find each document and update it individually?
On MongoDB, to update multiple documents (not just one) using Mongoose you can use the multi option:
Model.updateMany({
size: 'lage'
}, {
$set: { size: 'large' }
});
See more on in the Mongoose documentation for updating documents and here
For completeness, If any one has multiple query conditions and want to add new fields for every matching documents of query condition then we can go with
var bulk = Person.collection.initializeUnorderedBulkOp();
bulk.find(query1).update(update1);
bulk.find(query2).update(update2);
bulk.execute(callback);
In following documentation, it is said that db.collection.initializeUnorderedBulkOp()
Initializes and returns a new Bulk() operations builder for a
collection. The builder constructs an unordered list of write
operations that MongoDB executes in bulk. MongoDB executes in
parallel the write operations in the list.
https://docs.mongodb.org/v3.0/reference/method/db.collection.initializeUnorderedBulkOp/

Resources