sorting not working in mongodb - node.js

my script :
function getAllExperiences(){
return new Promise(function(resolve,reject){
var perPage = 10
var page = req.params.page || 1
var type = req.body.type;
var sortAs = req.body.sortAs;
Experiences
.find({status:'live'})
.sort({type:sortAs})//,{duration:durationValue}
.skip((perPage * page) - perPage)
.limit(perPage)
.exec(function(err, results) {
Experiences.count().exec(function(err, count) {
if (err)
{
reject(err);
}else{
var obj =
{
experiences: results,
current: page,
pages: Math.ceil(count / perPage)
};
resolve(obj);
}
})
})
});
}
i am sorting according. price, duration, ratings
when i set
var type = 'price';
var sortAs = -1;
if i set
var type = 'price';
var sortAs = 1;
if i set
var type = 'duration';
var sortAs = -1;
or
var type = 'duration';
var sortAs = 1;
All condition gives me same result. i thing type is not accepting. what am i doing wrong.

function getAllExperiences(){
return new Promise(function(resolve,reject){
var perPage = 10
var page = req.params.page || 1
var type = req.body.type;
var sortAs = Number(req.body.sortAs);
var sort = {};
sort[type] = sortAs;
Experiences
.find({status:'live'})
.sort(sort)//,{duration:durationValue}
.skip((perPage * page) - perPage)
.limit(perPage)
.exec(function(err, results) {
Experiences.count().exec(function(err, count) {
if (err)
{
reject(err);
}else{
var obj =
{
experiences: results,
current: page,
pages: Math.ceil(count / perPage)
};
resolve(obj);
}
})
})
});
}

What you are trying to do is
{ type: sortAs }
where type is being used as object key, what you should do is
{ [type]: sortAs }
Here type is being used as variable to create dynamic key for the object. thus this will create.
{duration: "-1"}
Following code should work for you.
function getAllExperiences(){
return new Promise(function(resolve,reject){
var perPage = 10
var page = req.params.page || 1
var type = req.body.type;
var sortAs = req.body.sortAs;
Experiences
.find({status:'live'})
.sort({ [type] : sortAs })//, using es6 for generating dynamic property
.skip((perPage * page) - perPage)
.limit(perPage)
.exec(function(err, results) {
Experiences.count().exec(function(err, count) {
if (err)
{
reject(err);
}else{
var obj =
{
experiences: results,
current: page,
pages: Math.ceil(count / perPage)
};
resolve(obj);
}
})
})
});
}

You should change .sort({type:sortAs})//,{duration:durationValue} to
var sortDefinition = {};
sortDefinition[type] = sortAs;
//then in pipeline
.sort(sortDefinition)
Because in your example it will always try to sort by field with name typesince it's a key name in that code.

Related

updating related models from parent model

