How do MongoDB cursors work when used with Node.js? - node.js

I am using Node.js with the npm package mongodb. When I use findOne(...), I get a result which is directly the item I searched for. When I use find(...) instead, I don't get an array of elements, I get a cursor which looks very weird if you console.log it.
My question is why does it return a cursor instead of the array of elements and is the cursor.forEach(...) call then asynchronous or how can the client get data out of the cursor?

It returns a cursor instead of an array to provide flexibility to the client to access the results in whatever way is optimal for its needs.
To get an array of all results, you can call the async toArray method on the cursor:
collection.find({...}).toArray((err, docs) => {...});
Same thing for aggregate:
collection.aggregate([{$match: {...}}]).toArray((err, docs) => {...});

Related

How to find multiple mongo db objects at once in node js

i am trying to run a search , it is working fine with findOne and recieving data but when i use find it just return some headers and prototypes like this
response i am getting
code
let data = await client
.db("Movies")
.collection("movies")
.find();
console.log(data);
please tell me where i am going wrong
Based on documentation
The toArray() method returns an array that contains all the documents from a cursor. The method iterates completely the cursor, loading all the documents into RAM and exhausting the cursor.
so just try
let data = await client
.db("Movies")
.collection("movies")
.find({}).toArray();
console.log(data);

Calling a function that returns a AsyncIterableIterator without using "for await" block

I'm writing an AWS Lambda function in TypeScript using the Node.js runtime. I'm using a "batchDelete" function from a DynamoDB ORM library which returns an AsyncIterableIterator type.
According to the documentation here https://github.com/awslabs/dynamodb-data-mapper-js#batchDelete, I should invoke the method with a for await loop like this:
for await (const found of mapper.batchDelete(toRemove)) {
// items will be yielded as they are successfully removed
}
This all works great but the problem comes in where if I enable ESLint on my project. The default rules throw an error because the for await block is empty. I also get a warning because the found constant is never used. I have no use for the found constant and don't want to log it. I was wondering if there was another way to call an AsyncIterableIterator function where we disregard what is returned and don't have the empty block?
If you don't care about the results of the iteration, then you should probably just do something like this:
await Promise.all(toRemove.map(item => mapper.delete(item));
To use the mapper.batchDelete(toRemove) result more directly, you have to allow for multiple levels of promises. Perhaps you could do this:
await Promise.all(await mapper.batchDelete(toRemove)[Symbol.asyncIterator]());
In doing this, await mapper.batchDelete(toRemove)[Symbol.asyncIterator](), that would get you the default async Iterator and then passing it to Promise.all() would iterate it to get an iterable of promises. Unfortunately, in building it to make this easier:
for await (const found of mapper.batchDelete(toRemove))
they made it a bit more difficult to just get an array of promises out of it.
FYI, here's a link to the code for the .batchDelete() method if you want to look at how it's implemented.

Adding a value from Mongoose DB into a variable in Node.js

I am still quite new to Node.js and can't seem to find anything to help me around this.
I am having an issue of getting the query from my last record and adding it to my variable.
If I do it like below: -
let lastRecord = Application.find().sort({$natural:-1}).limit(1).then((result) => { result });
Then I get the value of the variable showing in console.log as : -
Promise { <pending> }
What would I need to do to output this correctly to my full data?
Here is it fixed:
Application.findOne().sort({$natural:-1}).exec().then((lastRecord) => {
console.log(lastRecord); // "lastRecord" is the result. You must use it here.
}, (err) => {
console.log(err); // This only runs if there was an error. "err" contains the data about the error.
});
Several things:
You are only getting one record, not many records, so you just use findOne instead of find. As a result you also don't need limit(1) anymore.
You need to call .exec() to actually run the query.
The result is returned to you inside the callback function, it must be used here.
exec() returns a Promise. A promise in JavaScript is basically just a container that holds a task that will be completed at some point in the future. It has the method then, which allows you to bind functions for it to call when it is complete.
Any time you go out to another server to get some data using JavaScript, the code does not stop and wait for the data. It actually continues executing onward without waiting. This is called "asynchronisity". Then it comes back to run the functions given by then when the data comes back.
Asynchronous is simply a word used to describe a function that will BEGIN executing when you call it, but the code will continue running onward without waiting for it to complete. This is why we need to provide some kind of function for it to come back and execute later when the data is back. This is called a "callback function".
This is a lot to explain from here, but please go do some research on JavaScript Promises and asynchronisity and this will make a lot more sense.
Edit:
If this is inside a function you can do this:
async function someFunc() {
let lastRecord = await Application.findOne().sort({$natural:-1}).exec();
}
Note the word async before the function. This must me there in order for await to work. However this method is a bit tricky to understand if you don't understand promises already. I'd recommend you start with my first suggestion and work your way up to the async/await syntax once you fully understand promises.
Instead of using .then(), you'll want to await the record. For example:
let lastRecord = await Application.find().sort({$natural:-1}).limit(1);
You can learn more about awaiting promises in the MDN entry for await, but the basics are that to use a response from a promise, you either use await or you put your logic into the .then statement.

Mongoose js batch find

I'm using mongoose 3.8. I need to fetch 100 documents, execute the callback function then fetch next 100 documents and do the same thing.
I thought .batchSize() would do the same thing, but I'm getting all the data at once.
Do I have to use limit or offset? If yes, can someone give a proper example to do it?
If it can be done with batchSize, why is it not working for me?
MySchema.find({}).batchSize(20).exec(function(err,docs)
{
console.log(docs.length)
});
I thought it would print 20 each time, but its printing whole count.
This link has the information you need.
You can do this,
var pagesize=100;
MySchema.find().skip(pagesize*(n-1)).limit(pagesize);
where n is the parameter you receive in the request, which is the page number client wants to receive.
Docs says:
In most cases, modifying the batch size will not affect the user or the application, as the mongo shell and most drivers return results as if MongoDB returned a single batch.
You may want to take a look at streams and perhaps try to accumulate subresults:
var stream = Dummy.find({}).stream();
stream.on('data', function (dummy) {
callback(dummy);
})

Node MongoDB execution order

I'm using collection.insert then in the callback collection.findAndModify
About 10% of the time collection.findAndModify fails to find the document I just inserted, even though it's being executed after the insert callback. Why is this possible? How do I deal with that?
I insert it, then try to modify it, but it's not there, then try to modify it again and it's there.
You should give the second command in the callback as the insert is asynchronous. If you are using the mongodb native driver for node,
collection.insert(document, function(err, records){
//call collection.findAndModify here
});
Check the docs

Resources