diff --git a/backend-services/voting-app/src/main/java/org/cardano/foundation/voting/repository/VoteRepository.java b/backend-services/voting-app/src/main/java/org/cardano/foundation/voting/repository/VoteRepository.java index 51863c29c..48e498c2d 100644 --- a/backend-services/voting-app/src/main/java/org/cardano/foundation/voting/repository/VoteRepository.java +++ b/backend-services/voting-app/src/main/java/org/cardano/foundation/voting/repository/VoteRepository.java @@ -14,9 +14,12 @@ public interface VoteRepository extends JpaRepository { @Query("SELECT v.categoryId as categoryId, v.proposalId as proposalId FROM Vote v WHERE v.eventId = :eventId AND v.voterStakingAddress = :stakeAddress ORDER BY v.votedAtSlot, v.idNumericHash ASC") - List getVotesByStakeAddress(@Param("eventId") String eventId, @Param("stakeAddress") String stakeAddress); + List getVotesByStakeAddress(@Param("eventId") String eventId, + @Param("stakeAddress") String stakeAddress); - Optional findByEventIdAndCategoryIdAndVoterStakingAddress(String eventId, String categoryId, String voterStakeAddress); + Optional findByEventIdAndCategoryIdAndVoterStakingAddress(String eventId, + String categoryId, + String voterStakeAddress); @Query("SELECT COUNT(v) AS totalVoteCount, SUM(v.votingPower) AS totalVotingPower FROM Vote v WHERE v.eventId = :eventId") List getHighLevelEventStats(@Param("eventId") String eventId); @@ -25,7 +28,8 @@ public interface VoteRepository extends JpaRepository { List getHighLevelCategoryLevelStats(@Param("eventId") String eventId); @Query("SELECT v.categoryId as categoryId, v.proposalId AS proposalId, COUNT(v) AS totalVoteCount, SUM(v.votingPower) AS totalVotingPower FROM Vote v WHERE v.eventId = :eventId AND v.categoryId = :categoryId GROUP BY categoryId, proposalId ORDER BY totalVotingPower DESC, totalVoteCount DESC") - List getCategoryLevelStats(@Param("eventId") String eventId, @Param("categoryId") String categoryId); + List getCategoryLevelStats(@Param("eventId") String eventId, + @Param("categoryId") String categoryId); interface CategoryProposalProjection { diff --git a/backend-services/voting-app/src/main/java/org/cardano/foundation/voting/service/leader_board/DefaultLeaderBoardService.java b/backend-services/voting-app/src/main/java/org/cardano/foundation/voting/service/leader_board/DefaultLeaderBoardService.java index 1659d3589..06bb9d941 100644 --- a/backend-services/voting-app/src/main/java/org/cardano/foundation/voting/service/leader_board/DefaultLeaderBoardService.java +++ b/backend-services/voting-app/src/main/java/org/cardano/foundation/voting/service/leader_board/DefaultLeaderBoardService.java @@ -5,6 +5,8 @@ import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.cardano.foundation.voting.client.ChainFollowerClient; +import org.cardano.foundation.voting.client.ChainFollowerClient.CategoryDetailsResponse; +import org.cardano.foundation.voting.client.ChainFollowerClient.EventDetailsResponse; import org.cardano.foundation.voting.domain.Leaderboard; import org.cardano.foundation.voting.repository.CustomVoteRepository; import org.cardano.foundation.voting.repository.VoteRepository; @@ -58,7 +60,8 @@ public Either isHighLevelEventLeaderboardAvailable(String even @Override @Transactional(readOnly = true) - public Either isHighLevelCategoryLeaderboardAvailable(String event, boolean forceLeaderboard) { + public Either isHighLevelCategoryLeaderboardAvailable(String event, + boolean forceLeaderboard) { var eventDetailsE = chainFollowerClient.getEventDetails(event); if (eventDetailsE.isEmpty()) { return Either.left(Problem.builder() @@ -84,7 +87,8 @@ public Either isHighLevelCategoryLeaderboardAvailable(String e @Override @Transactional(readOnly = true) - public Either getEventLeaderboard(String event, boolean forceLeaderboard) { + public Either getEventLeaderboard(String event, + boolean forceLeaderboard) { var eventDetailsE = chainFollowerClient.getEventDetails(event); if (eventDetailsE.isEmpty()) { return Either.left(Problem.builder() @@ -168,8 +172,13 @@ public Either getEventLeaderboard(String even byCategoryStatsBuilder.votes(Optional.ofNullable(categoryLevelStats.getTotalVoteCount()).orElse(0L)); switch (eventDetails.votingEventType()) { - case BALANCE_BASED, STAKE_BASED -> - byCategoryStatsBuilder.votingPower(Optional.ofNullable(categoryLevelStats.getTotalVotingPower()).map(String::valueOf).orElse("0")); + case BALANCE_BASED, STAKE_BASED -> { + var votingPowerM = Optional.ofNullable(categoryLevelStats.getTotalVotingPower()) + .map(String::valueOf) + .orElse("0"); + + byCategoryStatsBuilder.votingPower(votingPowerM); + } } return byCategoryStatsBuilder.build(); @@ -181,6 +190,7 @@ public Either getEventLeaderboard(String even var byCategoryStatsCopy = new ArrayList<>(byCategoryStats); // pre init with empty if category not returned from db + eventDetails.categories().forEach(categoryDetails -> { if (!byCategoryStatsMap.containsKey(categoryDetails.id())) { var b = Leaderboard.ByCategoryStats.builder(); @@ -203,7 +213,9 @@ public Either getEventLeaderboard(String even @Override @Transactional(readOnly = true) - public Either getCategoryLeaderboard(String event, String category, boolean forceLeaderboard) { + public Either getCategoryLeaderboard(String event, + String category, + boolean forceLeaderboard) { var eventDetailsE = chainFollowerClient.getEventDetails(event); if (eventDetailsE.isEmpty()) { return Either.left(Problem.builder() @@ -279,7 +291,8 @@ public Either getCategoryLeader @Override @Transactional(readOnly = true, isolation = SERIALIZABLE) - public Either> getEventWinners(String event, boolean forceLeaderboard) { + public Either> getEventWinners(String event, + boolean forceLeaderboard) { var eventDetailsE = chainFollowerClient.getEventDetails(event); if (eventDetailsE.isEmpty()) { @@ -316,13 +329,15 @@ public Either> getEventWinners(String eve var categoryIds = eventDetails.categories() .stream() - .map(ChainFollowerClient.CategoryDetailsResponse::id) + .map(CategoryDetailsResponse::id) .toList(); return Either.right(customVoteRepository.getEventWinners(event, categoryIds)); } - private static HashMap calcProposalsResults(ChainFollowerClient.CategoryDetailsResponse categoryDetails, Map proposalResultsMap, ChainFollowerClient.EventDetailsResponse eventDetails) { + private static HashMap calcProposalsResults(CategoryDetailsResponse categoryDetails, + Map proposalResultsMap, + EventDetailsResponse eventDetails) { var categoryProposals = categoryDetails.proposals(); var proposalResultsMapCopy = new HashMap<>(proposalResultsMap); @@ -345,7 +360,9 @@ private static HashMap calcProposalsResults(ChainFoll @Override @Transactional(readOnly = true) - public Either isCategoryLeaderboardAvailable(String event, String category, boolean forceLeaderboard) { + public Either isCategoryLeaderboardAvailable(String event, + String category, + boolean forceLeaderboard) { var eventDetailsE = chainFollowerClient.getEventDetails(event); if (eventDetailsE.isEmpty()) { return Either.left(Problem.builder() @@ -380,7 +397,7 @@ public Either isCategoryLeaderboardAvailable(String event, Str return isCategoryLeaderboardAvailable(eventDetails, forceLeaderboard); } - private Either isHighLevelEventLeaderboardAvailable(ChainFollowerClient.EventDetailsResponse eventDetails, + private Either isHighLevelEventLeaderboardAvailable(EventDetailsResponse eventDetails, boolean forceLeaderboard) { if (forceLeaderboard) { return Either.right(true); @@ -393,7 +410,7 @@ private Either isHighLevelEventLeaderboardAvailable(ChainFollo return Either.right(eventDetails.proposalsReveal()); } - private Either isHighLevelCategoryLeaderboardAvailable(ChainFollowerClient.EventDetailsResponse eventDetails, + private Either isHighLevelCategoryLeaderboardAvailable(EventDetailsResponse eventDetails, boolean forceLeaderboard) { if (forceLeaderboard) { return Either.right(true); @@ -406,7 +423,7 @@ private Either isHighLevelCategoryLeaderboardAvailable(ChainFo return Either.right(eventDetails.proposalsReveal()); } - private Either isCategoryLeaderboardAvailable(ChainFollowerClient.EventDetailsResponse eventDetails, + private Either isCategoryLeaderboardAvailable(EventDetailsResponse eventDetails, boolean forceLeaderboard) { if (forceLeaderboard) { return Either.right(true); diff --git a/backend-services/voting-app/src/main/resources/db/migration/h2/V0__voting_app_init.sql b/backend-services/voting-app/src/main/resources/db/migration/h2/V0__voting_app_init.sql index 5dbcc5b59..063919fdc 100644 --- a/backend-services/voting-app/src/main/resources/db/migration/h2/V0__voting_app_init.sql +++ b/backend-services/voting-app/src/main/resources/db/migration/h2/V0__voting_app_init.sql @@ -21,15 +21,21 @@ CREATE TABLE vote ( CREATE INDEX idx_vote_stake_key ON vote (event_id, category_id, voter_stake_address); +-- index for the leaderboard query CREATE INDEX idx_vote_event_id ON vote (event_id); +-- index for the leaderboard query +CREATE INDEX idx_vote_event_id_category_id + ON vote (event_id, category_id); + +-- index for the leaderboard query CREATE INDEX idx_vote_event_id_category_id_proposal_id ON vote (event_id, category_id, proposal_id); DROP TABLE IF EXISTS vote_merkle_proof; --- benefit of storing vote merkle proof is that upon restart of app voter's receipt can be served from local db +-- benefit of storing vote merkle proof is that upon restart of app voter's receipt can be served from a db CREATE TABLE vote_merkle_proof ( vote_id VARCHAR(255) NOT NULL, vote_id_numeric_hash BIGINT NOT NULL, @@ -53,3 +59,4 @@ CREATE INDEX idx_vote_merkle_proof_vote_id_event_id -- special index to help us find out all vote_merkle_proofs that took part in rolled back transaction CREATE INDEX idx_vote_merkle_proof_transaction_rollback ON vote_merkle_proof (absolute_slot); + diff --git a/backend-services/voting-app/src/main/resources/db/migration/postgresql/V0__voting_app_init.sql b/backend-services/voting-app/src/main/resources/db/migration/postgresql/V0__voting_app_init.sql index 5dbcc5b59..bd914349f 100644 --- a/backend-services/voting-app/src/main/resources/db/migration/postgresql/V0__voting_app_init.sql +++ b/backend-services/voting-app/src/main/resources/db/migration/postgresql/V0__voting_app_init.sql @@ -21,15 +21,21 @@ CREATE TABLE vote ( CREATE INDEX idx_vote_stake_key ON vote (event_id, category_id, voter_stake_address); +-- index for the leaderboard query CREATE INDEX idx_vote_event_id ON vote (event_id); +-- index for the leaderboard query +CREATE INDEX idx_vote_event_id_category_id + ON vote (event_id, category_id); + +-- index for the leaderboard query CREATE INDEX idx_vote_event_id_category_id_proposal_id ON vote (event_id, category_id, proposal_id); DROP TABLE IF EXISTS vote_merkle_proof; --- benefit of storing vote merkle proof is that upon restart of app voter's receipt can be served from local db +-- benefit of storing vote merkle proof is that upon restart of app voter's receipt can be served from a db CREATE TABLE vote_merkle_proof ( vote_id VARCHAR(255) NOT NULL, vote_id_numeric_hash BIGINT NOT NULL,