Sailsjs native with Mapreduce - mongodb

I am working on sailsjs project, i just looking for suggestion to achieve the below output to make best performance with code samples.
My existing collection having this below document.
[{
"word" : "DAD",
"createdAt":"6/10/2016 7:25:59 AM",
"gamescore":1
},
{
"word" : "SAD",
"createdAt":"6/09/2016 7:25:59 AM",
"gamescore":1
},
{
"word" : "PAD",
"createdAt":"6/10/2016 8:25:59 AM",
"gamescore":1
}]
I need the below output which is something like this.
[{
"word" : "A",
"repeatedTimes" : "3",
"LatestRepeatedTime": "6/10/2016 8:25:59 AM"
},
{
"word" : "D",
"repeatedTimes" : "4",
"LatestRepeatedTime": "6/10/2016 8:25:59 AM"
},
{
"word" : "P",
"repeatedTimes" : "1",
"LatestRepeatedTime": "6/10/2016 8:25:59 AM"
},
{
"word" : "S",
"repeatedTimes" : "1",
"LatestRepeatedTime": "6/09/2016 8:25:59 AM"
}]
For the above scenario i implemented the below code to fetch, but it is not working at find query.
var m = function () {
var words = this.word;
if (words) {
for (var i = 0; i < words.length; i++) {
emit(words[i], 1);
}
}
}
var r = function (key, values) {
var count = 0;
values.forEach(function (v) {
count += v;
});
return count;
}
console.log(req.params.childid);
Activity.native(function (err, collection) {
console.log("hello");
collection.mapReduce(m, r, {
out: {merge: "words_count" + "_" + "575a4952bfb2ad01481e9060"}
}, function (err, result) {
Activity.getDB(function (err, db) {
var colname = "words_count" + "_" + "575a4952bfb2ad01481e9060";
var natCol = db.collection('words_count' + "_" + "575a4952bfb2ad01481e9060");
natCol.find({},..... **is not working**
natCol.count({}, function (err, docs) {
console.log(err);
console.log(docs);
res.ok(docs);
});
});
});
});
Answer:
natCol.aggregate([
{
$project:
{
_id: "$_id" ,
value:"$value"
}
}
], function(err, data){
console.log(data);
res.ok(data);
});

You could try the following
var m = function () {
if (this.word) {
for (var i = 0; i < this.word.length; i++) {
emit(this.word[i], {
"repeatedTimes": 1,
"LatestRepeatedTime": this.createdAt
});
}
}
};
var r = function (key, values) {
var obj = {};
values.forEach(function(value) {
printjson(value);
Object.keys(value).forEach(function(key) {
if (!obj.hasOwnProperty(key)) obj[key] = 0;
if (key === "repeatedTimes") obj[key] += value[key];
});
obj["LatestRepeatedTime"] = value["LatestRepeatedTime"];
});
return obj;
};
var opts = { out: {inline: 1} };
Activity.native(function (err, collection) {
collection.mapReduce(m, r, opts, function (err, result) {
console.log(err);
console.log(result);
res.ok(result);
});
});

Related

How can I pass a variable in sort funtcion of mongobd?

I want to pass this name variable in sort function. I am getting the value of name in console.log but its not working in sort function.
var coldata = req.body.order[0].column;
var name = req.body.columns[coldata].data; // Want to pass this variable in sort()
var first = req.body.order[0].dir;
var last = req.body.order[0].dir;
var x,y;
if (first == 'asc'){
x = 1
}else{
x = -1;
}
if (last == 'asc'){
y = 1
}else{
y = -1;
}
var searchStr = req.body.search.value;
if(req.body.search.value)
{
var regex = new RegExp(req.body.search.value, "i")
searchStr = { $or: [{'firstname':regex },{'lastname': regex}] };
}
else
{
searchStr={};
}
console.log(req.body.search.value)
var recordsTotal = 0;
var recordsFiltered=0;
console.log(searchStr);
db.count({}, function(err, c) {
recordsTotal=c;
db.count(searchStr, function(err, c) {
recordsFiltered=c;
db.find(searchStr, 'firstname lastname',{'skip': Number( req.body.start), 'limit': Number(req.body.length) }, function (err, results) {
if (err) {
console.log('error while getting results'+err);
return;
}
var data = JSON.stringify({
"draw": req.body.draw,
"recordsFiltered": recordsFiltered,
"recordsTotal": recordsTotal,
"data": results
});
res.send(data);
}).sort({name:x});// Not getting value of name here
});
});
});
You can use an aggregation pipeline
const sort = {};
sort[name] = x
const pipeline = [
{ $match: searchStr },
{ $skip: Number( req.body.start) },
{ $limit: Number( req.body.length) },
{ $sort: sort }
];
db.aggregate(pipeline) ...

