Using Momento Leaderboards
Momento Leaderboardsは、専用のAPIを備えたトップクラスのサービスであり、数千万のアイテムを持つリーダーボードと、迅速な取り込み/照会/更新をサポートします。
Leaderboard Client Methods
Create Leaderboard Client
リーダーボードを作成し、操作するには、まずLeaderboardClientを作成する必要があります。
Parameters
- configuration - LeaderboardConfiguration: リーダーボードクライアントを設定するためのオプションです。詳細については、SDK Configuration Objects を参照してください。
- credentialProvider - CredentialProvider: Momento API キーの使用については、Instantiating CredentialProviders を参照してください。
Returns
- 新しいリーダーボードを作成し、既存のリーダーボードと相互作用することができる PreviewLeaderboardClient オブジェクト
- JavaScript
- Java
- Go
new PreviewLeaderboardClient({
configuration: LeaderboardConfigurations.Laptop.v1(),
credentialProvider: CredentialProvider.fromEnvironmentVariable('MOMENTO_API_KEY'),
});
try (final LeaderboardClient leaderboardClient =
LeaderboardClient.builder(
CredentialProvider.fromEnvVar("MOMENTO_API_KEY"),
LeaderboardConfigurations.Laptop.latest())
.build()) {
// ...
}
credentialProvider, err := auth.NewEnvMomentoTokenProvider("MOMENTO_API_KEY")
if err != nil {
panic(err)
}
leaderboardClient, err = momento.NewPreviewLeaderboardClient(
config.LeaderboardDefault(),
credentialProvider,
)
if err != nil {
panic(err)
}
Create a Leaderboard
LeaderboardClient を使用して、キャッシュとリーダーボード名を指定してリーダーボードを作成します。
Parameters
- cacheName - string: どのキャッシュにリーダーボードを作成するか
- leaderboardName - string: リーダーボードの名前
Returns
- Leaderboard object or Error
- JavaScript
- Java
- Go
// You can create multiple leaderboards using the same leaderboard client
// but with different cache and leaderboard names
leaderboardClient.leaderboard(cacheName, 'momento-leaderboard');
leaderboardClient.leaderboard(cacheName, 'acorns-leaderboard');
// Leaderboard and cache names must be non-empty strings
try {
leaderboardClient.leaderboard(cacheName, ' ');
} catch (error) {
console.log('Expected error creating a leaderboard with invalid leaderboard name:', error);
}
final ILeaderboard leaderboard;
try {
leaderboard = leaderboardClient.leaderboard("cache", "leaderboard");
} catch (InvalidArgumentException e) {
throw new RuntimeException("Cache name or leaderboard name is invalid.", e);
}
leaderboard, err = leaderboardClient.Leaderboard(ctx, &momento.LeaderboardRequest{
CacheName: cacheName,
LeaderboardName: "leaderboard",
})
if err != nil {
panic(err)
}
return leaderboard
Leaderboard Methods
Upsert elements
リーダボードにまだ要素が存在しない場合、要素を挿入する。要素がすでにリーダーボードに存在する場合は、要素を更新する。upsert呼び出しは成功するか失敗するかのどちらかです。
Parameters
-
elements - Dictionary: upsert する (id, score) ペアのDictionary。
- id: integer
- score: double
-
idはプレイヤー識別子、セッション識別子、ブラウザ識別子、またはこのスコアボードに使いたい他の種類の識別子にすることができます。0から2^63-1まで、64ビットの符号なし整数で指定できます。
-
つまり、1人の選手が2つのIDを持っていない限り、2つのスコアを持つことはできません!
Returns
以下のいずれか:
- JavaScript
- Java
- Go
// Upsert a set of elements as a Map
const elements1: Map<number, number> = new Map([
[123, 100.0],
[234, 200.0],
[345, 300.0],
[456, 400.0],
]);
const result1 = await leaderboard.upsert(elements1);
switch (result1.type) {
case LeaderboardUpsertResponse.Success:
console.log('Successfully upserted elements to leaderboard');
break;
case LeaderboardUpsertResponse.Error:
console.log('Upsert error:', result1.message());
throw new Error(
`An error occurred while attempting to call upsert on leaderboard 'momento-leaderboard' in cache '${cacheName}': ${result1.errorCode()}: ${result1.message()}`
);
}
// Or upsert a set of elements as a Record
const elements2: Record<number, number> = {
567: 500,
678: 600,
789: 700,
890: 800,
};
const result2 = await leaderboard.upsert(elements2);
switch (result2.type) {
case LeaderboardUpsertResponse.Success:
console.log('Successfully upserted elements to leaderboard');
break;
case LeaderboardUpsertResponse.Error:
console.log('Upsert error:', result2.message());
throw new Error(
`An error occurred while attempting to call upsert on leaderboard 'momento-leaderboard' in cache '${cacheName}': ${result2.errorCode()}: ${result2.message()}`
);
}
final Map<Integer, Double> elements =
Map.of(
123, 100.0,
456, 200.0,
789, 300.0);
final UpsertResponse response = leaderboard.upsert(elements).join();
if (response instanceof UpsertResponse.Success) {
System.out.println("Successfully upserted elements.");
} else if (response instanceof UpsertResponse.Error error) {
throw new RuntimeException(
"An error occurred while attempting to call upsert elements into a leaderboard: "
+ error.getErrorCode(),
error);
}
upsertElements := []momento.LeaderboardUpsertElement{
{Id: 123, Score: 10.33},
{Id: 456, Score: 3333},
{Id: 789, Score: 5678.9},
}
_, err := leaderboard.Upsert(ctx, momento.LeaderboardUpsertRequest{Elements: upsertElements})
if err != nil {
panic(err)
}
アップサートはバッチ操作として実装されているので、大規模なリーダーボードでは、最大8192要素のバッチでアップサートを行うことができます。
- JavaScript
- Java
// To upsert a large number of elements, you must upsert
// in batches of up to 8192 elements at a time.
// This example shows how to paginate for a large value of `totalNumElements`, such as `20000`.
const elements = [...Array(totalNumElements).keys()].map(i => {
return {id: i + 1, score: i * Math.random()};
});
for (let i = 0; i < totalNumElements; i += 8192) {
// Create a Map containing 8192 elements at a time
const batch = new Map(elements.slice(i, i + 8192).map(obj => [obj['id'], obj['score']]));
// Then upsert one batch at a time until all elements have been ingested
const result = await leaderboard.upsert(batch);
switch (result.type) {
case LeaderboardUpsertResponse.Success:
break;
case LeaderboardUpsertResponse.Error:
console.log(`Error upserting batch [${i}, ${i + 8192})`);
break;
}
}
final Map<Integer, Double> elements =
IntStream.range(0, TOTAL_NUM_ELEMENTS)
.boxed()
.collect(
Collectors.toMap(i -> i + 1, i -> i * ThreadLocalRandom.current().nextDouble()));
for (long i = 0; i < elements.size(); i += 8192) {
final Map<Integer, Double> batch =
elements.entrySet().stream()
.skip(i)
.limit(8192)
.collect(
Collectors.toMap(
Map.Entry::getKey, Map.Entry::getValue, (v1, v2) -> v1, HashMap::new));
final UpsertResponse response = leaderboard.upsert(batch).join();
if (response instanceof UpsertResponse.Success) {
System.out.printf("Successfully upserted batch of %d elements%n", batch.size());
} else if (response instanceof UpsertResponse.Error error) {
throw new RuntimeException(
"An error occurred while upserting a batch of elements: " + error.getErrorCode(),
error);
}
}
Fetch elements by score
指定された最小スコアと最大スコアの範囲内にある要素を取得する。
同じスコアを持つ要素は、そのIDに基づいて英数字順に返されます(例えば、同じスコアを持つ要素のIDは、[1, 2, 10, 123, 234, ...]ではなく、[1, 10, 123, 2, 234, ...]の順に返されます)。
Parameters
Optional Parameters
- minScore - double: スコア範囲の下限を含む。デフォルトは
-inf
- maxScore - double: スコア範囲の排他的上限。デフォルトは
+inf
- order - LeaderboardOrder enum: 要素を取得する順番。デフォルトは昇順で、0が最も低いスコア
- offset - integer: 最初の要素を返す前にスキップする要素数。デフォルトは0
- count - integer: 返す要素の最大数。デフォルトは8192。
Returns
以下のいずれか:
- JavaScript
- Java
- Go
// By default, FetchByScore will fetch the elements from the entire score range
// with zero offset in ascending order. It can return 8192 elements at a time.
const result1 = await leaderboard.fetchByScore();
switch (result1.type) {
case LeaderboardFetchResponse.Success:
console.log('Successfully fetched elements using open score range:');
result1.values().forEach(element => {
console.log(`\tId: ${element.id} | Rank: ${element.rank} | Score: ${element.score}`);
});
break;
case LeaderboardFetchResponse.Error:
throw new Error(
`An error occurred while attempting to call fetchByScore with no options on leaderboard 'momento-leaderboard' in cache '${cacheName}': ${result1.errorCode()}: ${result1.message()}`
);
}
// Example specifying all FetchByScore options. You can provide any subset of these options
// to modify your FetchByScore request.
const result2 = await leaderboard.fetchByScore({
minScore: 10,
maxScore: 600,
order: LeaderboardOrder.Descending,
offset: 2,
count: 10,
});
switch (result2.type) {
case LeaderboardFetchResponse.Success:
console.log('Successfully fetched elements by score using all options:');
result2.values().forEach(element => {
console.log(`\tId: ${element.id} | Rank: ${element.rank} | Score: ${element.score}`);
});
break;
case LeaderboardFetchResponse.Error:
throw new Error(
`An error occurred while attempting to call fetchByScore with all options on leaderboard 'momento-leaderboard' in cache '${cacheName}': ${result2.errorCode()}: ${result2.message()}`
);
}
// By default, FetchByScore will fetch the elements from the entire score range
// with zero offset in ascending order. It can return 8192 elements at a time.
FetchResponse response = leaderboard.fetchByScore().join();
if (response instanceof FetchResponse.Success success) {
System.out.println("Successfully fetched elements:");
for (LeaderboardElement element : success.values()) {
System.out.printf(
"id: %d, score: %.2f, rank: %d%n",
element.getId(), element.getScore(), element.getRank());
}
} else if (response instanceof FetchResponse.Error error) {
throw new RuntimeException(
"An error occurred while attempting to fetch elements: " + error.getErrorCode(), error);
}
// Example specifying all FetchByScore options. You can provide any subset of these options
// to modify your FetchByScore request.
response = leaderboard.fetchByScore(1.0, 5.0, SortOrder.DESCENDING, 10, 10).join();
if (response instanceof FetchResponse.Success success) {
System.out.println("Successfully fetched elements:");
for (LeaderboardElement element : success.values()) {
System.out.printf(
"id: %d, score: %.2f, rank: %d%n",
element.getId(), element.getScore(), element.getRank());
}
} else if (response instanceof FetchResponse.Error error) {
throw new RuntimeException(
"An error occurred while attempting to fetch elements: " + error.getErrorCode(), error);
}
minScore := 150.0
maxScore := 3000.0
offset := uint32(1)
count := uint32(2)
fetchOrder := momento.ASCENDING
fetchByScoreResponse, err := leaderboard.FetchByScore(ctx, momento.LeaderboardFetchByScoreRequest{
MinScore: &minScore,
MaxScore: &maxScore,
Offset: &offset,
Count: &count,
Order: &fetchOrder,
})
if err != nil {
panic(err)
}
switch r := fetchByScoreResponse.(type) {
case *responses.LeaderboardFetchSuccess:
fmt.Printf("Successfully fetched elements by score:\n")
for _, element := range r.Values() {
fmt.Printf("ID: %d, Score: %f, Rank: %d\n", element.Id, element.Score, element.Rank)
}
}
FetchByScoreはバッチ操作として実装されているので、大規模なリーダーボードでは、最大8192個の要素をバッチでフェッチすることができます。
offset`パラメータを使用すると、要求されたスコア範囲内にある複数の要素を、要求された要素の終わりを示す空のリストを受け取るまで、ページ送りすることができます。
- JavaScript
- Java
// Use the offset option to paginate through your results if your leaderboard
// has more than 8192 elements.
// This example shows how to paginate for a large value of `totalNumElements`, such as `20000`.
for (let offset = 0; offset < totalNumElements; offset += 8192) {
const result = await leaderboard.fetchByScore({offset});
switch (result.type) {
case LeaderboardFetchResponse.Success:
processBatch(result.values());
break;
case LeaderboardFetchResponse.Error:
console.log(
`Error fetching batch by score [${offset}, ${offset + 8192}) (${result.errorCode()}: ${result.message()})`
);
}
}
for (int offset = 0; offset < TOTAL_NUM_ELEMENTS; offset += 8192) {
final FetchResponse response =
leaderboard.fetchByScore(null, null, null, offset, null).join();
if (response instanceof FetchResponse.Success success) {
System.out.println("Successfully fetched elements:");
for (LeaderboardElement element : success.values()) {
System.out.printf(
"id: %d, score: %.2f, rank: %d%n",
element.getId(), element.getScore(), element.getRank());
}
} else if (response instanceof FetchResponse.Error error) {
throw new RuntimeException(
"An error occurred while attempting to fetch elements: " + error.getErrorCode(), error);
}
}
Fetch elements by rank
指定された最小ランクと最大ランクの範囲内にある要素を取得する。
Parameters
- startRank - integer: ランク範囲の包含下限。デフォルトは0
- endRank - integer: ランク範囲の排他的な上限。デフォルトは
startRank
+ 8192
Optional Parameters
- order - LeaderboardOrder enum: 要素を取得する順番。デフォルトは昇順で、0が最も低いスコア
Returns
以下のいずれか:
- JavaScript
- Java
- Go
// By default, FetchByRank will fetch the elements in the range [startRank, endRank)
// in ascending order, meaning rank 0 is for the lowest score.
const result1 = await leaderboard.fetchByRank(0, 10);
switch (result1.type) {
case LeaderboardFetchResponse.Success:
console.log('Successfully fetched elements in rank range [0,10)');
result1.values().forEach(element => {
console.log(`\tId: ${element.id} | Rank: ${element.rank} | Score: ${element.score}`);
});
break;
case LeaderboardFetchResponse.Error:
throw new Error(
`An error occurred while attempting to call fetchByRank with no options on leaderboard 'momento-leaderboard' in cache '${cacheName}': ${result1.errorCode()}: ${result1.message()}`
);
}
final FetchResponse response = leaderboard.fetchByRank(0, 10, SortOrder.ASCENDING).join();
if (response instanceof FetchResponse.Success success) {
System.out.println("Successfully fetched elements:");
for (LeaderboardElement element : success.values()) {
System.out.printf(
"id: %d, score: %.2f, rank: %d%n",
element.getId(), element.getScore(), element.getRank());
}
} else if (response instanceof FetchResponse.Error error) {
throw new RuntimeException(
"An error occurred while attempting to fetch elements: " + error.getErrorCode(), error);
}
fetchOrder := momento.ASCENDING
fetchByRankResponse, err := leaderboard.FetchByRank(ctx, momento.LeaderboardFetchByRankRequest{
StartRank: 0,
EndRank: 100,
Order: &fetchOrder,
})
if err != nil {
panic(err)
}
switch r := fetchByRankResponse.(type) {
case *responses.LeaderboardFetchSuccess:
fmt.Printf("Successfully fetched elements by rank:\n")
for _, element := range r.Values() {
fmt.Printf("ID: %d, Score: %f, Rank: %d\n", element.Id, element.Score, element.Rank)
}
}