Add title to popular posts route to improve site efficiency
This commit is contained in:
parent
3ee97b5d43
commit
1228112213
5 changed files with 59 additions and 22 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
|
@ -1,2 +1,3 @@
|
||||||
/target
|
/target
|
||||||
.env
|
.env
|
||||||
|
upvoters
|
||||||
|
|
|
||||||
7
db/migrations/002_create_slug_to_title.sql
Normal file
7
db/migrations/002_create_slug_to_title.sql
Normal file
|
|
@ -0,0 +1,7 @@
|
||||||
|
CREATE EXTENSION IF NOT EXISTS pgcrypto;
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS titles (
|
||||||
|
slug text not null,
|
||||||
|
title text not null,
|
||||||
|
primary key (slug)
|
||||||
|
);
|
||||||
|
|
@ -13,6 +13,7 @@ use uuid::Uuid;
|
||||||
use crate::{
|
use crate::{
|
||||||
state::AppState,
|
state::AppState,
|
||||||
votes::{
|
votes::{
|
||||||
|
model::VoteRequest,
|
||||||
repository::{delete_vote, get_top_n_slugs, insert_new_vote},
|
repository::{delete_vote, get_top_n_slugs, insert_new_vote},
|
||||||
service::get_votes_and_voted,
|
service::get_votes_and_voted,
|
||||||
},
|
},
|
||||||
|
|
@ -51,10 +52,12 @@ async fn upvote_handler(
|
||||||
jar: CookieJar,
|
jar: CookieJar,
|
||||||
Path(slug): Path<String>,
|
Path(slug): Path<String>,
|
||||||
State(state): State<AppState>,
|
State(state): State<AppState>,
|
||||||
|
Json(upvote_payload): Json<VoteRequest>,
|
||||||
) -> impl IntoResponse {
|
) -> impl IntoResponse {
|
||||||
let (jar, voter_id) = get_or_init_voter_id(jar);
|
let (jar, voter_id) = get_or_init_voter_id(jar);
|
||||||
|
|
||||||
match insert_new_vote(&slug, &voter_id, &state.db).await {
|
info!("{:?}", upvote_payload);
|
||||||
|
match insert_new_vote(&slug, &voter_id, upvote_payload.title, &state.db).await {
|
||||||
Ok(()) => {
|
Ok(()) => {
|
||||||
info!(slug = %slug, voter_id = %voter_id, "upvoted successfully");
|
info!(slug = %slug, voter_id = %voter_id, "upvoted successfully");
|
||||||
(StatusCode::OK, jar, "Successfully upvoted")
|
(StatusCode::OK, jar, "Successfully upvoted")
|
||||||
|
|
@ -260,7 +263,7 @@ mod tests {
|
||||||
let slug = "delete_vote_handler_removes_vote_for_cookie_voter";
|
let slug = "delete_vote_handler_removes_vote_for_cookie_voter";
|
||||||
let voter_id = Uuid::from_u128(0xdef);
|
let voter_id = Uuid::from_u128(0xdef);
|
||||||
delete_votes_for_slug(slug, &db).await.unwrap();
|
delete_votes_for_slug(slug, &db).await.unwrap();
|
||||||
insert_new_vote(slug, &voter_id, &db).await.unwrap();
|
insert_new_vote(slug, &voter_id, None, &db).await.unwrap();
|
||||||
|
|
||||||
let app = crate::app(test_state(db.clone()));
|
let app = crate::app(test_state(db.clone()));
|
||||||
let request = Request::builder()
|
let request = Request::builder()
|
||||||
|
|
@ -309,8 +312,10 @@ mod tests {
|
||||||
let voter_id = Uuid::from_u128(0x123);
|
let voter_id = Uuid::from_u128(0x123);
|
||||||
let other_voter_id = Uuid::from_u128(0x456);
|
let other_voter_id = Uuid::from_u128(0x456);
|
||||||
delete_votes_for_slug(slug, &db).await.unwrap();
|
delete_votes_for_slug(slug, &db).await.unwrap();
|
||||||
insert_new_vote(slug, &voter_id, &db).await.unwrap();
|
insert_new_vote(slug, &voter_id, None, &db).await.unwrap();
|
||||||
insert_new_vote(slug, &other_voter_id, &db).await.unwrap();
|
insert_new_vote(slug, &other_voter_id, None, &db)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
let app = crate::app(test_state(db.clone()));
|
let app = crate::app(test_state(db.clone()));
|
||||||
let request = Request::builder()
|
let request = Request::builder()
|
||||||
|
|
@ -337,7 +342,7 @@ mod tests {
|
||||||
let slug = "get_votes_and_voted_handler_sets_cookie_for_new_voter";
|
let slug = "get_votes_and_voted_handler_sets_cookie_for_new_voter";
|
||||||
let existing_voter_id = Uuid::from_u128(0x789);
|
let existing_voter_id = Uuid::from_u128(0x789);
|
||||||
delete_votes_for_slug(slug, &db).await.unwrap();
|
delete_votes_for_slug(slug, &db).await.unwrap();
|
||||||
insert_new_vote(slug, &existing_voter_id, &db)
|
insert_new_vote(slug, &existing_voter_id, None, &db)
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
|
|
@ -378,12 +383,16 @@ mod tests {
|
||||||
Uuid::from_u128(0x101),
|
Uuid::from_u128(0x101),
|
||||||
Uuid::from_u128(0x102),
|
Uuid::from_u128(0x102),
|
||||||
] {
|
] {
|
||||||
insert_new_vote(slugs[0], &voter_id, &db).await.unwrap();
|
insert_new_vote(slugs[0], &voter_id, None, &db)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
}
|
}
|
||||||
for voter_id in [Uuid::from_u128(0x200), Uuid::from_u128(0x201)] {
|
for voter_id in [Uuid::from_u128(0x200), Uuid::from_u128(0x201)] {
|
||||||
insert_new_vote(slugs[1], &voter_id, &db).await.unwrap();
|
insert_new_vote(slugs[1], &voter_id, None, &db)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
}
|
}
|
||||||
insert_new_vote(slugs[2], &Uuid::from_u128(0x300), &db)
|
insert_new_vote(slugs[2], &Uuid::from_u128(0x300), None, &db)
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,12 @@ use serde::{Deserialize, Serialize};
|
||||||
use sqlx::prelude::FromRow;
|
use sqlx::prelude::FromRow;
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
|
||||||
|
/// Struct representing a vote request
|
||||||
|
#[derive(Debug, Deserialize)]
|
||||||
|
pub struct VoteRequest {
|
||||||
|
pub title: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
/// Struct representing a single vote (row) in the votes table
|
/// Struct representing a single vote (row) in the votes table
|
||||||
#[derive(Debug, FromRow)]
|
#[derive(Debug, FromRow)]
|
||||||
pub struct Vote {
|
pub struct Vote {
|
||||||
|
|
@ -16,4 +22,5 @@ pub struct Vote {
|
||||||
pub struct BestSlugs {
|
pub struct BestSlugs {
|
||||||
pub slug: String,
|
pub slug: String,
|
||||||
pub vote_count: i64,
|
pub vote_count: i64,
|
||||||
|
pub title: String,
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,12 @@ pub async fn vote_exists(slug: &str, voter_id: &Uuid, db: &PgPool) -> Result<boo
|
||||||
.await?;
|
.await?;
|
||||||
Ok(count > 0)
|
Ok(count > 0)
|
||||||
}
|
}
|
||||||
pub async fn insert_new_vote(slug: &str, voter_id: &Uuid, db: &PgPool) -> Result<()> {
|
pub async fn insert_new_vote(
|
||||||
|
slug: &str,
|
||||||
|
voter_id: &Uuid,
|
||||||
|
title: Option<String>,
|
||||||
|
db: &PgPool,
|
||||||
|
) -> Result<()> {
|
||||||
query(
|
query(
|
||||||
r#"insert into votes (slug, voter_id)
|
r#"insert into votes (slug, voter_id)
|
||||||
values ($1, $2)
|
values ($1, $2)
|
||||||
|
|
@ -29,6 +34,14 @@ pub async fn insert_new_vote(slug: &str, voter_id: &Uuid, db: &PgPool) -> Result
|
||||||
.bind(voter_id)
|
.bind(voter_id)
|
||||||
.execute(db)
|
.execute(db)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
|
if let Some(title) = title {
|
||||||
|
query(r#"insert into titles values ($1, $2)"#)
|
||||||
|
.bind(slug)
|
||||||
|
.bind(title)
|
||||||
|
.execute(db)
|
||||||
|
.await?;
|
||||||
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -43,9 +56,9 @@ pub async fn get_vote_count_for_slug(slug: &str, db: &PgPool) -> Result<i64> {
|
||||||
pub async fn get_top_n_slugs(n: i64, db: &PgPool) -> Result<Vec<BestSlugs>> {
|
pub async fn get_top_n_slugs(n: i64, db: &PgPool) -> Result<Vec<BestSlugs>> {
|
||||||
if n > 0 {
|
if n > 0 {
|
||||||
let top_slugs = query_as::<_, BestSlugs>(
|
let top_slugs = query_as::<_, BestSlugs>(
|
||||||
r#"select slug, count(*) AS vote_count
|
r#"select slug, title, count(*) AS vote_count
|
||||||
from votes
|
from votes natural join titles
|
||||||
group by slug
|
group by slug, title
|
||||||
order by vote_count desc
|
order by vote_count desc
|
||||||
limit $1
|
limit $1
|
||||||
"#,
|
"#,
|
||||||
|
|
@ -130,7 +143,7 @@ mod postgres_tests {
|
||||||
cleanup(&db, &votes).await;
|
cleanup(&db, &votes).await;
|
||||||
|
|
||||||
for (slug, voter_id) in votes.iter() {
|
for (slug, voter_id) in votes.iter() {
|
||||||
insert_new_vote(slug, voter_id, &db)
|
insert_new_vote(slug, voter_id, None, &db)
|
||||||
.await
|
.await
|
||||||
.expect("Insertions to db failed");
|
.expect("Insertions to db failed");
|
||||||
}
|
}
|
||||||
|
|
@ -196,22 +209,22 @@ mod postgres_tests {
|
||||||
)];
|
)];
|
||||||
cleanup(&db, &votes).await;
|
cleanup(&db, &votes).await;
|
||||||
|
|
||||||
insert_new_vote(&votes[0].0, &votes[0].1, &db)
|
insert_new_vote(&votes[0].0, &votes[0].1, None, &db)
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
insert_new_vote(&votes[0].0, &votes[0].1, &db)
|
insert_new_vote(&votes[0].0, &votes[0].1, None, &db)
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
insert_new_vote(&votes[0].0, &votes[0].1, &db)
|
insert_new_vote(&votes[0].0, &votes[0].1, None, &db)
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
insert_new_vote(&votes[0].0, &votes[0].1, &db)
|
insert_new_vote(&votes[0].0, &votes[0].1, None, &db)
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
insert_new_vote(&votes[0].0, &votes[0].1, &db)
|
insert_new_vote(&votes[0].0, &votes[0].1, None, &db)
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
insert_new_vote(&votes[0].0, &votes[0].1, &db)
|
insert_new_vote(&votes[0].0, &votes[0].1, None, &db)
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
|
|
@ -231,7 +244,7 @@ mod postgres_tests {
|
||||||
)];
|
)];
|
||||||
cleanup(&db, &votes).await;
|
cleanup(&db, &votes).await;
|
||||||
|
|
||||||
insert_new_vote(&votes[0].0, &votes[0].1, &db)
|
insert_new_vote(&votes[0].0, &votes[0].1, None, &db)
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
|
|
@ -265,10 +278,10 @@ mod postgres_tests {
|
||||||
];
|
];
|
||||||
cleanup(&db, &votes).await;
|
cleanup(&db, &votes).await;
|
||||||
|
|
||||||
insert_new_vote(&votes[0].0, &votes[0].1, &db)
|
insert_new_vote(&votes[0].0, &votes[0].1, None, &db)
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
insert_new_vote(&votes[1].0, &votes[1].1, &db)
|
insert_new_vote(&votes[1].0, &votes[1].1, None, &db)
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue