In mongo how to get the current position of the record in the table with the total records for pagination? - mongodb

I'm trying to return create a paginated list. I used graphql to query the data. With my query, I pass the number of records I need (In a variable named first) and the ID of the last fetched record (In a varible called after). Now I managed to write a query (Note that I used mongoose) to fetch the records. Now what I need to do is get the relavant information to perform the pagination like hasNextPage, hasPreviousPage, currentPage and totalPages.
To get most of these information I need to get the total number of records in the database. To do that I need to send another db request.
I also need to know the position of the record in the table. No idea how.
Here's the query:
new Promise((resolve, reject) =>
Company.where('_id')
.gt(after)
.limit(first)
.lean()
.exec((error, doc) => {
if (error) {
reject(error);
}
resolve({
edges: doc,
pageInfo: {
hasNextPage: '...',
hasPreviousPage: '...',
currentPage: '...',
totalPages: '...'
}
});
}))
Any idea how to do this efficiently?

you can try this module mongoose-paginate
here what i uses, for pagination,
var current = req.query.filter.current;
var limit = req.query.filter.limit;
console.log('params.query.filter.current',current);
var skip = Number(limit)*Number(current)-Number(limit);
console.log('skip::',skip);
Cours.find({'attributes.version.status': true}).skip(skip).limit(limit).sort({_id:'asc'}).exec(function (err, resulta) {
if (err) {
console.log('erreur trouverCours');
res.json({
protecteds: err
});
}
console.log('cours ::', resulta);
res.json({
"data": resulta
});
});

Related

Handling nested callbacks/promises with Mongoose

I am a beginner with Node.js and Mongoose. I spent an entire day trying to resolve an issue by scouring through SO, but I just could not find the right solution. Basically, I am using the retrieved values from one collection to query another. In order to do this, I am iterating through a loop of the previously retrieved results.
With the iteration, I am able to populate the results that I need. Unfortunately, the area where I am having an issue is that the response is being sent back before the required information is gathered in the array. I understand that this can be handled by callbacks/promises. I tried numerous ways, but I just haven't been successful with my attempts. I am now trying to make use of the Q library to facilitate the callbacks. I'd really appreciate some insight. Here's a snippet of the portion where I'm currently stuck:
var length = Object.keys(purchasesArray).length;
var jsonArray = [];
var getProductDetails = function () {
var deferred = Q.defer();
for (var i = 0; i < length; i++) {
var property = Object.keys(purchasesArray)[i];
if (purchasesArray.hasOwnProperty(property)) {
var productID = property;
var productQuery = Product.find({asin:
productQuery.exec(function (err, productList) {
jsonArray.push({"productName": productList[0].productName,
"quantity": purchasesArray[productID]});
});
}
}
return deferred.promise;
};
getProductDetails().then(function sendResponse() {
console.log(jsonArray);
response = {
"message": "The action was successful",
"products": jsonArray
};
res.send(response);
return;
}).fail(function (err) {
console.log(err);
})
});
I am particularly able to send one of the two objects in the jsonArray array as the response is being sent after the first element.
Update
Thanks to Roamer-1888 's answer, I have been able to construct a valid JSON response without having to worry about the error of setting headers after sending a response.
Basically, in the getProductDetails() function, I am trying to retrieve product names from the Mongoose query while mapping the quantity for each of the items in purchasesArray. From the function, eventually, I would like to form the following response:
response = {
"message": "The action was successful",
"products": jsonArray
};
where, jsonArray would be in the following form from getProductDetails :
jsonArray.push({
"productName": products[index].productName,
"quantity": purchasesArray[productID]
});
On the assumption that purchasesArray is the result of an earlier query, it would appear that you are trying to :
query your database once per purchasesArray item,
form an array of objects, each containing data derived from the query AND the original purchasesArray item.
If so, and with few other guesses, then the following pattern should do the job :
var getProductDetails = function() {
// map purchasesArray to an array of promises
var promises = purchasesArray.map(function(item) {
return Product.findOne({
asin: item.productID // some property of the desired item
}).exec()
.then(function product {
// Here you can freely compose an object comprising data from :
// * the synchronously derived `item` (an element of 'purchasesArray`)
// * the asynchronously derived `product` (from database).
// `item` is still available thanks to "closure".
// For example :
return {
'productName': product.name,
'quantity': item.quantity,
'unitPrice': product.unitPrice
};
})
// Here, by catching, no individual error will cause the whole response to fail.
.then(null, (err) => null);
});
return Promise.all(promises); // return a promise that settles when all `promises` are fulfilled or any one of them fails.
};
getProductDetails().then(results => {
console.log(results); // `results` is an array of the objects composed in getProductDetails(), with properties 'productName', 'quantity' etc.
res.json({
'message': "The action was successful",
'products': results
});
}).catch(err => {
console.log(err);
res.sendStatus(500); // or similar
});
Your final code will differ in detail, particularly in the composition of the composed object. Don't rely on my guesses.