MongoDB putting they key into $set instead of using it for lookup?

I am trying to update a message using userID as my _id
Is splitting it up into findOne - Save - Update the best way?
//
// Find and update message
//
var messageModel = require('../models/messageModel');
var messageTable = mongoose.model('messageModel');
var messageRecord = new messageModel();
var findMessage = () => {
return new Promise((resolve, reject) => {
console.log("=====START findMessage=====")
messageTable.findOne(
{ _id: userID }
,function(err, data) {
if (err) {
reject(new Error('findMessage: ' + err))
return;
}
// Who will have this as unread?
if (userManager==true) {
messageRecord.readUser = false;
messageRecord.readManager = true;
} else {
messageRecord.readUser = true;
messageRecord.readManager = false;
}
// If message not found, then create new one
if (!data) {
console.log("=====CREATE NEW RECORD=====")
messageRecord._id = userID;
messageRecord.activityDate = Math.round(new Date().getTime()/1000);
messageRecord.messages = {
"message" : message,
"date" : Math.round(new Date().getTime()/1000),
"property" : propertyID,
"booking" : bookingID,
"manager" : userManager
}
messageRecord.save(function (err, res) {
if (err) {
reject(new Error('findMessage: ' + err));
return;
}
})
console.log("=====RESOLVE findMessage=====")
resolve();
return;
}
// If message found, then add message
console.log("=====ADD LINE TO RECORD=====")
messageTable.update (
{ _id: userID },
{
$set: {
activityDate : Math.round(new Date().getTime()/1000),
readUser : messageRecord.readUser,
readManager : messageRecord.readManager
},
$push: {
messages: {
"message" : message,
"date" : Math.round(new Date().getTime()/1000),
"property" : propertyID,
"booking" : bookingID,
"manager" : userManager
}
}
},
{ upsert: true }
).exec(function (err, res) {
if (err) {
reject(new Error('findMessage: ' + err));
return;
}
})
console.log("=====RESOLVE findMessage=====")
resolve();
return;
});
})};
Do I need to put upsert:true? (what ever that means)
Or should I use findOneAndUpdate?
And would you use findOneAndUpdate or just update? And why?
I tought it went like this:
findone
if not found then save
if found then update
UPDATE - Thanks to lascot I ended up doing this, and it works great!
// Save message
messageTable.update (
{ _id: userID },
{
$setOnInsert: {
_id: userID
},
$set: {
activityDate : Math.round(new Date().getTime()/1000),
readUser : messageRecord.readUser,
readManager : messageRecord.readManager
},
$push: {
messages: {
"message" : message,
"date" : Math.round(new Date().getTime()/1000),
"property" : propertyID,
"booking" : bookingID,
"manager" : userManager
}
}
},
{ upsert: true }
).exec(function (err, res) {
if (err) {
reject(new Error('findMessage: ' + err));
return;
}
})

MongoDB - Many counts using an array

