I'm a complete beginner to map-reduce and relatively new with MongoDB. I am trying to perform a map-reduce on a collection (by the name Customers).
My map function:
var map = function() {
emit(this.nationality, {edad: this.age, telefono: this.phone_number});
};
My reduce function:
var reduce = function(key, values) {
return Array.sum(values);
};
I call mapReduce() as follows:
db.Customers.mapReduce( map , reduce , { out : { inline : 1}} );
Finally my result field is:
"results" : [
{
"_id" : "Brazil",
"value" : {
"edad" : 34,
"telefono" : null
}
},
{
"_id" : "Colombia",
"value" : "[object BSON][object BSON]"
},
{
"_id" : "Germany",
"value" : {
"edad" : 18,
"telefono" : 2986
}
}, ...
As you can see it contains BSON objects, the problem being that I can't find a way to visualize the content of these BSONs, not even in Robomongo.
I have looked for other answers in Stackoverflow addressing this, so far I have found this:
"Your reduce function must return the same format that your map function emits". But being honest, I haven´t managed to figure out how do I apply it to solve my problem. I mean, what is the format of my emit? And in which format is the output of my reduce function?
If anyone could provide me the exact code lines, my gratitude in advance.
Related
What I did
I call a function in objects' value.
And this function would be called in every object as much as users add an absent data.
What I want to do
Hence I recognize that I could have thousands of hundred of new object, I would rather minimize/eliminate duplication than write the function every time.
Explain what the code is for
The function's name I would like to auto-set is datesBetween().
datesBetween() can choose the mode, and it has parameters.
The mode refers the value from the key reason which is in same object. arr[0].absent[0].datesBetween's value should be datesBetween('leave')('2020-1-1','2020-1-4').
The parameters does same. First parameter should refer from the object key start which is in same object, second parameter should refer from the object key end.
Some idea
I guess this could use class but I am afraid to mix up with functions. if class would help, please do tell.
I write this thou, I am uncertain about this. Is using this could be a solution in this code?
var arr = [
{
name : "A",
absent :[
{
reason : "leave",
start : '2020-1-1',
end : '2020-1-4',
datesBetween : datesBetween('leave')(this.start, this.end)
}, {
reason : "sleeOver",
start : '2020-1-25',
end : '2020-1-26',
datesBetween : datesBetween('sleeOver')(this.start, this.end)
}
]
}, {
name : "B",
absent :[
{
reason : "weekendExcursion",
start : '2020-1-18',
end : '2020-1-19',
datesBetween : datesBetween('weekendExcursion')(this.start, this.end)
}
]
}
]
function autoAbsentSetter(){
//do I need class to use inheritance/property things?
}
function addAbsent(mode){
var funcs = {
'leave' : function absent_leave(name, absentName, absentStart, absentEnd){ //all kinds of leave are included. Detail further.
//some codes
},
'sleepOver' : function absent_sleepOver(name, absentName, absentStart, absentEnd){
//some codes
},
'weekdayExcursion' : function absent_weekdayExcursion(name, absentName, absentStart, absentEnd){
//some codes
},
'weekendExcursion' : function absent_weekendExcursion(name, absentName, absentStart, absentEnd){
//some codes
}
}
return funcs[mode];
}
You don't need to put those functions inside the array literal (and yes, you cannot access the other properties there anyway). Just write
var arr = [
{
name: "A",
absent: [
{
reason : "leave",
start : '2020-1-1',
end : '2020-1-4',
}, {
reason : "sleepOver",
start : '2020-1-25',
end : '2020-1-26',
}
]
}, {
name: "B",
absent: [
{
reason : "weekendExcursion",
start : '2020-1-18',
end : '2020-1-19',
}
]
}
];
for (const value of arr) {
for (const absence of value.absent) {
absence.datesBetween = datesBetween(absence.reason)(absence.start, absence.end);
}
}
You can do whatever you want in that loop processing your data.
I've stumbled upon some very strange behavior with MongoDB. For my test case, I have an MongoDB collection with 9 documents. All documents have the exact same structure, including the fields expired_at: Date and location: [lng, lat].
I now need to find all documents that are not expired yet and are within a bounding box; I show match documents on map. for this I set up the following queries:
var qExpiry = {"expired_at": { $gt : new Date() } };
var qLocation = { "location" : { $geoWithin : { $box : [ [ 123.8766, 8.3269 ] , [ 122.8122, 8.24974 ] ] } } };
var qFull = { $and: [ qExpiry, qLocation ] };
Since the expiry date is long in the past, and when I set the bounding box large enough, the following queries give me all 9 documents as expected:
db.docs.find(qExpiry);
db.docs.find(qLocation);
db.docs.find(qFull);
db.docs.find(qExpiry).sort({"created_at" : -1});
db.docs.find(qLocation).sort({"created_at" : -1});
Now here's the deal: The following query returns 0 documents:
db.docs.find(qFull).sort({"created_at" : -1});
Just adding sort to the AND query ruins the result (please note that I want to sort since I also have a limit in order to avoid cluttering the map on larger scales). Sorting by other fields yield the same empty result. What's going on here?
(Actually even stranger: When I zoom into my map, I sometimes get results for qFull, even with sorting. One could argue that qLocation is faulty. But when I only use qLocation, the results are always correct. And qExpiry is always true for all documents anyway)
You may want to try running the same query using the aggregation framework's $match and $sort pipelines:
db.docs.aggregate([
{ "$match": qFull },
{ "$sort": { "created_at": -1 } }
]);
or implicitly using $and by specifiying a comma-separated list of expressions as in
db.docs.aggregate([
{
"$match": {
"expired_at": { "$gt" : new Date() },
"location" : {
"$geoWithin" : {
"$box" : [
[ 123.8766, 8.3269 ],
[ 122.8122, 8.24974 ]
]
}
}
}
},
{ "$sort": { "created_at": -1 } }
]);
Not really sure why that fails with find()
chridam suggestion using the aggregation framework of MongoDB proved to be the way to go. My working query now looks like this:
db.docs.aggregate(
[
{ $match : { $and : [qExpiry, qLocation]} },
{ $sort: {"created_at": -1} }.
{ $limit: 50 }.
]
);
Nevertheless, if any can point out way my first approach did not work, that would be very useful. Simply adding sort() to a non-empty query shouldn't suddenly return 0 documents. Just to add, since I still tried for a bit, .sort({}) return all documents but was not very useful. Everything else failed including .sort({'_id': 1}).
I'm new to Mongo and trying compare a array with a documents of collections and return list of matching records.
Let me explain:First Array
I have a collection (user) with following documents:
> db.user.find().pretty()
{
"_id" : ObjectId("57358220bf3e7d076b6ccdb1"),
"name" : "Sunny",
"phone" : "9417702107",
"email" : "ssdhiman07#gmail.com"
}
{
"_id" : ObjectId("57358242bf3e7d076b6ccdb2"),
"name" : "Pal",
"phone" : "9015719419",
"email" : "ss998#gmail.com"
}
{
"_id" : ObjectId("57358262bf3e7d076b6ccdb3"),
"name" : "viveky",
"phone" : "8826565107",
"email" : "sds998#gmail.com"
}
Second Array: i have a array of objects that is come from Http request below is structure of array.
{
"contacts" : [
{
"name" : "Sunny",
"phone" : "9417702107"
},
{
"name" : "Sukhpal",
"phone" : "9015719419"
},
{
"name" : "anurag",
"phone" : "9988776655"
},
{
"name" : "vivek",
"phone" : "8826565107"
}
]
}
Now I want to know which objects of Second Array are exists in First Array and which doesn't . Comparison should on basis of phone only . And in result i want same Array as Second Array but with one extra field that is
"exists":"true" or "exists":"false" . Something like this.
{
"contacts" : [
{
"name" : "Sunny",
"phone" : "9417702107"
"exists" :"true"
},
{
"name" : "pal",
"phone" : "90177668899"
"exists" :"false"
}
]
}
So for this i had tried something here is code of node.js with mongoos.
exports.matchcontacts = function(req, res, next)
{
var response = {};
var conArray = req.body.contacts;
var contact_list = [];
for(var i=0; i<conArray.length;i++)
{
var name = conArray[i].name;
var phone = conArray[i].phone;
Users.findOne({"phone":conArray[i].phone},function(err,data)
{
if(err)
{
response = {"error" : true,"message" : "Error fetching data"};
}
else if(!data)
{
contact_list.push({name:name,phone:phone,exists:"false"});
}
else
{
contact_list.push({name:name,phone:phone,exists:"true"});
}
});
}
response = {"error":false,"contacts":contact_list};
res.json(response);
};
But always got null {} empty result, and if i tried to get response inside Callback function then it return only single last compared value.
Problem is in first method is that callback function return result very late so result always empty .
and in second method loop override result and it also inefficient to use callback inside loop it will call no of time. So whole story i had explained
Now Please can anybody help me with code or suggest right way to get desired result thanks
Haven´t used mongodb, but the idea I would use is to first iterate your contacts and make a mapping of the phones to the corresponding object and mark them as non existent:
var obj = {};
for(var i=0; i<conArray.length;i++){
obj[conArray[i].phone] = conArray[i];
conArray[i].exists = false;
}
Then search in some way for the users that have those phones in your db, something like
var results = Users.find(records that have phone in Object.keys(obj) array)
Finally, you iterate your existant records and mark the corresponding contact
for(var i=0;i<results.length;i++){
obj[results[i].phone].exists = true;
}
I am trying to find a particular record (with an id of 1) and then to return only its history field. This history field is an array of objects with a timestamp property. When I return this history field, I want it to contain only objects with timestamp properties greater than 0.
Sample collection
[{
"_id" : 1,
"history" : [
{
"content" : "hello",
"timestamp" : 1394639953878,
"_id" : ObjectId("53208451767743b748ddbd7d")
},
{
"content" : "world",
"timestamp" : 1394639953879,
"_id" : ObjectId("33208451767743b748ddbd7e")
}
]
}]
Thus, according to Mongo and Mongoose documentation, I do something like:
model.find({_id: 1})
.select({ history: { $elemMatch: { timestamp: { $gt: 0 } } } });
The issue: I understand that there will only be one result (since I select the id 1) but I am using $elemMatch to populate its history field with all objects that have a timestamp greater than 0. The issue is that only one object is ever in history.
The $elemMatch projection returns only the first matching element satisfying the condition. Checkout the documentation.
Using aggregate would be the right approach to achieve what you want I think. Please see below:
db.bar.aggregate([
{$match:{_id:1}},
{$unwind:"$history"},
{$match:{"history.timestamp":{$gt:0}}},
{$group:{_id:"$_id", history:{$push:"$history"}}}
])
I have a JSON string that looks like this:
{
"resultType" : "history",
"currentTime" : "2011-10-22T15:46:00+00:00",
"columns" : ["date","orders","quantity","low","high","average"],
"rowsets" : [
{
"generatedAt" : "2011-10-22T15:42:00+00:00",
"rows" : [
["2011-12-03T00:00:00+00:00",40,40,1999,499999.99,35223.50],
["2011-12-02T00:00:00+00:00",83,252,9999,11550,11550]
]
}
]
}
Every time I try to parse it, I use code like this:
var data = JSON.parse(json);
console.log(data);
And the following is what is printed to the console:
{
"resultType" : "history",
"currentTime" : "2011-10-22T15:46:00+00:00",
"columns" : ["date","orders","quantity","low","high","average"],
"rowsets" : [
{
"generatedAt" : "2011-10-22T15:42:00+00:00",
"rows" : [Object]
}
]
}
I've tried a couple of things, but how can I get the data in the rows field? After parsing, the console just shows [Object].
The output you're seeing is just the way it's being displayed. If you access data.rowsets[0].rows, you can see that the JSON was indeed successfully parsed. You can also use util.inspect() when specifying the depth property to tell Node.js to recurse deeper when formatting the object.
Here is an example using util.inspect():
var data = JSON.parse(json);
// a null depth means to recurse indefinitely
console.log(util.inspect(data, { depth: null }));
Have tested your code. It's fine.
var a = {
"resultType" : "history",
"currentTime" : "2011-10-22T15:46:00+00:00",
"columns" : ["date","orders","quantity","low","high","average"],
"rowsets" : [
{
"generatedAt" : "2011-10-22T15:42:00+00:00",
"rows" : [
["2011-12-03T00:00:00+00:00",40,40,1999,499999.99,35223.50],
["2011-12-02T00:00:00+00:00",83,252,9999,11550,11550]
]
}
]
}
var util = require('util');
console.log(util.inspect(a.rowsets[0].rows, { showHidden: true, depth: null }));