我在CouchDB中的文档结构如下所示:
{
"_id": "0a68cdbe4a7f3abf4046bc",
"_rev": "1-1508",
"score": {"math":90, "physics": 88, "chemistry": 60},
"student_name": "Mike"
}
我需要在前端显示以下统计信息以设置学生的个人资料:
假设我只有2名学生,第二名学生的记录如下:
{
"_id": "0a68cdbe2344a3abf4046bc",
"_rev": "1-1608",
"score": {"math":80, "physics": 98, "chemistry": 90},
"student_name": "Jane"
}
所以Mike的排名应该是:
math: 1
physics: 2
chemistry: 2
total: 2
和Jane的排名应该是
math: 2
physics: 1
chemistry: 1
total: 1
如果我没有明确说明问题,请告诉我。
我没有想出创建视图以获得排名的方法。我尝试过:
编辑:用户名查询功能和排名检索不需要仅通过视图实现。欢迎任何想法!
Edit2 :课程数量为1K到3K。学生人数将在1M到2M之间。
答案 0 :(得分:6)
我认为用一个视图做你想做的事是不可能的。 会尝试这样的地图功能:
function(doc) {
emit (["math", doc.score.math], doc.student_name);
emit (["physics", doc.score.physics], doc.student_name);
emit (["chemistry",doc.score.chemistry], doc.student_name);
emit (["total",doc.score.math+doc.score.physics+doc.score.chemistry], doc.student_name);
}
然后我会按顺序查询。这将返回按分数排序的值的学生列表。在那之后,我认为你必须以编程方式在软件中选择等级。
我认为reduce函数没有用,因为函数不会缩小结果集,也因为我无法想出一种按学生姓名查询的方法,并且整个学生列表也是如此时间。我认为列表没有用,因为我再也看不出如何让列表知道学生的名字,同时让整个学生在结果中。
答案 1 :(得分:2)
也许排序视图和列表的组合可行。
您的resultByChemistryScore视图将如下所示
function(doc) {
emit(doc.score.chemistry, [doc._id, doc.student_name]);
}
然后您的GET请求类似于http://localhost:5984/results/_design/results/_view/resultByChemistryScore?descending=true
此时您也可以使用offset
和limit
GET查询参数来实现分页。
从这一点开始,list
可以计算,直到它到达您指定的学生。
您的列表功能“排名”看起来像这样
function(head, req) {
start({ "headers": { "content-type": "application/json" } } );
var row, rank = 0;
while ( row = getRow() ) {
if ( row.id == req.query.id ) break;
// increment rank if not a tie
if ( old_row != null && old_row.key != row.key )
rank++;
old_row = row;
};
send( JSON.stringify( { "rank" : rank } ) );
}
您的请求基本上是http://localhost:5984/results/_design/results/_list/rank/resultByChemistryScore?id=fet&descending=true
。
它不是很漂亮,我会给你的。如果你说......第1,000,000名最优秀的化学学生,服务器可能需要一段时间才能完成整个列表。但服务器执行此操作肯定比客户端更容易。
修改强> 添加了领带处理案例
答案 2 :(得分:2)
所以我认为没有一个解决方案可以完全在CouchDB中完成,它将为学生检索单个值,主题配对。但是,可以创建一个map / reduce视图,它几乎可以生成您正在寻找的内容。然后,该视图的结果可用于查找学生,科目对的排名。
我们首先构建一个视图,其中的地图与joscas建议的地图非常相似。唯一的区别是主题名称不是硬编码的:
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);
}
我们将其与reduce函数配对,该函数将为group=true
和grouping_level=1
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;
}
我在数据集中添加了第三个学生并创建了一些关系以使其变得有趣。这是使用的数据:
{
"_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"
}
视图保存为排名,可以像这样查询:
http://127.0.0.1:5984/atest/_design/atest/_view/rank?group=true&group_level=1
产生这个结果:
{
"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"]}}
]
}
可以通过这样的主题查询视图(假设最低分数为0,最高分为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
(没有网址编码):
http://127.0.0.1:5984/atest/_design/atest/_view/rank?group=true&group_level=1&startkey=["math",0]&endkey=["math",100]
产生这个结果:
{
"rows":[
{"key":["math"],"value":{"1":["Jane","Mike"],"3":["Charlie"]}}
]
}
可以使用Javascript(或其他客户端技术)搜索生成的词典,以确定学生在单个(或所有)科目中的排名。
答案 3 :(得分:1)
我有一个基于@joscas答案的想法。 您可以创建如下视图:
key -> value
---------------------
("math", 0) -> 2
("math", 1) -> 3
("math", 2) -> 5
....
("math", 100) -> 50
我假设每门课程的分数范围是0到100.这个想法是:
bucket0
到bucket100
)。key -> accumulative value
------------------------------------
("math", 0) -> 2
("math", 1) -> 5
("math", 2) -> 10
....
("math", 99) -> 32324
("math", 100)-> 32374
根据课程名称c
和您的分数s
,第二个视图会告诉您how many students play not as good as you in this course
,您可以使用n-#s
从中n
获取排名,其中c
1}}是#s
中注册的学生总数,s
是得分低于"math", 99
的学生人数。例如,{{1}}的查询将返回32374-32324 = 50,这是在“数学”中得到99的学生的等级。
对于问题的总分部分,您可以使用类似的想法,但更改存储桶大小和数量。