MapReduce is a data processing technique in MongoDB used to process large amounts of data and perform calculations such as sum, count, average, grouping, etc.
MongoDB Map-Reduce consists of two main functions:
- Map function → reads each document and emits a key-value pair.
- Reduce function → combines the values having the same key.
- out → specifies where the result should be stored.
Basic syntax:
db.collection.mapReduce(
mapFunction,
reduceFunction,
{ out: "resultCollection" }
)
Example 1: Find Sum of Marks Grouped by Age
Suppose we have the following student collection:
use student
db.studentdata.insertMany([
{name: "Ram", marks: 40, age: 20},
{name: "Shyam", marks: 50, age: 20},
{name: "Swati", marks: 60, age: 18},
{name: "Rahul", marks: 60, age: 21},
{name: "Riya", marks: 70, age: 21}
])
Display the data:
db.studentdata.find()
Map function
var mapFunction = function() {
emit(this.age, this.marks);
};
Here:
this.age→ keythis.marks→ value
So the emitted data is approximately:
20 → 40
20 → 50
18 → 60
21 → 60
21 → 70
Reduce function
var reduceFunction = function(key, values) {
return Array.sum(values);
};
Execute MapReduce
db.studentdata.mapReduce(
mapFunction,
reduceFunction,
{out: "Result_mapreduce"}
)
Display the result:
db.Result_mapreduce.find()
Output
{ "_id" : 18, "value" : 60 }
{ "_id" : 20, "value" : 90 }
{ "_id" : 21, "value" : 130 }
Explanation
For age 20:
40 + 50 = 90
For age 21:
60 + 70 = 130
Therefore, MapReduce groups students by age and calculates the total marks for each age group.
Example 2: Calculate Average Marks Grouped by Age
Using the same collection:
db.studentdata.find()
Map function
var mapFunction = function() {
emit(this.age, this.marks);
};
Reduce function
For an average, a simple Array.avg(values) should not be used here. A reliable MapReduce approach is to emit both the sum and count.
var mapFunction = function() {
emit(this.age, {
sum: this.marks,
count: 1
});
};
Reduce:
var reduceFunction = function(key, values) {
var result = {
sum: 0,
count: 0
};
values.forEach(function(value) {
result.sum += value.sum;
result.count += value.count;
});
return result;
};
Run:
db.studentdata.mapReduce(
mapFunction,
reduceFunction,
{out: "AverageMarks"}
)
Then:
db.AverageMarks.find()
The result contains the sum and count for each age. The average is:
Average = sum / count
For example, age 20:
Sum = 90
Count = 2
Average = 90 / 2 = 45
Example 3: Count Books Written by Each Author
Consider a books collection:
use books
db.books.insertMany([
{
title: "MongoDB: The Definitive Guide",
authors: [
{firstName: "BRIJESH", lastName: "PATEL"}
],
price: 32.99
},
{
title: "MongoDB in Action",
authors: [
{firstName: "JAYESH", lastName: "PATEL"}
],
price: 30.83
},
{
title: "MongoDB in Action 2",
authors: [
{firstName: "JIGNESH", lastName: "THANKI"}
],
price: 35.30
},
{
title: "Scaling MongoDB",
authors: [
{firstName: "NIKHIL", lastName: "DAVE"}
],
price: 25.30
}
])
Map function
var mapFunction = function() {
for (var i = 0; i < this.authors.length; i++) {
var author = this.authors[i];
emit(
author.firstName + " " + author.lastName,
1
);
}
};
Each author emits:
BRIJESH PATEL → 1
JAYESH PATEL → 1
JIGNESH THANKI → 1
NIKHIL DAVE → 1
Reduce function
var reduceFunction = function(author, counts) {
var total = 0;
for (var i = 0; i < counts.length; i++) {
total += counts[i];
}
return total;
};
Execute
db.books.mapReduce(
mapFunction,
reduceFunction,
{out: {inline: 1}}
)
Output
BRIJESH PATEL → 1
JAYESH PATEL → 1
JIGNESH THANKI → 1
NIKHIL DAVE → 1
If an author has written multiple books, the reduce function will add those 1 values and give the total number of books written by that author.
Example 4: Sum of Rank Grouped by Age
Suppose:
use employee
db.employee.insertMany([
{id: 1, rank: 10, age: 80},
{id: 2, rank: 20, age: 80},
{id: 3, rank: 30, age: 90},
{id: 4, rank: 40, age: 90},
{id: 5, rank: 50, age: 70}
])
Map
var mapFunction = function() {
emit(this.age, this.rank);
};
Reduce
var reduceFunction = function(key, values) {
return Array.sum(values);
};
Execute
db.employee.mapReduce(
mapFunction,
reduceFunction,
{out: "RankResult"}
)
Display:
db.RankResult.find()
Output
{ "_id" : 70, "value" : 50 }
{ "_id" : 80, "value" : 30 }
{ "_id" : 90, "value" : 70 }
Because:
Age 70 → 50
Age 80 → 10 + 20 = 30
Age 90 → 30 + 40 = 70
Important correction in your original example
You wrote:
var map=function(){ emit(this.age,this.rank)};
var reduce=function(age,rank){ return Array.avg(rank);};
The problem is that Array.avg() is not the appropriate MapReduce reducer for calculating an average.
Also, if rank contains values such as:
"A", "A", "B", "B", "C"
you cannot calculate their numerical average because they are strings.
For example:
{id: 1, rank: "A", age: 80}
Here "A" is text, not a number.
If you want to calculate an average, use a numeric field such as:
{age: 20, marks: 40}
and calculate average using sum + count.
MapReduce flow
Documents
↓
Map Function
↓
Key-Value pairs
↓
Group by Key
↓
Reduce Function
↓
Result Collection
Note: MapReduce is a legacy MongoDB feature; for new MongoDB applications, the aggregation pipeline is generally preferred.