How to make many counts using an array as input in Mongoose, and return an array
I am trying to use the code below but it is not working, list2 is returning as empty.
list = ['Ann', 'Bob', 'John', 'Karl'];
list2 = [];
for(let i = 0; i < list.length; i++) {
Clients.count({name: list[i]}, function(err, doc){
list2.push(doc);
})
}
return list2
You could run an aggregation pipeline as follows:
list = ['Ann', 'Bob', 'John', 'Karl'];
list2 = [];
Clients.aggregate([
{ "$match": { "name": { "$in": list } } },
{
"$group": {
"_id": "$name",
"count": { "$sum": 1 }
}
},
{
"$group": {
"_id": null,
"list2": {
"$push": {
"name": "$_id",
"count": "$count"
}
}
}
}
]).exec(function(err, results) {
list2 = results[0].list2;
console.log(list2);
});
const async = require('async');
var list = ['Ann', 'Bob', 'John', 'Karl'];
async.map(list, function(item, callback) {
result = {};
Clients.count({name: item}, function(err, data) {
result[item] = data || 0;
return callback(null, result);
});
}, function(err, data) {
console.log(data);
});
Here's another way based on Med Lazhari's answer
const async = require('async');
var list = ['Ann', 'Bob', 'John', 'Karl'];
var counting = function (item, doneCallback) {
var query = Clients.count({name: item});
query.then(function (doc) {
return doneCallback(null, doc);
});
};
async.map(list, counting, function(err, data) {
console.log(data);
});

How can I find documents in a collection based on array values

I tried adding a helper to return array with documents, which looks like:
documents = {
realPlayers: [],
subscribers: [
{playerId: },
{playerId: },
{playerId: }
],
privatGame:,
gameType:,
gameStatus: 'active'
}
I tried this, but it doesn't work (forEach too):
Template.myGames.helpers({
'myGames': function() {
let gamePul = [],
activeGames = Games.find({gameStatus: "active"});
for (var i = 0; i < activeGame.length; i++) {
if (activeGame[i].subscribers) {
for (var j = 0; j < activeGame[i].subscribers.length; j++) {
if (activeGame[i].subscribers[j].playerId = Meteor.userId()) gamePul.push(activeGame[i]);
}
}
};
return gamePul;
}
});
You're just trying to find the set of games the current user is a subscriber to right? You just need $elemMatch in your query:
Template.myGames.helpers({
myGames: function() {
return Games.find({ gameStatus: "active",
subscribers: { $elemMatch: { playerId: Meteor.userId() }}});
}
});
docs

How to calculate ratios for an additive attribute with mongodb?

Using the sample mongodb aggregation collection (http://media.mongodb.org/zips.json), I would like to output the population share of every city in California.
In SQL, it could look like this:
SELECT city, population/SUM(population) as poppct
FROM (
SELECT city, SUM(population) as population
FROM zipcodes
WHERE state='CA'
GROUP BY city
) agg group by state;
This can be done using mongodb map/reduce:
db.runCommand({
mapreduce : "zipcodes"
, out : { inline : 1}
, query : {state: "CA"}
, map : function() {
emit(this.city, this.pop);
cache.totalpop = cache.totalpop || 0;
cache.totalpop += this.pop;
}
, reduce : function(key, values) {
var pop = 0;
values.forEach(function(value) {
if (value && typeof value == 'number' && value > 0) pop += value;
});
return pop;
}
, finalize: function(key, reduced) {
return reduced/cache.totalpop;
}
, scope: { cache: { } }
});
Can this be also achieved using the new aggregation framework (v2.2)? This would require some form of global scope, as in the map/reduce case.
Thanks.
Is this what you're after?
db.zipcodes.remove();
db.zipcodes.insert([
{ city:"birmingham", population:1500000, state:"AL" },
{ city:"London", population:10000, state:"ON" },
{ city:"New York", population:1000, state:"NY" },
{ city:"Denver", population:100, state:"CO" },
{ city:"Los Angeles", population:1000000, state:"CA" },
{ city:"San Francisco", population:2000000, state:"CA" },
]);
db.zipcodes.runCommand("aggregate", { pipeline: [
{ $match: { state: "CA" } }, // WHERE state='CA'
{ $group: {
_id: "$city", // GROUP BY city
population: { $sum: "$population" }, // SUM(population) as population
}},
]});
produces
{
"result" : [
{
"_id" : "San Francisco",
"population" : 2000000
},
{
"_id" : "Los Angeles",
"population" : 1000000
}
],
"ok" : 1
}
you could try:
db.zipcodes.group( { key: { state:1 } ,
reduce: function(curr, result) {
result.total += curr.pop;
result.city.push( { _id: curr.city, pop: curr.pop } ); },
initial: { total: 0, city:[] },
finalize: function (result) {
for (var idx in result.city ) {
result.city[idx].ratio = result.city[idx].pop/result.total;
}
} } )