Reference — Filter
Filter 10 fields, canonical/summoner factory, Base64Url codec with * for null, Redis keys and ActivityFilter minGames.
Filter (src/main/java/com/safjnest/lol/model/Filter.java:13) is the core of every aggregation — it encapsulates 10 dimensions for statistics, activity, matchups, champion stats.
Fields
public class Filter {
private int champion; // 0 = all, >0 = champion ID
private LaneType lane; // null = all
private GameQueueType queue; // null = all (or ALL)
private TierType rank; // null = all
private RankBehavior rankBehavior = GREATER_OR_EQUAL; // EXACT or GREATER_OR_EQUAL
private String patch; // null = latest, format major.minor
private LeagueShard region; // null = all
private int opponent; // 0 = all, >0 = opponent champion ID
private int duo; // 0 = all, >0 = duo champion ID
private long timeStart; // 0 = epoch 0, otherwise millis
private long timeEnd; // 0 = now, otherwise millis
public enum RankBehavior { EXACT, GREATER_OR_EQUAL }
}
| Field | Type | Default (new Filter() :20) | Validation | Notes |
|---|---|---|---|---|
champion | int | 0 | >=0 (activityFilter :150) | 0=all |
lane | LaneType | null | LaneTypeUtils.playables | TOP..UTILITY |
queue | GameQueueType | null (but constructor sets patch/rank/period) | optionalQueue | null=all, TEAM_BUILDER_RANKED_SOLO→RANKED_SOLO_5X5 |
rank | TierType | EMERALD | rank | null=all |
rankBehavior | RankBehavior | GREATER_OR_EQUAL | enum | EXACT vs >= |
patch | String | PatchUtils.getPatch() (:21) | \d+\.\d+ | null=latest |
region | LeagueShard | null | region | null=all |
opponent | int | 0 | >=0 | |
duo | int | 0 | >=0 | |
timeStart | long | SeasonUtils.getCurrentSplitRange()[0] (:24) | >=0 | |
timeEnd | long | SeasonUtils.getCurrentSplitRange()[1] | >=0 |
Constructor Filter() (:20) initializes patch = PatchUtils.getPatch() (:21), rank=EMERALD (:22), rankBehavior=GREATER_OR_EQUAL, timeStart/timeEnd = current split (SeasonUtils.getCurrentSplitRange :24).
Factory defaults
Filter.canonical()
canonical() (src/main/java/com/safjnest/lol/model/Filter.java:44):
public static Filter canonical() {
SeasonUtils.SeasonRange season = SeasonUtils.getCurrentSeasonRange(); // :45
return new Filter().setChampion(0).setLane(null).setQueue(null).setRank(null)
.setPatch(null).setRegion(null).setOpponent(0).setDuo(0)
.setPeriod(season == null ? 0 : season.start(), season == null ? 0 : season.end()); // :55
}
Current season filter — zeroes everything except period = aggregated SeasonRange (min start, max end of splits in rsc/testing/lol_testing/split.json SeasonUtils.java:41). Used for GET /records (RecordsController.java:29) and champion aggregations.
Filter.summoner(timeStart, timeEnd)
summoner(tStart, tEnd) (Filter.java:31):
public static Filter summoner(long timeStart, long timeEnd) {
return new Filter().setChampion(0).setLane(null).setQueue(null).setRank(null)
.setPatch(null).setRegion(null).setOpponent(0).setDuo(0).setPeriod(timeStart, timeEnd); // :41
}
Filter for profile/activity — explicit period, others zeroed. Used by LolApiParameters.activityFilter (:151):
Filter filter = start==0 && end==0 ? Filter.canonical() : Filter.summoner(start,end);
return filter.setQueue(queue).setChampion(champion); // :152
ActivityFilter
ActivityFilter (src/main/java/com/safjnest/lol/model/ActivityFilter.java:3) extends Filter, adds minGames:
public class ActivityFilter extends Filter {
private int minGames = 5; // DEFAULT_MIN_GAMES:5
public int minGames() { return minGames; }
public ActivityFilter setMinGames(int minGames) { if (minGames<1) throw IAE; this.minGames=minGames; return this; }
}
Constructor zeroes everything and sets period = Filter.canonical().period() (:19). Used for GET /profile/{puuid}/matchups via LolApiParameters.matchupsFilter (:137):
ActivityFilter filter = new ActivityFilter();
filter.setQueue(queue).setLane(role).setMinGames(minGames);
if (start!=0 || end!=0) filter.setPeriod(start, effectiveEnd).setPatch(null);
else filter.setPatch(patch);
Codec — Base64Url * for null
FilterCodec (src/main/java/com/safjnest/lol/utils/FilterCodec.java:6):
public static String encode(String raw) { return Base64.getEncoder().encodeToString(raw.getBytes(UTF_8)); } // :10
public static String encodeUrl(String raw) { return Base64.getUrlEncoder().withoutPadding().encodeToString(raw.getBytes(UTF_8)); } // :15
public static String decode(String key) { return new String(Base64.getDecoder().decode(key), UTF_8); } // :20
public static String join(String... parts) { return String.join("|", parts); } // :30
public static String encodeValue(Object v) { return v!=null ? v.toString() : "*"; } // :40
public static String encodeInt(int v) { return v!=0 ? String.valueOf(v) : "*"; } // :44
public static String encodeEnum(Enum<?> v) { return v!=null ? v.name() : "*"; } // :52
public static String encodeOrdinal(Enum<?> v) { return v!=null ? String.valueOf(v.ordinal()) : "*"; } // :56
public static String decodeString(String part) { return "*".equals(trimmed) ? null : trimmed; } // :60
public static T decodeEnum(String part, Class<T> type) { if ("*".equals(trimmed)) return null; return valueOf(trimmed, type); } // :74
*representsnullor0for int.encode= standard Base64 with padding,encodeUrl= URL-safe Base64 without padding (:15).
Keys — toKey, pageKey, toStateKey, toSummonerKey, genericKey
Filter (Filter.java:237):
| Method | File:line | Raw format (join |) | Codec | Parts | Redis use |
|---|---|---|---|---|---|
toKey() | :237 | champion|lane|queue|rank|rankBehavior|patch|region (+ opponent|duo if non-zero) | encode (standard) | 7+2 | legacy champion stats (7 parts) |
pageKey() | :251 | lane|queue|rank|rankBehavior|patch|region|timeStart|timeEnd (+ opponent|duo) | encode | 8+2 | pagination (no longer used) |
toStateKey() | :266 | champion|lane(ordinal)|queue(ordinal)|rank(ordinal)|patch|region|opponent|duo|timeStart|timeEnd|rankBehavior | encodeUrl | 11 | profile_matchups (:283 via toStateKey) |
toSummonerKey() | :283 | champion|lane|queue|rank|rankBehavior|patch|region|opponent|duo|timeStart|timeEnd | encodeUrl | 11 | profile_statistics, profile_activity, summoner_statistics (ProfileService.java:341) |
genericKey() | :300 | queue|rank|rankBehavior|patch|region|lane|timeStart|timeEnd | encode | 8 | champion_builds/champion_stats per champion |
Reverse decoding:
fromKey(:69):Base64.decode+ split 7-9 parts,decodeEnumfor lane/queue/rank.fromSummonerKey(:105):decodeUrl+ split 11 exact parts (if parts.length !=11 throw IAE),decodeIntfor champion/opponent/duo,decodeLongfor time.fromStateKey(:85):decodeUrl+decodeOrdinalfor lane/queue/rank.fromGenericKey(:58):decode+ 4-6 parts,decodeEnumfor queue/rank/region/lane.
Example keys
Filter f = Filter.canonical(); // EMERALD rank cleared, season period
String summonerKey = f.toSummonerKey();
// raw: "0|*|*|*|GREATER_OR_EQUAL|*|*|*|*|1700000000000|1716000000000"
// encodedUrl: "MHwqKnwqKkdSRUFURVJfT1JfRVFVQUx8Kip8Kip8Kip8MTcwMDAwMDAwMDAwMHwxNzE2MDAwMDAwMDAw"
Filter g = Filter.summoner(0,0).setQueue(GameQueueType.RANKED_SOLO_5X5).setChampion(157);
String key = g.toSummonerKey();
// raw: "157|*|RANKED_SOLO_5X5|*|GREATER_OR_EQUAL|*|*|*|*|0|0"
// encodedUrl: "MTU3fCp8UkFOS0VEX1NPTE9fNVg1fCp8R1JFQVRFUl9PUl9FUVVBTHwqKnwqKnwqKnwwfDA"
Filter h = new ActivityFilter().setLane(LaneType.MID).setPatch("15.6").setMinGames(5);
String stateKey = h.toStateKey();
// raw: "0|2|ordinal-queue|ordinal-rank|15.6|*|*|*|timeStart|timeEnd|GREATER_OR_EQUAL"
// encodedUrl: URL-safe Base64 without padding
*fornull/0ensuressplit("\\|", -1)(FilterCodec.java:36) preserves empty fields.
FromSummonerKey — validation
// Filter.java:105
public static Filter fromSummonerKey(String key) {
String raw = FilterCodec.decodeUrl(key);
String[] parts = FilterCodec.split(raw);
if (parts.length != 11) throw new IllegalArgumentException("Invalid summoner filter key");
return new Filter()
.setChampion(decodeInt(parts[0])) // *→0
.setLane(decodeEnum(parts[1], LaneType.class))
.setQueue(decodeEnum(parts[2], GameQueueType.class))
.setRank(decodeEnum(parts[3], TierType.class))
.setRankBehavior(RankBehavior.valueOf(parts[4]))
.setPatch(decodeString(parts[5])) // *→null
.setRegion(decodeEnum(parts[6], LeagueShard.class))
.setOpponent(decodeInt(parts[7]))
.setDuo(decodeInt(parts[8]))
.setPeriod(decodeLong(parts[9]), decodeLong(parts[10]));
}
ActivityFilter — minGames
ActivityFilter.minGames (ActivityFilter.java:22) default 5 (DEFAULT_MIN_GAMES). Validation LolApiParameters.minGames (:119): >=1 otherwise 400.
Used only for matchups — filters opponents with fewer than minGames games. aggregationFilter() (:32): Filter.fromStateKey(toStateKey()) for normalization.
Use in Redis
// ProfileService.java:341
String cacheKey = RedisKey.SUMMONER_STATISTICS.of(cacheRegion, shard, puuid, filter.toSummonerKey());
ProfileStatistics cached = RedisClient.get(cacheKey, ProfileStatistics.class);
if (cached == null) {
Document doc = database.getCollection("profile_statistics").find(Filters.eq("_id", puuid + ":" + filter.toSummonerKey())).first();
// ...
RedisClient.set(RedisKey.SUMMONER_STATISTICS, stats, cacheRegion, shard, puuid, filter.toSummonerKey());
}
Same for SUMMONER_ACTIVITY and SUMMONER_MATCHUPS (with toStateKey for matchups).
