I want to implement whole search feature in Mongodb collection using Nodejs.
What should I pass to SaleModel.find() so given value search in whole collection?
Here is what I have try so for but it only search product_name and I want to search sale_amount, sale_person, department_name too.
How I can do this?
SaleModel.find({'product_name': 'searched value'});
Schema:
var saleSchema = mongoose.Schema({
product_name:{ type:String, required:true},
sale_amount:{ type:Number, required:true },
sale_date:{ type:Date, default:Date() },
sale_person:{ type:String, required:true },
department:{ type:mongoose.Schema.Types.ObjectId, ref:'department' },
});
module.exports = mongoose.model('sale', saleSchema);
I would highly write more cleaner, below sample uses async.parallel, Promise and Mongoose.Query
function list(req) {
// promise or callback works as well
return new Promise(function(resolve, reject){
// npm install async --save
var async = require('async');
// some validation can be applied
var page = {
skip: req.query.start || 1,
limit: req.query.length || 25,
text: req.query.search || '' // <== this is new property!
};
// reuse Mongoose.Query with search by regex
var Query = Models.SaleModel.find({
product_name: new RegExp(page.text, "i")
});
// run without waiting until the previous function has completed
async.parallel([
function(){
Query.count(callback); // <== count
},
function(){
Query.skip(page.skip).limit(page.limit).exec('find', callback); // <== items
// or the below one should also work, just don't remember
// Query.skip(page.skip).limit(page.limit).exec(callback);
}
]), function(err, results){
if(err){
reject(err);
} else {
resolve({
count: results[0],
data: results[1]
});
}
});
});
}
Related
I'm building a restful api using node express mongoose/mongo etc.. I'm trying to output an array of users that are being followed by a particular user. Here is the schema.
var UserSchema = new mongoose.Schema({
username: {type: String, lowercase: true, unique: true, required: [true, "can't be blank"], match: [/^[a-zA-Z0-9]+$/, 'is invalid'], index: true},
email: {type: String, lowercase: true, unique: true, required: [true, "can't be blank"], match: [/\S+#\S+\.\S+/, 'is invalid'], index: true},
bio: String,
image: String,
following: [{ type: mongoose.Schema.Types.ObjectId, ref: 'User' }]
}, {timestamps: true});
So every User has array of users in an array in the key 'following'. I'm trying to output that list by first finding the user record through it's own id and then mapping through this array to find the followed users for this current user.
router.get('/users/friends', auth.required, function(req, res, next) {
var limit = 20;
var offset = 0;
if(typeof req.query.limit !== 'undefined'){
limit = req.query.limit;
}
if(typeof req.query.offset !== 'undefined'){
offset = req.query.offset;
}
User.findById(req.payload.id)
.then(function(user){
if (!user) { return res.sendStatus(401); }
return res.json({
users: user.following.map(function(username){
User.findById(username)
.then(function(userlist){
console.log('userlist:',userlist.username);
return userlist.username;
})
.catch(next)
})
})
})
.catch(next);
});
Now the console.log in this code outputs the correct data in the js console but I can't seem to find a way to deliver this to the client. So far my efforts bring forth 'null' values in the client. The correct amount of records but just null values. Any ideas how to fix this?
revised my code to this after taking advice below. Now it manages to get the first record to the client but then errors out with
UnhandledPromiseRejectionWarning: Unhandled promise rejection (rejection id: 1): Error: Can't set headers after they are sent.
Blockquote
router.get('/users/friends', auth.required, function(req, res, next) {
var limit = 20;
var offset = 0;
if (typeof req.query.limit !== 'undefined') {
limit = req.query.limit;
}
if (typeof req.query.offset !== 'undefined') {
offset = req.query.offset;
}
User.findById(req.payload.id)
.then(function(user) {
if (!user) {
return res.sendStatus(401);
}
Promise.all(
user.following
).then(function(userarray) {
console.log(userarray);
userarray.forEach(function(userid) {
Promise.all([
User.find({
_id: {
$in: userid
}
})
.limit(Number(limit))
.skip(Number(offset))
.populate('author')
.exec()
]).then(function(results) {
userdetails = results[0];
var userdetailsCount = results[1];
return res.json({
userdetails: userdetails.map(function(userdetail){
return userdetail;
})
});
})
})
})
})
.catch(next);
});
Your problem section is:
return res.json({
users: user.following.map(function(username){
User.findById(username)
.then(function(userlist){
console.log('userlist:',userlist.username);
return userlist.username;
})
.catch(next)
})
})
The bit User.findById(username) will return a promise. But you are not awaiting on that promise. I'm guessing you think that the then function following that promise, which logs userlist.username to the console and returns it, should mean that your map function returns a list of userlist.username's. But this is not the case. Your map function is returning an array of promises.
What you really want is a feature like Bluebird's Promise.map: http://bluebirdjs.com/docs/api/promise.map.html (or, look for a similar feature, to deal with arrays of promises, in whichever promise library you happen to be using).
I am using Angular and have to populate a table and for this i need an array which has values by multiple calls to server.
I have following scenerio
Angular Controller:
var application={};
var data-[];
$scope.tableData=[];
Restangular.all("a").getList().then(function(arr1){
for(var i=0;i<arr1.length;i++)
{
application.app=arr1[i];
Restangular.one("b",arr1[i].id).get().then(function(obj1){
application.obj1=obj1;
});
Restangular.one("c",arr1[i].id).get().then(function(obj2){
application.obj2=obj2;
});
data.push(application);
application={};
if(i==arr1.length-1){
$scope.tableData=data;
}
}
});
Now the table in view shows row equal to length of arr1 and also shows the data in arr1 but the data by other Restangular calls are not attached with that array except last iteration.
OnlyiIn last iteration the array is fully made including arr1,obj1,obj2 other array indexes r missing obj1 n obj2.
This is because of async behaviour of Restangular response but cannot understand how to handle it.
Note:
(Expected result)
data[
{
app:{
app_id:1,
status:1
},
obj1:{
name:"user1",
gender:"male"
},
obj2:{
telephone:"63532367",
address:"abc"
}
},
{
app:{
app_id:2,
status:1
},
obj1:{
name:"user2",
gender:"female"
},
obj2:{
telephone:"63532367",
address:"xyz"
}
},{
app:{
app_id:3,
status:1
},
obj1:{
name:"user3",
gender:"female"
},
obj2:{
telephone:"63532367",
address:"xyz"
}
}
]
(Current Result)
data[
{
app:{
app_id:1,
status:1
}
},
{
app:{
app_id:2,
status:1
}
},
{
app:{
app_id:3,
status:1
},
obj1:{
name:"user3",
gender:"female"
},
obj2:{
}
}
]
Restangular is async (all the Restangular.one() is left before the callback you pass to .then). That's why Promises is used.
Even if it would be possible to make Restangular to be sync you shouldn't do that because this will block the browser until the data is requested which will be a bad user experience.
You should try to get into Promise as they were designed to look like sync code but behave async.
You can try something like this:
var a = Restangular.all("a").getList().then(function(arr1){
// Some modification of the backend data response
return modifiedData; // Now this should be passed to each of those Restanngular.one() methods sequentially
});
Above code will return the Promise that is returned by the .then call, which can be chained as following concept:
(new Promise(function( resolve, reject){
$timeout(function() {
resolve("some");
});
}))
.then(function(data) {
return data+' data';
})
.then(function(data) {
return new Promise(function(resolve, reject) {
$timeout(function() {
resolve(data+' !!!!!!');
});
});
})
.then(function(data) {
// this will have 'some data !!!!!!'
console.log(data);
});
var awsSdk = require('aws-sdk');
awsSdk.config = {
"accessKeyId": "key",
"secretAccessKey": "secret",
"region": "us-east-1"
}
var s3 = new awsSdk.S3({
accessKeyId: 'key',
secretAcessKey: 'secret'
});
exports.awsDelete = function(req, res){
s3.deleteObject({
Bucket: 'bucket',
Key: req.body.photo
}, function(err,data){
if (err) console.log('delete err', err);
console.log(data);
});
};
I can't figure out how to make this work (yet).
initially, I was getting a "no config" error, so I added the awsSdk.config json above. Now, it's just getting hung / pausing with no error. I am getting the expected key in req.body.photo.
My hunch is that i'm missing something in my config..
What am I missing / screwing up?
Update
I've added the code suggested below, but still no luck. I'll show how i'm passing my parameter:
updated code from answer below:
'use strict';
var aws = require('./aws');
var amazon = require('aws-sdk');
amazon.config = new amazon.Config();
amazon.config.accessKeyId = aws.key;
amazon.config.secretAccessKey = aws.secret;
amazon.config.region = aws.region;
var s3 = new amazon.S3();
exports.awsDelete = function(req, res){
var params = {
Bucket: aws.bucket,
Key: res.body.photo
};
s3.deleteObject(params, function(err, data) {
if (err) console.log(err)
else console.log("Successfully deleted myBucket/myKey");
});
};
route:
app.post('/awsDelete', uploads.awsDelete);
Front end Angular:
factory:
angular.module('clientApp').factory('Uploads', function($http) {
return {
delete: function(data){
console.log('delete fired');
return $http.post('/awsDelete', data);
}
};
});
angular controller:
angular.module('clientApp').controller('Distiller-editCtrl', function(Uploads){
$scope.item = {}
$scope.delete = function(){
Uploads.delete($scope.item).then(function(res){
console.log(res)
});
};
});
Seems it 'sort of works'. But something is making it take an extremely long time:
POST /awsDelete 200 120007ms
If I refresh the page, that causes it to successfully delete it as well.
Does anyone notice anything in my code that could be causing such a long response time.
Also, not getting the "successfully completed" console.log
I just tested this in node and it worked fine, obviously you need to put in your own accesskey, secretaccesskey, bucket and bucket key:
var AWS = require('aws-sdk');
AWS.config = new AWS.Config();
AWS.config.accessKeyId = "";
AWS.config.secretAccessKey = "";
AWS.config.region = "us-east-1";
var s3 = new AWS.S3();
var params = {
Bucket: 'test537658ghdfshgfd',
Key: '1.png'
};
s3.deleteObject(params, function(err, data) {
if (err) console.log(err)
else console.log("Successfully deleted myBucket/myKey");
});
Alternatively you can use Minio-Js client library, its Open Source and compatible with AWS S3.
Below is remove-object.js example, you can find complete list here
var Minio = require('minio')
var s3Client = new Minio({
endPoint: 's3.amazonaws.com',
accessKey: 'YOUR-ACCESSKEYID',
secretKey: 'YOUR-SECRETACCESSKEY'
})
// Remove an object name my-objectname.
s3Client.removeObject('my-bucketname', 'my-objectname', function(e) {
if (e) {
return console.log(e)
}
console.log("Success")
})
Please replace YOUR-ACCESSKEYID and YOUR-SECRETACCESSKEY with your own also replace the endPoint to the one you have your bucket is created.
us-east-1: 's3.amazonaws.com',
us-west-1 : 's3-us-west-1.amazonaws.com',
us-west-2 : 's3-us-west-2.amazonaws.com',
eu-west-1: 's3-eu-west-1.amazonaws.com',
sa-east-1: 's3-sa-east-1.amazonaws.com',
eu-central-1: 's3-eu-central-1.amazonaws.com',
ap-southeast-1: 's3-ap-southeast-1.amazonaws.com',
ap-southeast-2: 's3-ap-southeast-2.amazonaws.com',
ap-northeast-1: 's3-ap-northeast-1.amazonaws.com'
Installing Monio-js
$ npm install --save minio
Hope it helps.
Disclaimer: I work for Minio.
The request I make to my node api takes more than 4 minutes to respond. While the time the response is received. The angular app does not accept the response. On firebug, the url turns red.
How can I overcome this.
api.route('/allsearch')
.post(function(req,res){
var filters=req.body.everything;
var filterid=req.body.filterId;
var searchid=req.body.searchid;
var zipgroup=req.body.zipgroup;
var myObject = new Array();
function getData(docs, filters, filterid, callback) {
function loop(i) {
searchingalgo(docs[i], filters, filterid, function(pers){
myObject[i] = pers;
if (i < docs.length) {
loop(i + 1);
} else {
callback();
}
});
};
loop(0);
};//closing get data()
Searchradius.findOne({"searchid" : searchid, user: req.decoded.id}).exec(function(err, docs) {
// Array to hold async tasks
var asyncTasks = [];
// Loop through some items
zipgroup.forEach(function(item){
// We don't actually execute the async action here
// We add a function containing it to an array of "tasks"
asyncTasks.push(function(callback){
// Call an async function, often a save() to DB
console.log(item);
searchingalgo(item, filters, filterid, function(pers){
myObject[item] = pers;
// Async call is done, alert via callback
callback();
});
});
});
Async.parallel(asyncTasks, function(){
//console.log(myObject);
Searchradius.update({ _id: searchid }, { $set: { ucounteds: myObject , uzips: zipgroup }}, function(err, result){
if(err) {
res.send(err);
return;
}
var fields = ['city', 'state', 'zip','distance', 'count'];
var myresults = [];
var tc=0;
var newMyobj= new Array();
co=0;
zipgroup.forEach(function(item){
tc+=myObject[item];
//myresults.push(jobj);
});
for(i=0;i<zipgroup.length;i++){
newMyobj[i]=myObject[zipgroup[i]];
}
console.log(tc);
Searchfilter.update({ _id: filterid }, { $set: { counted_results: tc }}, function(err, resultupdate){
//console.log(resultupdate);
//console.log(tc);
});
// console.log(myObject);
// console.log(newMyobj);
res.json({
success: true,
zips: zipgroup,
states: docs.states,
cities: docs.cities,
distances: docs.distances,
counted_results : newMyobj,
});
}); //update searchradius
}); //getdata function
}); //searchradius findone
});
As requested, this is my node API. the zipgroup is a array of zips like
[37663, 37664, 37669, 37671, 37660, 37669, 37667, 37668, 37666, 37665, 37662, 37661]
Just to be clear the collection Consumer1s has more than 2900009876 documents. It is indexed properly and the query is taking the least time possible. But I am still facing this problem.
Any suggestions would be helpful.
This is my post request from angular controller.
$http.post('/api/allsearch',
{
"everything":$scope.filterSearch ,
"searchid":$routeParams.id,
"filterId": $scope.filterId,
"zipgroup" : $scope.zipgroup
})
.success(function(data){
for(var i=0; i<data.zips.length;i++){
oneset={
"zip": data.zips[i],
"state": data.states[i],
"city": data.cities[i],
"distance": data.distances[i],
"count": data.counted_results[i]
};
$scope.totalCount+=data.counted_results[i];
$scope.results.push(oneset);
}
angular.forEach($scope.results, function (result) {
result.distance = parseFloat(result.distance);
});
$rootScope.processing=false;
$scope.filterlinkdisplay=true;
});
There are at least several options:
set AngularJS $http timeout for 10 mins or so, so that AngularJS request doesn't time out, waiting for 4 mins to get the data
polling: 1) AngularJS app does initial request 2) Node.js server issues a unique token to AngularJS app for this request and starts working on collecting data 3) AngularJS app waits for several mins and does a request with previously received token to get data 4) If result is ready, you're done. If not, wait again and do another request from AngularJS
use WebSocket. On client side it is supported by many browsers, on server side use ws. It's a bi-directional protocol, so server can notify clients when data is ready.
I have read all the posts on Stackoverflow regarding this issue and tried the following:
1) Adding $.param({}) wrapper
messages.fetch({
data: $.param({ limit: 14 }),
});
2)
Setting traditional to true
messages.fetch({
data: { limit: 14 },
traditional: true
});
3)
Setting processData to true
messages.fetch({
data: { limit: 14 },
processData: true,
});
Despite this, none of these methods work. Is there something I am missing here?
For what I think you're tying to do, you need to use the "url" property on the model/collection. Fetch is a GET type, not a PUT or POST so you aren't sending "data" to the server. Try something like the following:
Note that $.param({ limit: 14 }) returns "limit=14" The point is, that is not a valid serialized object that the server is going to understand.
If your "fetch" method is a "HttpGet"
var MyModel = Backbone.Model.extend({
// place holder for models api. Also look at: http://backbonejs.org/#Model-urlRoot
_baseUrl = 'api/somewhere/great'
// see: http://backbonejs.org/#Model-url
url: function() {
// this returns 'api/somewhere/great?limit=14';
return this._baseUrl + '?' + $.param({ limit: 14 });
}
});
Now, if you want to HttpPut or HttpPost, then you can override "fetch" and do something like the following:
var MyModel = Backbone.Model.extend({
url = 'api/somewhere/great'
fetch: function(options) {
options = options || {
data: {
limit: 14
}
};
/*
signature method of sync: Backbone.sync = function (method, model, options)
*/
return this.sync("create", this, options);
}
});
References:
http://backbonejs.org/#Model-url
http://api.jquery.com/jQuery.param/