mongodb中使用lookup联合查询及分组group排序sort

 0 0条评论

先说一下应用场景

paper表

有score(得分)、time(考试使用时间),createtime(开始提交时间)、user(用户id,引用user表)四列,结构如下:

const schema = baseSchema().add({
    /**
    * 会员ID
    */
    user: {
        type: mongoose.Schema.Types.ObjectId,
        ref: 'user',
        required: true
    },
    /** 
    * 得分
    */
    score: { type: Number, required: true, index: true },
    /**
    * 所用时间,单位秒
    */
    time: { type: Number, required: true, index: true },
    /**
    * 答题时间
    */
    createtime: { type: Number }
})

user表

有主键_id和昵称nickname两个字段。

一个会员可以多次答题,也就是说paper表中,可以有多个同样的user记录。

(两张表实际项目中列肯定不止那么少,我只是找几个重点罗列)

需求

计算得分前十的排行榜,每个user只根据他最好的得分来进行排名,并且还要显示nickname,得分相同则答题时间少的排名优先,如果再相同,则答题时间早的排名优先。

举个例子:

张三,答题两次,score100,90,time20

李四,答题两次,score80,70,time15

赵五,同样,score80分,60分,time16秒

那么最终排行榜第一名就是张三的90分,第二名就是李四的80分,第三名就是赵五的80分。

即一个user 答题3次都是获得最高分,那么最终的排名也只按照他最好的一次成绩来统计。

先上完整代码

    Model.aggregate().lookup({
        from: 'users', localField: 'user',
        foreignField: '_id', as: 'users'
    })
    .sort('-score time createtime')
    .group({
        _id: '$user', score: { $first: '$score' }, time: { $first: '$time' },
        createtime: { $first: '$createtime' }, users: { $first: '$users' }
    })
    .project({
        _id: 0, score: 1, time: 1, createtime: 1,
        nickname: { $min: '$users.nickname' }
    })
    .sort('-score time createtime').limit(10)

第一个方法,lookup,就是聚合中联合外键表,有一点非常重要,from字段,必须要加上分数形式,因为在mongoose创建表格时,它会自动把你的表名改成复数形式的,所以这里也一定要填写复数形式,否则会出错。

第二个方法,sort,此时数据中已经有完成的user表和paper表的联合数据了,根据实际需求进行排序。

第三个方法,group,因为只取每个用户的最好成绩,所以需要group分组,然后$first关键字取得第一行数据,也就说该用户的最好成绩。users里面的得到的是一个数组,因为数据都一样,取第一个即可。

第四个方法,project,过滤掉其他不需要列明,0代表不要,1代表需要,另外users结构是数组,所以只要取一个即可,所以随便使用min或者max都行,因为只有一组数据了。

第五个方法,sort,此时集合里是每个用户最好的记录的数据了,然后再根据数据进行排序,即可获得最终的排名。

至此完美实现需求。

本文作者:双黑

版权声明:本站文章欢迎链接分享,禁止全文转载!

游客