I am trying to create a related model via the parent model using the uRL of the parent model
Parent : Applications
Related : Applicants
This is the below relationship
applications --> hasMany -- > applicants ( foreignkey : application
id )
applicants --> BelongsTo --> applications (foreignkey : applicationid )
I want to do an upsert on both the models togther using the url of the parent model
PATCH /api/applications
Below is the applications.js file
module.exports = function(Application)
{
Application.afterRemote('upsert', function(ctx, instance, next){
var response ={};
response.id = ctx.result.id;
var applicants = ctx.req.body.applicants || undefined;
Application.getApp(function (err, app) {
var resp = { "applicants" :[]};
if (applicants){
for (var i=0; i<applicants.length ; i++)
{
applicants[i].application_id = ctx.result.id;
app.models.Applicants.upsert(applicants[i], function (err, result)
{
if (err) throw err;
if (!result) {
var err = new Error("Insert into Applicant failed");
err.statusCode = 500;
next(err);
}
// This prints the result in the console.
console.log('***** In APP' + JSON.stringify(result));
resp.applicants.push(result);
});
console.log(JSON.stringify(applicants));
}
// This still results in a empty array
response.applicants = resp.applicants;
ctx.result = response;
}
});
next();
});
How can I fetch the result of the upsert to the applicants model and then send it back in the application response for the api.
Thanks
This is what I would do. There is certainly a better way thought but it might be a good start.
'use strict';
var app = require('../../server/server');
var models = app.models;
var Applicants;
app.on('started', function () {
Applicants = models.Applicants;
});
module.exports = function (Application)
{
Application.afterRemote('upsert', function (ctx, instance, next) {
var response = {};
response.id = ctx.result.id;
var applicants = ctx.req.body.applicants || undefined;
if (applicants) {
var resp = {"applicants": []};
var count = 0;
for (var i = 0, applicantsLength = applicants.length; i < applicantsLength; i++) {
applicants[i].application_id = ctx.result.id;
Applicants.upsert(applicants[i], function (err, result) {
if (err)
return next(err);
if (!result) {
var err = new Error("Insert into Applicant failed");
err.statusCode = 500;
return next(err);
}
resp.applicants.push(result);
count++;
if (count === applicantsLength) {
response.applicants = resp.applicants;
ctx.result = response;
next();
}
});
}
} else {
next();
}
});
};
If this is not working, you should look for the 'before save' hook.

display 2 two table records in nodejs

I have tow MongoDB collection.
1) users
2) reviews.
at the first collection, I have stored username and id. and 2nd table I have stored user_id and comments and star rating.
I want to display on listing page users and his reviews.
I have written below code but it is not working.
var getTopSellers = function () {
var defer = Q.defer();
User.find({ isRegistered: true }).sort({ updatedAt: -1 }).limit(10).exec(function (err, sellers) {
if (!err) {
if (sellers && sellers.length > 0) {
for (var i = 0; i < sellers.length; i++) {
var sellerDetails = {};
var tempObj = {};
try {
tempObj.reviews = getreviews(sellers[i]._id);
sellerArr.push(tempObj);
} catch (e) {
// console.log("catch error:-", e);
}
}
out = U.getJson(C.SUCCESS_CODE, C.SUCCESS, sellerArr);
defer.resolve(out);
} else {
out = U.getJson(C.KNOWN_ERROR_CODE, 'No data found');
defer.reject(out);
}
} else {
console.log("Error:-", err);
out = U.getJson(C.ERROR_CODE, C.INETRNAL_SERVER_ERROR, b, err);
defer.reject(out);
}
})
return defer.promise;
};
var getreviews = function (user_id) {
var defer = Q.defer();
Review.find({ user_type: user_id }).sort({ updatedAt: -1 }).limit(10).exec(function (err, reviews) {
if (!err) {
if (reviews && reviews.length > 0) {
out = U.getJson(C.SUCCESS_CODE, C.SUCCESS, reviews);
defer.resolve(out);
} else {
out = U.getJson(C.KNOWN_ERROR_CODE, 'No data found');
defer.reject(out);
}
} else {
console.log("Error:-", err);
out = U.getJson(C.ERROR_CODE, C.INETRNAL_SERVER_ERROR, b, err);
defer.reject(out);
}
})
return defer.promise;
};
Please suggest

async each is running to fast?

