Get the original query object in Mongoose - mongodb

I have a loop to perform multiple queries through Mongoose
"use strict";
var Mongoose = require("mongoose");
var User = Mongoose.model("User");
var Cache = {};
for (var index=0; index<usernames.length; index++) {
var query = {
username:usernames[index]
};
User.find(query).
exec(function(error,users){
//THIS IS A CALLBACK FUNCTION,
//HOW TO GET THE 'query' VARIABLE ABOVE?
//I WANT TO PUT THE RESULT INTO CACHE:
var username = users[0].username;
Cache[username] = users[0];
});
}
I need to know which result is of which query, in the callback function above.
It is for db query caching purpose. I can extract 'username' from 'users[0]', but when the array 'users' is empty, there's no such thing.

Put anonymous function inside your loop; and use .findOne() instead of .find() if you are only interested in the first user or if the username values are unique.
for (var index = 0; index < usernames.length; index++) {
(function () {
var query = {
username: usernames[index]
};
User.findOne(query).
exec(function (error, user) {
//use your query here
var username = user.username;
Cache[username] = user;
});
})()
}
However consider async for this kind of operations.

Related

mongodb showing array of null when printing the outside of query

i am trying to push the resultant of the count to an array in mogodb query, while pushing it showing the array after that if print it outside of query it is showing empty array.
collection1 in db is like below
[{title:Home,
date:24-10-2016},
{title:Accesories,
date:13-02-2016}
]
my code
exports.listOfCategories=function(req,res){
collection1.find().exec(function (err, categories) {
if (err) {
return res.status(400).send({
message: errorHandler.getErrorMessage(err)
});
} else {
var categoryList = categories;
var catTitle;
var allCat = [];
// console.log(categoryList);
for (var i = 0; i < categoryList.length; i++) {
catTitle = categoryList[i].title;
contentCounts(catTitle);
function contentCounts(content, callback) {
var catName = new RegExp(content, 'i');
var mongoQuery = {
"ProCategory.title": catName
}
collection2.find(mongoQuery).count(function (err, count) {
generateContentArr(content, count)
});
}
function generateContentArr(content, count) {
allCat.push({
name: content,
count: count
});
console.log(JSON.stringify(allCat));
// Here it is showing the array what i pushed
}
}
console.log(JSON.stringify(allCat));
// Here it not showing the total array, it showing an empty array
res.json(allCat);
}
});
}
Thanks in advance
You are not waiting for the result of an async operation, in your case in the for loop you need to wait for the result of mongo operation, but as for loop is synchronous, you are just making calls to mongo but don't wait for the results, and print the empty array right after the loop.
I would suggest you to use promises instead of callbacks, I don't know which version of mongoose you are using but the last version have promise support for mongo methods like find and count. Here is an example for your case:
var Promise = require("bluebird");
function countByTitle(catTitle){
var mongoQuery = {"ProCategory.title": new RegExp(catTitle, 'i')}
return collection2.count(mongoQuery).then(function(count) {
return {
name: catTitle,
count: count
};
});
}
collection1.find().then(function (categories) {
var categoryList = categories;
var promises = [];
for (var i = 0; i < categoryList.length; i++) {
promises.push(countByTitle(categoryList[i].title));
}
return Promise.all(promises).then(results => {
console.log(JSON.stringify(results));
})
}).catch(function (err) {
//if there is any error while resolving the promises, this block will be called
return res.status(400).send({
message: errorHandler.getErrorMessage(err)
});
});

Querying OCB from JavaScript (WireCloud)

