This commit is contained in:
@@ -1,13 +1,11 @@
|
||||
use super::{SqliteSearchAdapter, create_search_adapter};
|
||||
use super::{create_search_adapter, SqliteSearchAdapter};
|
||||
use domain::{
|
||||
models::{
|
||||
EntityType, IndexableDocument, Movie,
|
||||
Person, PersonId, SearchFilters, SearchQuery,
|
||||
ExternalPersonId,
|
||||
collections::PageParams,
|
||||
collections::PageParams, EntityType, ExternalPersonId, IndexableDocument, Movie, Person,
|
||||
PersonId, SearchFilters, SearchQuery,
|
||||
},
|
||||
value_objects::{MovieId, MovieTitle, ReleaseYear},
|
||||
ports::{SearchCommand, SearchPort},
|
||||
value_objects::{MovieId, MovieTitle, ReleaseYear},
|
||||
};
|
||||
use sqlx::SqlitePool;
|
||||
|
||||
@@ -17,33 +15,43 @@ async fn pool_with_schema() -> SqlitePool {
|
||||
"CREATE TABLE movies (id TEXT PRIMARY KEY, title TEXT NOT NULL,
|
||||
release_year INTEGER, director TEXT, poster_path TEXT, external_metadata_id TEXT)",
|
||||
)
|
||||
.execute(&pool).await.unwrap();
|
||||
.execute(&pool)
|
||||
.await
|
||||
.unwrap();
|
||||
sqlx::query(
|
||||
"CREATE TABLE persons (id TEXT PRIMARY KEY, external_id TEXT UNIQUE,
|
||||
tmdb_person_id INTEGER UNIQUE, name TEXT NOT NULL,
|
||||
known_for_department TEXT, profile_path TEXT)",
|
||||
)
|
||||
.execute(&pool).await.unwrap();
|
||||
.execute(&pool)
|
||||
.await
|
||||
.unwrap();
|
||||
sqlx::query(
|
||||
"CREATE TABLE movie_cast (movie_id TEXT, tmdb_person_id INTEGER,
|
||||
name TEXT, character TEXT, billing_order INTEGER, profile_path TEXT)",
|
||||
)
|
||||
.execute(&pool).await.unwrap();
|
||||
sqlx::query(
|
||||
"CREATE TABLE movie_genres (movie_id TEXT, tmdb_id INTEGER, name TEXT)",
|
||||
)
|
||||
.execute(&pool).await.unwrap();
|
||||
.execute(&pool)
|
||||
.await
|
||||
.unwrap();
|
||||
sqlx::query("CREATE TABLE movie_genres (movie_id TEXT, tmdb_id INTEGER, name TEXT)")
|
||||
.execute(&pool)
|
||||
.await
|
||||
.unwrap();
|
||||
sqlx::query(
|
||||
"CREATE VIRTUAL TABLE movies_fts USING fts5(
|
||||
movie_id UNINDEXED, title, director, overview, genres, keywords,
|
||||
cast_names, crew_names, release_year UNINDEXED, language UNINDEXED)",
|
||||
)
|
||||
.execute(&pool).await.unwrap();
|
||||
.execute(&pool)
|
||||
.await
|
||||
.unwrap();
|
||||
sqlx::query(
|
||||
"CREATE VIRTUAL TABLE people_fts USING fts5(
|
||||
person_id UNINDEXED, name, known_for_department UNINDEXED)",
|
||||
)
|
||||
.execute(&pool).await.unwrap();
|
||||
.execute(&pool)
|
||||
.await
|
||||
.unwrap();
|
||||
pool
|
||||
}
|
||||
|
||||
@@ -72,18 +80,32 @@ async fn index_and_search_movie_by_title() {
|
||||
let movie_id = movie.id().clone();
|
||||
|
||||
sqlx::query("INSERT INTO movies VALUES (?, ?, ?, ?, ?, ?)")
|
||||
.bind(id_str).bind("Interstellar").bind(2014i32)
|
||||
.bind("Christopher Nolan").bind::<Option<String>>(None).bind::<Option<String>>(None)
|
||||
.execute(&pool).await.unwrap();
|
||||
.bind(id_str)
|
||||
.bind("Interstellar")
|
||||
.bind(2014i32)
|
||||
.bind("Christopher Nolan")
|
||||
.bind::<Option<String>>(None)
|
||||
.bind::<Option<String>>(None)
|
||||
.execute(&pool)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
cmd.index(IndexableDocument::Movie { id: movie_id.clone(), movie: Box::new(movie), profile: None })
|
||||
.await.unwrap();
|
||||
cmd.index(IndexableDocument::Movie {
|
||||
id: movie_id.clone(),
|
||||
movie: Box::new(movie),
|
||||
profile: None,
|
||||
})
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let results = query.search(&SearchQuery {
|
||||
text: Some("Interstellar".to_string()),
|
||||
filters: SearchFilters::default(),
|
||||
page: default_page(),
|
||||
}).await.unwrap();
|
||||
let results = query
|
||||
.search(&SearchQuery {
|
||||
text: Some("Interstellar".to_string()),
|
||||
filters: SearchFilters::default(),
|
||||
page: default_page(),
|
||||
})
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(results.movies.items.len(), 1);
|
||||
assert_eq!(results.movies.items[0].title, "Interstellar");
|
||||
@@ -99,19 +121,33 @@ async fn remove_movie_clears_from_index() {
|
||||
let movie_id = movie.id().clone();
|
||||
|
||||
sqlx::query("INSERT INTO movies VALUES (?, ?, ?, ?, ?, ?)")
|
||||
.bind(id_str).bind("Inception").bind(2010i32)
|
||||
.bind("Christopher Nolan").bind::<Option<String>>(None).bind::<Option<String>>(None)
|
||||
.execute(&pool).await.unwrap();
|
||||
.bind(id_str)
|
||||
.bind("Inception")
|
||||
.bind(2010i32)
|
||||
.bind("Christopher Nolan")
|
||||
.bind::<Option<String>>(None)
|
||||
.bind::<Option<String>>(None)
|
||||
.execute(&pool)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
cmd.index(IndexableDocument::Movie { id: movie_id.clone(), movie: Box::new(movie), profile: None })
|
||||
.await.unwrap();
|
||||
cmd.index(IndexableDocument::Movie {
|
||||
id: movie_id.clone(),
|
||||
movie: Box::new(movie),
|
||||
profile: None,
|
||||
})
|
||||
.await
|
||||
.unwrap();
|
||||
cmd.remove(EntityType::Movie, id_str).await.unwrap();
|
||||
|
||||
let results = query.search(&SearchQuery {
|
||||
text: Some("Inception".to_string()),
|
||||
filters: SearchFilters::default(),
|
||||
page: default_page(),
|
||||
}).await.unwrap();
|
||||
let results = query
|
||||
.search(&SearchQuery {
|
||||
text: Some("Inception".to_string()),
|
||||
filters: SearchFilters::default(),
|
||||
page: default_page(),
|
||||
})
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
assert!(results.movies.items.is_empty());
|
||||
}
|
||||
@@ -126,32 +162,54 @@ async fn search_with_genre_filter() {
|
||||
let movie_id = movie.id().clone();
|
||||
|
||||
sqlx::query("INSERT INTO movies VALUES (?, ?, ?, ?, ?, ?)")
|
||||
.bind(id_str).bind("The Dark Knight").bind(2008i32)
|
||||
.bind("Christopher Nolan").bind::<Option<String>>(None).bind::<Option<String>>(None)
|
||||
.execute(&pool).await.unwrap();
|
||||
.bind(id_str)
|
||||
.bind("The Dark Knight")
|
||||
.bind(2008i32)
|
||||
.bind("Christopher Nolan")
|
||||
.bind::<Option<String>>(None)
|
||||
.bind::<Option<String>>(None)
|
||||
.execute(&pool)
|
||||
.await
|
||||
.unwrap();
|
||||
sqlx::query("INSERT INTO movie_genres VALUES (?, 1, 'Action')")
|
||||
.bind(id_str)
|
||||
.execute(&pool).await.unwrap();
|
||||
.execute(&pool)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
cmd.index(IndexableDocument::Movie {
|
||||
id: movie_id.clone(),
|
||||
movie: Box::new(movie),
|
||||
profile: None,
|
||||
}).await.unwrap();
|
||||
})
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
// Matching genre — no text filter
|
||||
let results = query.search(&SearchQuery {
|
||||
text: None,
|
||||
filters: SearchFilters { genre: Some("Action".to_string()), ..Default::default() },
|
||||
page: default_page(),
|
||||
}).await.unwrap();
|
||||
let results = query
|
||||
.search(&SearchQuery {
|
||||
text: None,
|
||||
filters: SearchFilters {
|
||||
genre: Some("Action".to_string()),
|
||||
..Default::default()
|
||||
},
|
||||
page: default_page(),
|
||||
})
|
||||
.await
|
||||
.unwrap();
|
||||
assert_eq!(results.movies.items.len(), 1);
|
||||
|
||||
// Non-matching genre
|
||||
let results = query.search(&SearchQuery {
|
||||
text: None,
|
||||
filters: SearchFilters { genre: Some("Comedy".to_string()), ..Default::default() },
|
||||
page: default_page(),
|
||||
}).await.unwrap();
|
||||
let results = query
|
||||
.search(&SearchQuery {
|
||||
text: None,
|
||||
filters: SearchFilters {
|
||||
genre: Some("Comedy".to_string()),
|
||||
..Default::default()
|
||||
},
|
||||
page: default_page(),
|
||||
})
|
||||
.await
|
||||
.unwrap();
|
||||
assert!(results.movies.items.is_empty());
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user