I have the following code running through an object with async:
async.each(Object.keys(shopList), function(key, callback){
var shop = shopList[key];
saveOrder(payId, shopList[key], key, req.body, req.user, function(err, newOrder){
if (err) {
callback(err);
}else{
orderCount++;
console.log("succes!", orderCount, newOrder.number);
callback();
}
})
}, function(err){
if (err) {
console.log("ERROR!", err);
}else{
console.log("done!");
}
})
In this function a another function is called. This code looks like this:
saveOrder = function(payId, shop, nameSlug, body, user, callback){
console.log("saveOrder");
var orderNumber = 0;
Order.findOne().sort({_id:-1}).exec(function(err, latestOrder) {
if(latestOrder.number){
orderNumber = latestOrder.number.split("-")[1];
}
var order = new Order();
var date = new Date();
order.number = date.getFullYear().toString() + date.getMonth().toString() + "-" + (parseInt(orderNumber)+1);
order.date = date;
order.payId = payId;
order.status = {
status: "Created",
comment: "",
date: new Date()
};
order.comment = body.comment;
order.shop = {
name: shop.name,
nameSlug: nameSlug
}
order.billingDetails = {
//order details
}
order.sendDetails = {
//more order details
}
order.user = {
//yep, order details
}
var orderItems = [];
for(p = 0; p < shop.items.length; p++){
var product = shop.items[p];
var orderItem = {
_id: product._id,
name: product.name,
brand: product.brand[0].name,
price: product.price,
quantity: product.quantity
}
orderItems.push(orderItem);
}
order.items = orderItems;
order.save(function(err, result){
if (err){
console.log("err!", err);
return callback(err)
}else{
console.log("saved!");
return callback(null, result);
}
})
})
}
The problem is in the last function. There I try to create a ordernumber which must be unique. I get the last order, split the ordernumber and do a +1.
When I have more objects in my shopList, this function is triggered when he is not ready. With other words, the first order isn't saved then, and I will get the same ordernumber.
How can I fix this? I tried a setTimeout in the async.each but that isn't working.
You could use a mutex using locks.
The callbacks will wait that the mutex is unlocked to lock it making that you won't have simultaneous executions.
var locks = require('locks');
var mutex = locks.createMutex();
saveOrder = function(payId, shop, nameSlug, body, user, callback){
mutex.lock(function () {
console.log("saveOrder");
var orderNumber = 0;
Order.findOne().sort({_id:-1}).exec(function(err, latestOrder) {
if(latestOrder.number){
orderNumber = latestOrder.number.split("-")[1];
}
var order = new Order();
var date = new Date();
order.number = date.getFullYear().toString() + date.getMonth().toString() + "-" + (parseInt(orderNumber)+1);
order.date = date;
order.payId = payId;
order.status = {
status: "Created",
comment: "",
date: new Date()
};
order.comment = body.comment;
order.shop = {
name: shop.name,
nameSlug: nameSlug
}
order.billingDetails = {
//order details
}
order.sendDetails = {
//more order details
}
order.user = {
//yep, order details
}
var orderItems = [];
for(p = 0; p < shop.items.length; p++){
var product = shop.items[p];
var orderItem = {
_id: product._id,
name: product.name,
brand: product.brand[0].name,
price: product.price,
quantity: product.quantity
}
orderItems.push(orderItem);
}
order.items = orderItems;
order.save(function(err, result){
if (err){
console.log("err!", err);
return callback(err)
}else{
console.log("saved!");
return callback(null, result);
}
})
})
mutex.unlock(); //don't forget to unlock the mutex
});
}
You should use async.waterfall instead of async.each, because:
async.waterfall - runs the tasks array of functions in series, each passing their results to the next in the array. http://caolan.github.io/async/docs.html#waterfall
async.each - applies the function iteratee to each item in coll, in parallel.
Fixed this issue with using eachSeries() instead of each()
http://caolan.github.io/async/docs.html#eachSeries

Sorting MapReduce results on MongoDB

I have my MapReduce working correctly to group my results by date. All works well, however I'd like to have the results to be returned from Most Recent to Oldest based on 'created' value.
findTimelineByQuery: function (query, fields, options, callback) {
var obj = {};
obj.map = function() {
emit(Date.UTC(this.created.getFullYear(), this.created.getMonth(), this.created.getDate()), {
created:this.created,
title:this.title,
type: this.type,
id: this._id,
owner: this.owner,
value: this.value
});
};
obj.reduce = function(previous, current) {
var array = [];
var res = {items:array};
current.forEach(function (v) {
res.items.push(v);
});
return res;
};
obj.verbose = true;
obj.query = query;
_Items.mapReduce(obj, function(error, model, stats) {
callback(model);
});
}

Node.js callback with MongoDB update never returns although it updates DB

