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.