I'm trying to get type fields for each attribute of my entities. Quering Orion and getting entities is not the problem (I do this through NGSI Source widget) but the way getting these parameters.
From NGSI Source (usual suscription to Orion instance):
var doInitialSubscription = function doInitialSubscription() {
this.subscriptionId = null;
this.ngsi_server = MashupPlatform.prefs.get('ngsi_server');
this.ngsi_proxy = MashupPlatform.prefs.get('ngsi_proxy');
this.connection = new NGSI.Connection(this.ngsi_server, {
ngsi_proxy_url: this.ngsi_proxy
});
var types = MashupPlatform.prefs.get('ngsi_entities').split(new RegExp(',\\s*'));
var entityIdList = [];
var entityId;
for (var i = 0; i < types.length; i++) {
entityId = {
id: '.*',
type: types[i],
isPattern: true
};
entityIdList.push(entityId);
}
var attributeList = null;
var duration = 'PT3H';
var throttling = null;
var notifyConditions = [{
'type': 'ONCHANGE',
'condValues': MashupPlatform.prefs.get('ngsi_update_attributes').split(new RegExp(',\\s*'))
}];
var options = {
flat: true,
onNotify: handlerReceiveEntity.bind(this),
onSuccess: function (data) {
this.subscriptionId = data.subscriptionId;
this.refresh_interval = setInterval(refreshNGSISubscription.bind(this), 1000 * 60 * 60 * 2); // each 2 hours
window.addEventListener("beforeunload", function () {
this.connection.cancelSubscription(this.subscriptionId);
}.bind(this));
}.bind(this)
};
this.connection.createSubscription(entityIdList, attributeList, duration, throttling, notifyConditions, options);
};
var handlerReceiveEntity = function handlerReceiveEntity(data) {
for (var entityId in data.elements) {
MashupPlatform.wiring.pushEvent("entityOutput", JSON.stringify(data.elements[entityId]));
}
};
To MyWidget:
MashupPlatform.wiring.registerCallback("entityInput", function (entityString) {
var entity;
entity = JSON.parse(entityString);
id = entity.id;
type = entity.type;
for(var attr in entity){
attribute = entity[attr];
}
I'm trying to code something similar to obtain the value of type fields. How can I do that? (I'm sure it's quite easy...)
You cannot make use of the current NGSI source operator implementation (at least v3.0.2) if you want to get the type metadata of attributes as the NGSI source makes use of the flat option (discarding that info).
We are studying updating this operator to allow creating subscriptions without using the flat option. The main problem here is that other components expect the data provided by this operator being provided in the format returned when using the flat option. I will update this answer after analysing deeper the issue.

How should i update documents, each with different update data set, in mongodb collections

I have mongodb in which there is 3 huge collections say 'A', 'B' and 'C'
Each collection contains about 2 million documents.
There are certain properties for each of the document.
Each document need to be updated based on those values of certain properties, from which i can determine what should be the '$set' to that document.
currently i am using the same approach for each collection.
that to find all documents in batches. collection them in memory (which i think the culprit for the current approach), then one by one update them all.
For the first collection(that have similar data as in other collections), it takes 10 minutes to get completed. then the next two collections taking 2 hours approx to get the task done or mongodb client get crashed earlier.
There is something wrong and no desired in the current approach.
Model.collection.find({}).batchSize(BATCH).toArray(function(err, docs){
if(err || !docs || !docs.length)
return afterCompleteOneCollection(err);
var spec = function(index) {
if(index % 1000 === 0) console.log('at index : ' + index);
var toSet = { };
var toUnset = { };
var over = function(){
var afterOver = function(err){
if(err) return afterCompleteOneCollection(err);
if(index < docs.length - 1) spec(index+1);
else afterCompleteOneCollection(null);
};
var sb = Object.keys(toSet).length;
var ub = Object.keys(toUnset).length;
if(sb || ub) {
var all = {};
if(sb) all.$set = toSet;
if(ub) all.$unset = toUnset;
Model.collection.update({ _id : docs[index]._id }, all, {}, afterOver);
} else afterOver(null);
};
forEachOfDocument(docs[index], toSet, toUnset, over);
};
spec(0);
});
Is there any better solution for the same.?
The streaming approach from here http://mongodb.github.io/node-mongodb-native/api-generated/cursor.html#stream worked for me
This is what i am doing :
var stream = Model.collection.find().stream();
stream.on('data', function(data){
if(data){
var toSet = { };
var toUnset = { };
var over = function(){
var afterOver = function(err){
if(err) console.log(err);
};
var sb = Object.keys(toSet).length;
var ub = Object.keys(toUnset).length;
if(sb || ub) {
var all = {};
if(sb) all.$set = toSet;
if(ub) all.$unset = toUnset;
Model.collection.update({ _id : data._id }, all, {}, afterOver);
} else afterOver(null);
};
forEachOfDocument(data, toSet, toUnset, over);
}
});
stream.on('close', function() {
afterCompleteOneCollection();
});

MongoDB/Mongoose .find() equal to true/false where null==true

Trying to simplify this snippet into a one liner.
var conditions = [{key: isTrueOrFalse}];
if (isTrueOrFalse)
conditions.push({key: null});
query.find({$or: conditions});
The key, by default, isn't set in the model.
I wrote this util function to make it a one liner, but I'd prefer to use something built into mongoose if possible.
var findWithDefaultIfNull = function(query, key, value, defaultValue) {
var conditions = [];
var condition = {};
condition[key] = value;
conditions.push(condition);
if (value == defaultValue) {
condition = {};
condition[key] = null;
conditions.push(condition);
}
console.log(conditions);
query.find({$or: conditions});
};
exports.findWithDefaultIfNull = findWithDefaultIfNull;

findOne use in mapreduce mongodb

I want to use findOne in map reduce. What is wrong with my code? My error is:
Command 'mapreduce' failed: exception: map invoke failed: JS Error: TypeError: user has no properties nofile_b:3 (response: { "errmsg" : "exception: map invoke failed: JS Error: TypeError: user has no properties nofile_b:3", "code" : 9014, "ok" : 0.0 })
string map = #"
function() {
var movie = this;
var user = db.users.findOne({UserId : parseInt(movie.UserId)});
emit( movie.UserId, {Name:user.Name});
}";
string reduce = #"
function(key, values) {
var result =values;
return result;
}";
string finalize = #"
function(key, value){
return value;
}";
Under c# code
var collection = database.GetCollection("movies");
var options = new MapReduceOptionsBuilder();
options.SetFinalize(finalize);
options.SetOutput(MapReduceOutput.Inline);
var results = collection.MapReduce(map, reduce, options);
lbResultList.Items.Clear();
foreach (var result in results.GetResults())
{
lbResultList.Items.Add(result.ToJson());
}
I solve my problem map function change
function () {
var user = db.users.find({UserId:this.UserId});
var userName ='';
var userSurName ='';
user.forEach(function(u) {
userName = u.Name;
userSurName = u.SurName;
});
emit(
this._id,
{title: this.Title,category:this.Category,UserName: userName,UserSurName: userSurName}
);
}
I think it is not logical,like sub query this solve. What can I do this case?
It looks like you're trying to do an SQL JOIN. MapReduce is the wrong tool for that. You'll actually want to just break this into an aggregate operation to get an array of UserID's and a query operation to get the names of users:
Something like this in mongo shell:
var UserIDArray = [];
movieCollection.aggregate({$group: {_id: "$UserId"}}).forEach(function (v) {
UserIDArray.push(v._id);
});
UserIDNamePairs = userCollection.find({_id : {$in: UserIdArray}}, {_id: "$name"}).toArray();