I am in the market for a new vehicle. Instead of repeatedly searching the dealerships websites, I thought this would be an interesting and fun opportunity to learn a little node and mongodb so I'm scraping my local dealerships' websites to grab the makes and models that I am interested in.
The problem that I am running into is that node won't terminate after my final callback has run through.
var cheerio = require('cheerio');
var request = require('request');
var db = require('mongodb');
var S = require('string');
var log = require('console').log;
var async = require('async');
var links = [];
var website = 'http://www.yahoo.com';
async.series(
[
function(){
log('starting');
db.connect('mongodb://127.0.0.1:27017/test',
function(err, base){
if(err) throw err;
db = base;
});
},
request(website, start)
],
function(){
log('closing DB');
db.close();
});
function start(err,resp,body){
var $ = cheerio.load(body);
var numPages = 2;
$('.gbps').each(function(i,elem) {
links.push('http://www.yahoo.com');
});
var pageURLS = [];
for (var i = 2; i<=numPages; i++){
//create URLs for additional pages
pageURLS[i-2] = website;
}
var pages = 1;
log('getting page URLs');
pageURLS.forEach(function(url, index, array){
request(url, function(error,response,bodies) {
pages++;
var $ = cheerio.load(bodies);
$('.tab').each(function(i,elem) {
links.push('http://www.yahoo.com');
});
if (pages == numPages){
getDetailInfo();
};
});
});
}
function getDetailInfo(){
log(links.length);
links.forEach(function(link, index, array){
request(link, doStuff);
});
}
function doStuff(err, response, body){
if(err){
log(err);
}
parseDetailResponse(err,response,body, addToDB);
}
function parseDetailResponse(err,resp,body,callback){
log('parsing');
var $ = cheerio.load(body);
var specs = $('.specifications').children().map(function(i, elem){
var key = 'key';
var value = 'value';
var ret = {};
ret [ 'name' ] = key;
ret [ 'value' ] = value;
return ret;
});
var makeAndModel = 'makeAndModel';
callback(['picture url', 'vehicle description', 100, specs, makeAndModel]);
}
function getMakeAndModel(stuff){
var $ = cheerio.load(stuff);
temp = $('.gbps').map(function(i, elem){
var ret = {};
switch(i){
case 0:
ret['name'] = 'year';
ret['value'] = $(this).text();
break;
case 1:
ret['name'] = 'make';
ret['value'] = $(this).text();
break;
case 2:
ret['name'] = 'model';
ret['value'] = $(this).text();
break;
case 3:
ret['name'] = 'ignore';
ret['value'] = $(this).text();
break;
default:
ret['name'] = 'ignore';
ret['value'] = 'ignore';
}
return ret;
});
return temp;
}
function addToDB(arr){
log('adding to DB');
pic = arr[0];
description = arr[1];
price = arr[2];
specs = arr[3];
makeAndModel = arr[4];
var obj = {};
for (var i = specs.length - 1; i >= 0; i--) {
obj [specs[i].name] = specs[i].value;
};
for (var i = makeAndModel.length - 1; i >= 0; i--){
obj [makeAndModel[i].name] = makeAndModel[i].value;
};
db.collection('carsTest').update(
{VIN: obj.VIN},
{
$set: {
VIN: obj.VIN,
make: obj.make,
model: obj.model,
year: obj.year,
price: price,
engine: obj.Engine,
interior: obj.Interior,
exterior: obj.Exterior,
'model code': obj['Model Code'],
'stock number': S(obj['Stock Number']).toInt(),
transmission: obj.Transmission,
mileage: obj.Mileage ? obj.Mileage : 0,
description: description,
picture: pic,
}
},
{upsert: true, safe: true},
function(err,result){
if(err){
throw err;
}
});
log('finished with this one!');
}
I've omitted and changed a fair amount as a proof here without a lot of error checking or anything but even this will add the document but won't quit. Node just sits there, waiting for something to happen and it never calls the final callback to close the db and exit.
> db.carsTest.find().pretty()
{
"_id" : ObjectId("52139aa7c9b7a39e0f1eb61d"),
"VIN" : null,
"description" : "vehicle description",
"engine" : null,
"exterior" : null,
"interior" : null,
"make" : null,
"mileage" : 0,
"model" : null,
"model code" : null,
"picture" : "picture url",
"price" : 100,
"stock number" : NaN,
"transmission" : null,
"year" : null
}
I think that you misunderstand how async.series works.
Your functions in async.series don't take callback as an argument and they don't call it. And that request(...) stuff is probably not a function at all. That's probably why it breaks async loop. Try this:
async.series(
[
function(callback) { // <--- missing callback
log('starting');
db.connect('mongodb://127.0.0.1:27017/test',
function(err, base){
if(err) throw err;
db = base;
callback(); // <--- missing callback
});
},
function(callback) { // <--- missing function with callback
request(website, function(err,resp,body) {
start(err, resp, body, callback);
})
}
],
function(){
log('closing DB');
db.close();
}
);
Note that I've added callback argument when calling start. Thus you will have to refactor your code havily so that every function accepts callback which can be called at the end when you know that all jobs are done. For example you can add async.parallel inside start and this function may look like this:
function start(err, resp, body, callback) {
// some stuff happens here
var jobs = []
pageURLS.forEach(function(url, index, array){
jobs.push(function(clb) {
request(url, function(error,response,bodies) {
// some stuff
clb(); // <--- this refers to the local callback for the job
});
});
});
async.parallel(jobs, function() {
// all jobs are done, let's finilize everything
callback();
});
};

Resources