public async Task UpdateScore(ScoreRecord score, string leaderboardName) { var client = await CreateClient(leaderboardName); score.CreatedOn = DateTime.UtcNow; await client.IndexAsync(score); }
public async Task <long> GetRanking(ScoreRecord score, LeaderboardQuery filters, string leaderboardName) { var client = await CreateClient(leaderboardName); var rankResult = await client.CountAsync <ScoreRecord>(desc => desc .Query(query => CreateQuery(query, filters, q => { var mustClauses = new List <Func <Nest.QueryContainerDescriptor <ScoreRecord>, Nest.QueryContainer> >(); mustClauses.Add(q1 => q1.Range(r => r.Field(record => record.Score).GreaterThan(score.Score))); if (!EnableExequo) { mustClauses.Add(q1 => q1.Bool(b2 => b2.Must( q2 => q2.Term(t => t.Field(record => record.Score).Value(score.Score)), q2 => q2.DateRange(r => r.Field(record => record.CreatedOn).LessThan(score.CreatedOn)) ))); } return(q.Bool(b => b.Should(mustClauses))); } ))); if (!rankResult.IsValid) { throw new InvalidOperationException($"Failed to compute rank. {rankResult.ServerError.Error.Reason}"); } return(rankResult.Count + 1); }
public Nest.QueryContainer CreateNextPaginationFilter(Nest.QueryContainerDescriptor <ScoreRecord> q, ScoreRecord pivot) { // ( score < pivot.score) OR (score == pivot.score AND createdOn > pivot.createdOn) return(q.Bool(b1 => b1.Should( q1 => q1.Range(r => r.Field(record => record.Score).LessThan(pivot.Score)), q1 => q1.Bool(b2 => b2.Must( q2 => q2.Term(t => t.Field(record => record.Score).Value(pivot.Score)), q2 => q2.DateRange(r => r.Field(record => record.CreatedOn).GreaterThan(pivot.CreatedOn)) )) ))); }
public async Task <LeaderboardResult <ScoreRecord> > Query(LeaderboardQuery rq) { var isContinuation = rq is LeaderboardContinuationQuery; var client = await CreateClient(rq); ScoreRecord start = null; if (!string.IsNullOrEmpty(rq.StartId)) { start = await GetScore(rq.StartId, rq.Name); if (start == null) { throw new ClientException("Admiral not found in leadeboard."); } } var result = await client.SearchAsync <ScoreRecord>(s => { s = s.AllowNoIndices(); s = s.Query(query => CreateQuery(query, rq, q => { if (start != null)//If we have a pivot we must add constraint to start the result around it. { //Create next/previous additional constraints if ((rq as LeaderboardContinuationQuery)?.IsPrevious == true) { return(CreatePreviousPaginationFilter(q, start)); } else { return(CreateNextPaginationFilter(q, start)); } } else { return(q); } })).AllowNoIndices(); if ((rq as LeaderboardContinuationQuery)?.IsPrevious == true) { s = s.Sort(sort => sort.Ascending(record => record.Score).Descending(record => record.CreatedOn)); } else { s = s.Sort(sort => sort.Descending(record => record.Score).Ascending(record => record.CreatedOn)); } if ((isContinuation && (rq as LeaderboardContinuationQuery)?.IsPrevious == false) || start == null) { s = s.Size(rq.Count + 1).From(rq.Skip);// We get one more document than necessary to be able to determine if we can build a "next" continuation } else// The pivot is not included in the result set, if we are not running a continuation query, we must prefix the results with the pivot. { s = s.Size(rq.Count).From(rq.Skip); } return(s); }); if (!result.IsValid) { if (result.ServerError.Status == 404) { return(new LeaderboardResult <ScoreRecord> { Results = new List <LeaderboardRanking <ScoreRecord> >() }); } _logger.Log(LogLevel.Error, "leaderboard", "failed to process query request.", result.ServerError); throw new InvalidOperationException($"Failed to query leaderboard : {result.ServerError.Error.Reason}"); } var documents = result.Documents.ToList(); if (!isContinuation && start != null) { documents.Insert(0, start); } else if ((rq as LeaderboardContinuationQuery)?.IsPrevious == true) { documents.Reverse(); } //Compute rankings if (documents.Any()) { int firstRank = 0; try { firstRank = (int) await GetRanking(documents.First(), rq, rq.Name); } catch (InvalidOperationException ex) { _logger.Log(LogLevel.Error, "leaderboard", ex.Message, ex); throw new InvalidOperationException($"Failed to query leaderboard : {ex.Message}"); } var rank = firstRank; var lastScore = int.MaxValue; var lastRank = firstRank; var results = new List <LeaderboardRanking <ScoreRecord> >(); foreach (var doc in documents.Take(rq.Count)) { if (EnableExequo) { int currentRank; if (doc.Score == lastScore) { currentRank = lastRank; } else { currentRank = rank; } results.Add(new LeaderboardRanking <ScoreRecord> { Document = doc, Ranking = currentRank }); lastRank = currentRank; } else { results.Add(new LeaderboardRanking <ScoreRecord> { Document = doc, Ranking = rank }); } rank++; } var leaderboardResult = new LeaderboardResult <ScoreRecord> { Results = results }; if (firstRank > 1)//There are scores before the first in the list { var previousQuery = new LeaderboardContinuationQuery(rq); previousQuery.Skip = 0; previousQuery.Count = rq.Count; previousQuery.IsPrevious = true; previousQuery.StartId = results.First().Document.Id; leaderboardResult.Previous = SerializeContinuationQuery(previousQuery); } if (documents.Count > rq.Count || (rq as LeaderboardContinuationQuery)?.IsPrevious == true)//there are scores after the last in the list. { var nextQuery = new LeaderboardContinuationQuery(rq); nextQuery.Skip = 0; nextQuery.Count = rq.Count; nextQuery.IsPrevious = false; nextQuery.StartId = results.Last().Document.Id; leaderboardResult.Next = SerializeContinuationQuery(nextQuery); } return(leaderboardResult); } else { return(new LeaderboardResult <ScoreRecord>()); } }