Ionic 2 MEAN Application doesn't return updated data on get request

I've been having this weird issue with an application I'm building. Essentially a function is invoked I want to read in a user's current game statistics -Wins, losses, draws etc - I do this using a service which creates an observable and consumes data from my rest api. On first call of this method the data read in is the most current up to date version but after this point I update the document for the user in the database and then when I execute the function again it reads in the original document before the update. However when I check the database the document has in face been updated.
Here is my provider function for consuming the data.
getUser(id) {
if (this.data) {
return Promise.resolve(this.data);
}
return new Promise(resolve => {
this.http.get('https://pitchlife-hearts.herokuapp.com/api/users/' + id)
.map(res => res.json())
.subscribe(data => {
this.data = data;
resolve(this.data);
});
});
}
Here is the call I make in my function.
play(challenger, opponent) {
this.userService.getUser(_id).then((data) => {
this.challenger_account = {
_id: data._id,
points: data.maroon_points,
wins: data.wins,
draws: data.draws,
losses: data.losses
};
Here is my update call.
this.userService.updateUser(this.challenger_account);
Here is my api endpoint call as well although this does work every time I update the data.
app.post('/api/users/update', function (req, res) {
// Update a user
var options = {};
User.update({_id : req.body._id }, {
maroon_points: req.body.points,
wins: req.body.wins,
draws: req.body.draws,
losses: req.body.losses
}, options,
function (err, user) {
if (err)
res.send(err);
res.json(user);
});
});
Any help with this would be hugely appreciated as this is driving me crazy.
When are you updating the this.data property that the getUser(id) { ... } method uses?
Because the first time the getUser(id) {...} method is executed, this.data is null and because of that the http request is made. But after that, the value of this.data is always returned, but if you don't update it manually, it'll be always the first value it was set to.

What is the syntax for a mongoose query where I just want one property's value?

I am sending a query to mongoDB using mongoose. The collection is named Step. I want the result of this query to be an array of _id values, one per step. Currently I am getting all of the step objects in their entirety, because req.query isn't defined in this case.
service:
this.getSteps = function() {
return $http({
method: 'GET',
url: '/api/step'
})
.then(function(response) {
return response.data;
});
};
controller:
readStep: function (req, res) {
Step.find(req.query, function(err, result) {
if (err) {
res.status(500).send(err);
}
res.status(200).send(result);
});
}
Set the second parameter of the find query to '_id' to retrieve only the _id of the objects.
Step.find(req.query, '_id', function(err, result) {
This will return data like this:
[{_id: 123}, {_id: 234}]
If you want to get an array of the Step ids on their own, use the javascript map function like so
result = result.map(function(doc) {
return doc._id;
});
which will give you an array like this:
[123, 234]
You'll need to use query.select, something like as shown below:
Step.find(query).select({ "_id": 1}).then(....);
I'm not able to type much because I'm responding from my handheld.
Hope this help!

Is there a way to query MongoDB Rest API with a list or array of IDs

I'm using a MEAN stack and with Mongoose. Is there a way to query MongoDB with multiple ids to only return those specific IDs in one query e.g. /api/products/5001,5002,5003
Is this possible or would I need to query each product individually or add an additional attribute to the products and query by that.
Update: To clarify as suggested below I've managed to get it partially working using {'_id': { $in: [5001,5002,5003]} however I'm having problems figuring out how to pass the list from the api url to the find function.
Using Express.js for router
router.get('/list/:ids', controller.showByIDs);
exports.showByIDs = function(req, res) {
Product.find({'_id': { $in: [req.params.ids]}}, function (err, product) {
if(err) { return handleError(res, err); }
if(!product) { return res.send(404); }
return res.json(product);
})
};
Then trying /api/products/list/5001 works however /api/products/list/5001,5002 doesn't. I'm not sure if it's a syntax problem in the url or my router code that needs to change or the controller.
You can use the $in operator to query for multiple values at once:
Products.find({_id: {$in: [5001, 5002, 5003]}}, function (err, products) { ... });
On the Express side, you need to use a format for the ids parameter that lets you split it into an array of id values, like you had in your first example:
/api/products/5001,5002,5003
Then in your route handler, you can call the split function on the req.params.ids string to turn it into an array of id values that you can use with $in:
exports.showByIDs = function(req, res) {
var ids = req.params.ids.split(',');
Product.find({'_id': { $in: ids}}, function (err, product) {
if(err) { return handleError(res, err); }
if(!product) { return res.send(404); }
return res.json(product);
})
};

Looking for help with reading from MongoDB in Node.JS

I have a number of records stored in a MongoDB I'm trying to output them to the browser window by way of a Node.JS http server. I think I'm a good portion of the way along but I'm missing a few little things that are keeping it from actually working.
The code below uses node-mongo-native to connect to the database.
If there is anyone around who can help me make those last few connections with working in node I'd really appreciate it. To be fair, I'm sure this is just the start.
var sys = require("sys");
var test = require("assert");
var http = require('http');
var Db = require('../lib/mongodb').Db,
Connection = require('../lib/mongodb').Connection,
Server = require('../lib/mongodb').Server,
//BSON = require('../lib/mongodb').BSONPure;
BSON = require('../lib/mongodb').BSONNative;
var host = process.env['MONGO_NODE_DRIVER_HOST'] != null ? process.env['MONGO_NODE_DRIVER_HOST'] : 'localhost';
var port = process.env['MONGO_NODE_DRIVER_PORT'] != null ? process.env['MONGO_NODE_DRIVER_PORT'] : Connection.DEFAULT_PORT;
sys.puts("Connecting to " + host + ":" + port);
function PutItem(err, item){
var result = "";
if(item != null) {
for (key in item) {
result += key + '=' + item[key];
}
}
// sys.puts(sys.inspect(item)) // debug output
return result;
}
function ReadTest(){
var db = new Db('mydb', new Server(host, port, {}), {native_parser:true});
var result = "";
db.open(function (err, db) {
db.collection('test', function(err, collection) {
collection.find(function (err, cursor){
cursor.each( function (err, item) {
result += PutItem(err, item);
});
});
});
});
return result;
}
http.createServer(function (req, res) {
res.writeHead(200, {'Content-Type': 'text/plain'});
res.end("foo"+ReadTest());
}).listen(8124);
console.log('Server running on 8124');
Sources:
- mongo connectivity code:
https://github.com/christkv/node-mongodb-native/blob/master/examples/simple.js
- node. http code: nodejs.org
EDIT CORRECTED CODE
Thanks to Mic below who got me rolling in the right direction. For anyone interested, the corrected solution is here:
function ReadTest(res){
var db = new Db('mydb', new Server(host, port, {}), {native_parser:true});
var result = "";
res.write("in readtest\n");
db.open(function (err, db) {
res.write("now open\n");
db.collection('test', function(err, collection) {
res.write("in collection\n");
collection.find(function (err, cursor){
res.write("found\n");
cursor.each( function (err, item) {
res.write("now open\n");
var x = PutItem(err, item);
sys.puts(x);
res.write(x);
if (item == null) {
res.end('foo');
}
});
});
});
});
}
http.createServer(function (req, res) {
res.writeHead(200, {'Content-Type': 'text/plain'});
res.write("start\n");
ReadTest(res);
}).listen(8124);
console.log('Server running on 8124');
My guess is that you are returning result, writing the response, and closing the connection before anything is fetched from the db.
One solution would be to pass the response object to where you actually need it, something like:
function readTest(res) {
db.open(function (err, db) {
db.collection('test', function(err, collection) {
collection.find(function (err, cursor) {
res.writeHead(200, {'Content-type' : 'text/plain'});
cursor.each( function (err, item) { res.write(item); });
res.end();
...
Of course, you should also handle errors and try to avoid nesting too many levels, but that's a different discussion.
Instead of writing all the low-level Mongodb access code, you might want to try a simple library like mongous so that you can focus on your data, not on MongoDB quirks.
You might want to try mongoskin too.
Reading documents
To apply specific value filters, we can pass specific values to the find() command. Here is a SQL query:
SELECT * FROM Table1 WHERE name = 'ABC'
which is equivalent to the following in MongoDB (notice Collection1 for Table1):
db.Collection1.find({name: 'ABC'})
We can chain count() to get the number of results, pretty() to get a readable result. The results can be further narrowed by adding additional parameters:
db.Collection1.find({name: 'ABC', rollNo: 5})
It's important to notice that these filters are ANDed together, by default. To apply an OR filter, we need to use $or. These filters will be specified depending upon the structure of the document. Ex: for object attribute name for an object school, we need to specify filter like "school.name" = 'AUHS'
We're using here the DOT notation, by trying to access a nested field name of a field school. Also notice that the filters are quoted, without which we'll get syntax errors.
Equality matches on arrays can be performed:
on the entire arrays
based on any element
based on a specific element
more complex matches using operators
In the below query:
db.Collection1.find({name: ['ABC','XYZ']})
MongoDB is going to identify documents by an exact match to an array of one or more values. Now for these types of queries, the order of elements matters, meaning that we will only match documents that have ABC followed by XYZ and those are the only 2 elements of the array name
{name:["ABC","GHI","XYZ"]},
{name:["DEF","ABC","XYZ"]}
In the above document, let's say that we need to get all the documnts where ABC is the first element. So, we'll use the below filter:
db.Schools.find({'name.0': 'ABC' })