Skip to main content

Points Ranking

Store user IDs and points in a Sorted Set and read the ranking from highest to lowest score.

Data Structure

Key demo:ranking:points uses user IDs as members and points as scores.

User (ELEMENT)Points (SCORE)
u100220.25
u100110.75

Mapping

ZRANGE ... WITHSCORES returns ELEMENT and SCORE columns, with one row mapped to one RankEntry.

RankEntry.java
package com.example.redis;

import net.hasor.dbvisitor.mapping.Column;

public class RankEntry {
@Column("ELEMENT")
private String userId;
@Column("SCORE")
private Double points;

public String getUserId() {
return this.userId;
}

public void setUserId(String userId) {
this.userId = userId;
}

public Double getPoints() {
return this.points;
}

public void setPoints(Double points) {
this.points = points;
}
}

Mapper

PointsRankingMapper.java
package com.example.redis;

import java.util.List;
import net.hasor.dbvisitor.mapper.Param;
import net.hasor.dbvisitor.mapper.Query;
import net.hasor.dbvisitor.mapper.SimpleMapper;

@SimpleMapper
public interface PointsRankingMapper {
@Query("ZADD #{key} INCR #{points} #{userId}")
double addPoints(@Param("key") String key, @Param("userId") String userId,
@Param("points") double points);

@Query("ZRANGE #{key} 0 #{lastIndex} REV WITHSCORES")
List<RankEntry> top(@Param("key") String key, @Param("lastIndex") int lastIndex);

@Query("ZREVRANK #{key} #{userId}")
Long rank(@Param("key") String key, @Param("userId") String userId);
}

Calling the Mapper

Run inside the Session created above:

PointsRankingMapper mapper = session.createMapper(PointsRankingMapper.class);
String key = "demo:ranking:points";

mapper.addPoints(key, "u1001", 10.5);
double updated = mapper.addPoints(key, "u1001", 0.25);
mapper.addPoints(key, "u1002", 20.25);
mapper.addPoints(key, "u1003", 5.0);
List<RankEntry> top = mapper.top(key, 1);
Long rank = mapper.rank(key, "u1002");

Starting with an empty ranking, updated = 10.75. top contains u1002 / 20.25 followed by u1001 / 10.75. rank = 0; the displayed place is rank + 1.

Notes

  • top takes the inclusive last index: pass 9 for the top 10. rank returns null for a missing member.
  • ZADD INCR preserves fractional increments; the current ZINCRBY path reads the increment as an integer. Scores use Double and are unsuitable for amounts requiring exact decimal arithmetic.