So I don't think there is a solution that can be accomplished entirely in CouchDB that will retrieve a single value for a student,subject pairing. However, a map/reduce view can be created that will produce nearly what you're looking for. The results of that view can then be used to find the rank for a student,subject pair.
We start by building a view with a map very similar to the one suggested by joscas. The only difference is that the subject names aren't hardcoded:
map.js
function(doc) {
var total = 0;
for (var subject in doc.score) {
var score = doc.score[subject];
emit([subject, score], doc.student_name);
total += score;
}
emit(["total", total], doc.student_name);
}
We pair that with a reduce function that will produce a ranking for each subject given a group=true
and grouping_level=1
reduce.js
function(keys, values) {
var rankings = {}; // In order to return ties, a simple array can't be used.
var rank = 0;
var place = 0;
var last_score = -1;
for (var i = 0; i < values.length; i++) {
var name = values[i];
var score = keys[i][0][1]; // The 0th element of the key is the [subject, score] array.
if (score == last_score) {
// Tie, add another student to this rank.
place++;
} else {
// Not a tie, create a new rank.
rank += (place + 1);
rankings[rank] = new Array();
place = 0;
last_score = score;
}
rankings[rank][place] = name;
}
return rankings;
}
data
I added a third student to data set and created some ties to make it interesting. Here's the data used:
{
"_id": "ce6b2cd97e73258014679ab7bb9e7cdc",
"_rev": "2-b62581d22c186bfc8ebe1703a2dfb506",
"score": {
"chemistry": 60,
"math": 90,
"physics": 88
},
"student_name": "Mike"
}
{
"_id": "ce6b2cd97e73258014679ab7bb9e8ada",
"_rev": "5-94d6cfbd3cf22f903ebc306570d1f1af",
"score": {
"chemistry": 90,
"math": 90,
"physics": 98
},
"student_name": "Jane"
}
{
"_id": "ce6b2cd97e73258014679ab7bb9e960b",
"_rev": "1-d8c7fe88de63cf3d6e9743696f96aad0",
"score": {
"chemistry": 61,
"math": 89,
"physics": 88
},
"student_name":
"Charlie"
}
results
The view is saved as rank and can be queried like this:
http://127.0.0.1:5984/atest/_design/atest/_view/rank?group=true&group_level=1
Which produces this result:
{
"rows":[
{"key":["chemistry"],"value":{"1":["Jane"],"2":["Charlie"],"3":["Mike"]}},
{"key":["math"],"value":{"1":["Jane","Mike"],"3":["Charlie"]}},
{"key":["physics"],"value":{"1":["Jane"],"2":["Charlie","Mike"]}},
{"key":["total"],"value":{"1":["Jane"],"2":["Charlie","Mike"]}}
]
}
The view can be queried by subject like this (assuming the lowest score is 0 and the highest score is 100):
http://127.0.0.1:5984/atest/_design/atest/_view/rank?group=true&group_level=1&startkey=%5B%22math%22,0%5D&endkey=%5B%22math%22,100%5D
(without the url encoding):
http://127.0.0.1:5984/atest/_design/atest/_view/rank?group=true&group_level=1&startkey=["math",0]&endkey=["math",100]
Which produces this result:
{
"rows":[
{"key":["math"],"value":{"1":["Jane","Mike"],"3":["Charlie"]}}
]
}
The resulting dictionaries can be searched using Javascript (or other client side technology) to determine the rank of the student in a single (or all) subjects.