fix(api): episode rows need a token beyond series title
Per amended DESIGN.md 9.2, an episode row now also requires at least one query token to match the episode title on its own, so a series-title-only query lists the series and no episodes.
This commit is contained in:
+3
-3
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"db_name": "SQLite",
|
"db_name": "SQLite",
|
||||||
"query": "SELECT e.id AS \"episode_id!: i64\", s.id AS \"series_id!: i64\", s.title AS \"series_title!: String\", printf('S%02dE%02d', se.number, e.number) AS \"tag!: String\", e.title AS \"title!: String\", s.poster_path, s.vote_average, s.tmdb_id AS \"series_tmdb_id!: i64\" FROM episodes e JOIN seasons se ON se.id = e.season_id JOIN series s ON s.id = se.series_id WHERE NOT EXISTS (SELECT 1 FROM json_each(?) token WHERE (s.title || ' ' || e.title) NOT LIKE '%' || token.value || '%' ESCAPE '\\') ORDER BY s.title, se.number, e.number, e.id",
|
"query": "SELECT e.id AS \"episode_id!: i64\", s.id AS \"series_id!: i64\", s.title AS \"series_title!: String\", printf('S%02dE%02d', se.number, e.number) AS \"tag!: String\", e.title AS \"title!: String\", s.poster_path, s.vote_average, s.tmdb_id AS \"series_tmdb_id!: i64\" FROM episodes e JOIN seasons se ON se.id = e.season_id JOIN series s ON s.id = se.series_id WHERE NOT EXISTS (SELECT 1 FROM json_each(?) token WHERE (s.title || ' ' || e.title) NOT LIKE '%' || token.value || '%' ESCAPE '\\') AND EXISTS (SELECT 1 FROM json_each(?) token WHERE e.title LIKE '%' || token.value || '%' ESCAPE '\\') ORDER BY s.title, se.number, e.number, e.id",
|
||||||
"describe": {
|
"describe": {
|
||||||
"columns": [
|
"columns": [
|
||||||
{
|
{
|
||||||
@@ -45,7 +45,7 @@
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
"parameters": {
|
"parameters": {
|
||||||
"Right": 1
|
"Right": 2
|
||||||
},
|
},
|
||||||
"nullable": [
|
"nullable": [
|
||||||
true,
|
true,
|
||||||
@@ -58,5 +58,5 @@
|
|||||||
false
|
false
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"hash": "2bc048c0fca4822eb38befbe3091f44f961fafb79432d4771aa48e36d32bf2a2"
|
"hash": "80399f9c14b159b5c4883aaeae370a7869d1104dfd80dc3a568ef134c4659ca9"
|
||||||
}
|
}
|
||||||
@@ -233,9 +233,11 @@ pub async fn search(
|
|||||||
library.extend(series_rows.into_iter().map(LibraryResult::Series));
|
library.extend(series_rows.into_iter().map(LibraryResult::Series));
|
||||||
|
|
||||||
// §9.2 names the TV case explicitly: `bluey hospital` finds the episode.
|
// §9.2 names the TV case explicitly: `bluey hospital` finds the episode.
|
||||||
// Tokens may split across the series and episode titles, so the haystack
|
// Tokens may split across the series and episode titles, so every token
|
||||||
// is both joined and every token must land somewhere in it.
|
// must land in the concatenation — and at least one must match the
|
||||||
let episode_rows = sqlx::query_as!(LibraryEpisode, r#"SELECT e.id AS "episode_id!: i64", s.id AS "series_id!: i64", s.title AS "series_title!: String", printf('S%02dE%02d', se.number, e.number) AS "tag!: String", e.title AS "title!: String", s.poster_path, s.vote_average, s.tmdb_id AS "series_tmdb_id!: i64" FROM episodes e JOIN seasons se ON se.id = e.season_id JOIN series s ON s.id = se.series_id WHERE NOT EXISTS (SELECT 1 FROM json_each(?) token WHERE (s.title || ' ' || e.title) NOT LIKE '%' || token.value || '%' ESCAPE '\') ORDER BY s.title, se.number, e.number, e.id"#, tokens)
|
// episode title on its own, or a series-title-only query would list
|
||||||
|
// every episode the series has.
|
||||||
|
let episode_rows = sqlx::query_as!(LibraryEpisode, r#"SELECT e.id AS "episode_id!: i64", s.id AS "series_id!: i64", s.title AS "series_title!: String", printf('S%02dE%02d', se.number, e.number) AS "tag!: String", e.title AS "title!: String", s.poster_path, s.vote_average, s.tmdb_id AS "series_tmdb_id!: i64" FROM episodes e JOIN seasons se ON se.id = e.season_id JOIN series s ON s.id = se.series_id WHERE NOT EXISTS (SELECT 1 FROM json_each(?) token WHERE (s.title || ' ' || e.title) NOT LIKE '%' || token.value || '%' ESCAPE '\') AND EXISTS (SELECT 1 FROM json_each(?) token WHERE e.title LIKE '%' || token.value || '%' ESCAPE '\') ORDER BY s.title, se.number, e.number, e.id"#, tokens, tokens)
|
||||||
.fetch_all(database.pool())
|
.fetch_all(database.pool())
|
||||||
.await?;
|
.await?;
|
||||||
library.extend(episode_rows.into_iter().map(LibraryResult::Episode));
|
library.extend(episode_rows.into_iter().map(LibraryResult::Episode));
|
||||||
@@ -956,15 +958,32 @@ mod tests {
|
|||||||
assert_eq!(response["tmdb"][0]["kind"], "series");
|
assert_eq!(response["tmdb"][0]["kind"], "series");
|
||||||
assert_eq!(response["tmdb"][0]["title"], "Bluey");
|
assert_eq!(response["tmdb"][0]["title"], "Bluey");
|
||||||
|
|
||||||
|
// §9.2, amended: an episode row surfaces only when the query matches
|
||||||
|
// something beyond the series title. `bluey` alone returns the series
|
||||||
|
// row and no episode below it.
|
||||||
let response: serde_json::Value = reqwest::get(format!("{base}/api/search?q=bluey"))
|
let response: serde_json::Value = reqwest::get(format!("{base}/api/search?q=bluey"))
|
||||||
.await
|
.await
|
||||||
.expect("search")
|
.expect("search")
|
||||||
.json()
|
.json()
|
||||||
.await
|
.await
|
||||||
.expect("json");
|
.expect("json");
|
||||||
assert_eq!(response["library"][0]["kind"], "series");
|
let library = response["library"].as_array().expect("library");
|
||||||
assert_eq!(response["library"][0]["title"], "Bluey");
|
assert_eq!(library.len(), 1);
|
||||||
assert_eq!(response["library"][0]["tmdb_id"], 82_728);
|
assert_eq!(library[0]["kind"], "series");
|
||||||
|
assert_eq!(library[0]["title"], "Bluey");
|
||||||
|
assert_eq!(library[0]["tmdb_id"], 82_728);
|
||||||
|
|
||||||
|
// An episode-title-only query still finds the episode.
|
||||||
|
let response: serde_json::Value = reqwest::get(format!("{base}/api/search?q=hospital"))
|
||||||
|
.await
|
||||||
|
.expect("search")
|
||||||
|
.json()
|
||||||
|
.await
|
||||||
|
.expect("json");
|
||||||
|
let library = response["library"].as_array().expect("library");
|
||||||
|
assert_eq!(library.len(), 1);
|
||||||
|
assert_eq!(library[0]["kind"], "episode");
|
||||||
|
assert_eq!(library[0]["title"], "Hospital");
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
|
|||||||
Reference in New Issue
Block a user