package io.kaif.model.vote;
import java.time.Instant;
import java.time.LocalDate;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.UUID;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
import org.springframework.stereotype.Repository;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.collect.ImmutableMap;
import io.kaif.database.DaoOperations;
import io.kaif.model.zone.Zone;
@Repository
public class HonorRollDao implements DaoOperations {
@Autowired
private NamedParameterJdbcTemplate namedParameterJdbcTemplate;
private final RowMapper<HonorRoll> honorRollRowMapper = (rs, rowNum) -> new HonorRoll(UUID.fromString(
rs.getString("accountId")),
rs.getString("zone") == null ? null : Zone.valueOf(rs.getString("zone")),
// for total
rs.getString("bucket"),
rs.getString("username"),
rs.getLong("articleUpVoted"),
rs.getLong("debateUpVoted"),
rs.getLong("debateDownVoted"));
@Override
public NamedParameterJdbcTemplate namedJdbc() {
return namedParameterJdbcTemplate;
}
public Optional<HonorRoll> findHonorRoll(UUID accountId, Zone zone, Instant instant) {
final String sql = ""
+ " SELECT * "
+ " FROM HonorRoll "
+ " WHERE accountId = ? "
+ " AND zone = ? "
+ " AND bucket = ? "
+ " LIMIT 1 ";
return jdbc().query(sql,
honorRollRowMapper,
accountId,
zone.value(),
monthlyBucket(instant).toString()).stream().findAny();
}
public void updateRotateVoteStats(HonorRollVoter voter) {
String upsert = ""
+ " INSERT "
+ " INTO HonorRoll "
+ " (accountId, zone, bucket, username, articleUpVoted, "
+ " debateUpVoted, debateDownVoted) "
+ " VALUES (:accountId, :zone, :bucket, :username, :deltaArticleUpVoted,"
+ " :deltaDebateUpVoted, :deltaDebateDownVoted) "
+ " ON CONFLICT (accountId, zone, bucket) "
+ " DO UPDATE "
+ " SET articleUpVoted = HonorRoll.articleUpVoted + :deltaArticleUpVoted "
+ " , debateUpVoted = HonorRoll.debateUpVoted + :deltaDebateUpVoted "
+ " , debateDownVoted = HonorRoll.debateDownVoted + :deltaDebateDownVoted ";
Map<String, Object> params = ImmutableMap.<String, Object>builder().put("accountId",
voter.getAccountId())
.put("zone", voter.getZone().value())
.put("bucket", monthlyBucket(voter.getFlakeId()).toString())
.put("username", voter.getUsername())
.put("deltaArticleUpVoted", voter.getDeltaArticleUpVoted())
.put("deltaDebateUpVoted", voter.getDeltaDebateUpVoted())
.put("deltaDebateDownVoted", voter.getDeltaDebateDownVoted())
.build();
namedJdbc().update(upsert, params);
}
public List<HonorRoll> listHonorRollByAccount(UUID accountId, Instant instant) {
final String sql = " SELECT * FROM HonorRoll WHERE accountId = ? AND bucket = ? ORDER BY zone ";
return jdbc().query(sql, honorRollRowMapper, accountId, monthlyBucket(instant).toString());
}
public List<HonorRoll> listHonorAllByAccount(UUID accountId) {
final String sql = ""
+ " SELECT accountId, username, zone, "
+ " '2015-01-01' AS bucket, "
+ " sum(articleUpVoted) AS articleUpVoted, "
+ " sum(debateUpVoted) AS debateUpVoted, "
+ " sum(debateDownVoted) AS debateDownVoted "
+ " FROM HonorRoll "
+ " WHERE accountId = ? "
+ " GROUP BY accountId, username, zone "
+ " ORDER BY zone ";
return jdbc().query(sql, honorRollRowMapper, accountId);
}
/**
* see {@link #listHonorRollWithCache(java.time.LocalDate, int)} for why leak bucket to outside
*/
@Cacheable(value = "listHonorRoll")
public List<HonorRoll> listHonorRollByZoneWithCache(Zone zone, LocalDate bucket, int limit) {
final String sql = ""
+ " SELECT * "
+ " FROM HonorRoll "
+ " WHERE zone = ? "
+ " AND bucket = ? "
+ " ORDER BY (articleUpVoted + debateUpVoted - debateDownVoted) DESC, username ASC "
+ " LIMIT ? ";
return jdbc().query(sql, honorRollRowMapper, zone.value(), bucket.toString(), limit);
}
/**
* argument using `LocalDate bucket` leak rotation logic outside of Dao,
* But to make @Cacheable work properly. we have to do so.
*/
@Cacheable(value = "listHonorRoll")
public List<HonorRoll> listHonorRollWithCache(LocalDate bucket, int limit) {
final String sql = ""
+ " SELECT accountId, bucket, username, "
+ " NULL AS zone, "
+ " sum(articleUpVoted) AS articleUpVoted, "
+ " sum(debateUpVoted) AS debateUpVoted, "
+ " sum(debateDownVoted) AS debateDownVoted, "
+ " sum(articleUpVoted) + sum(debateUpVoted) - sum(debateDownVoted) AS score "
+ " FROM HonorRoll "
+ " WHERE bucket = ? "
+ " GROUP BY accountId, bucket, username "
+ " ORDER BY score DESC, username ASC "
+ " LIMIT ? ";
return jdbc().query(sql, honorRollRowMapper, bucket.toString(), limit);
}
@VisibleForTesting
@CacheEvict(value = "listHonorRoll", allEntries = true)
public void evictAllCaches() {
}
}