diff --git a/src/rust/lektor_db/src/connexion.rs b/src/rust/lektor_db/src/connexion.rs index a540a037320aef72e50dc947d31db2989fc3ef06..65551092ed8709ea280a3da9e1b60cfd200dc3c7 100644 --- a/src/rust/lektor_db/src/connexion.rs +++ b/src/rust/lektor_db/src/connexion.rs @@ -1,4 +1,5 @@ use crate::{models::*, types::*, uri::LktUri, *}; +use diesel::connection::DefaultLoadingMode; use hashbrown::HashMap; use kurisu_api::v1 as api_v1; use smallstring::SmallString; @@ -36,6 +37,20 @@ pub enum LktDatabaseSearchType { Database, } +/// Get a table from the schema +macro_rules! table { + ($table: ident) => { + crate::schema::$table::table + }; +} + +/// Get a field from a table in the schema +macro_rules! field { + ($table:ident . $field:ident) => { + crate::schema::$table::$field + }; +} + /// Load the diesel DSL for a given table. With the loaded dsl execute the /// expression... macro_rules! with_dsl { @@ -246,18 +261,20 @@ impl LktDatabaseConnection { /// Search the database with the built search. We return the local ids. pub fn search(&mut self, search: LktDatabaseSearch) -> LktDatabaseResult<Vec<i64>> { - let karas: Vec<_> = match search.uri { + let mut karas: Vec<_> = match search.uri { LktUri::Id(uri_id) => vec![with_dsl! { kara => kara .filter(id.is(uri_id)) .filter(is_virtual.is(false)) + .order_by(id) .select(id) .first::<i64>(&mut self.sqlite)? }], LktUri::KaraMaker(uri_kara_maker) => with_dsl! { kara_maker => kara_maker .filter(name.like(uri_kara_maker.as_str())) - .inner_join(schema::kara::table) - .filter(schema::kara::is_virtual.is(false)) + .inner_join(table!(kara)) + .filter(field!(kara.is_virtual).is(false)) + .order_by(id) .select(id) .load::<i64>(&mut self.sqlite)? }, @@ -265,6 +282,7 @@ impl LktDatabaseConnection { LktUri::Origin(uri_origin) => with_dsl! { kara => kara .filter(song_origin.is(uri_origin.as_str())) .filter(is_virtual.is(false)) + .order_by(id) .select(id) .load::<i64>(&mut self.sqlite)? }, @@ -272,14 +290,16 @@ impl LktDatabaseConnection { LktUri::Type(uri_type) => with_dsl!(kara => kara .filter(song_type.is(uri_type.as_str())) .filter(is_virtual.is(false)) + .order_by(id) .select(id) .load::<i64>(&mut self.sqlite)? ), LktUri::Language(uri_lang) => with_dsl!(kara_lang => kara_lang .filter(code.is(uri_lang.as_str())) - .inner_join(schema::kara::table) - .filter(schema::kara::is_virtual.is(false)) + .inner_join(table!(kara)) + .filter(field!(kara.is_virtual).is(false)) + .order_by(id) .select(id) .load::<i64>(&mut self.sqlite)? ), @@ -290,43 +310,63 @@ impl LktDatabaseConnection { with_dsl! { kara => { use schema::kara_lang::code as code; let fuzzy = fuzzy!( song_title source_name song_origin song_type code ); - kara.inner_join(schema::kara_lang::table) + kara.inner_join(table!(kara_lang)) .filter(fuzzy.like(uri_search)) + .order_by(id) .select(id) .load::<i64>(&mut self.sqlite)? }} } LktUri::Playlist(uri_playlist) => with_dsl!(playlist_kara => playlist_kara - .inner_join(schema::playlist::table) - .filter(schema::playlist::name.like(uri_playlist.as_str())) + .inner_join(table!(playlist)) + .filter(field!(playlist.name).like(uri_playlist.as_str())) + .order_by(kara_id) .select(kara_id) .load::<i64>(&mut self.sqlite)? ), LktUri::Any => with_dsl! { kara => kara .select(id) + .order_by(id) .load::<i64>(&mut self.sqlite)? }, }; - let karas = match search.search_type { + let mut kara_index = 0; + let mut kara_filter = |filter: i64| { + while let Some(&kara_id) = karas.get(kara_index) { + if kara_id != filter { + karas.remove(kara_index); + } else { + kara_index += 1; + } + } + }; + + match search.search_type { LktDatabaseSearchType::Playlist(plt_name) => { - let mut plt_ids: Vec<_> = schema::playlist::table - .filter(schema::playlist::name.is(plt_name.as_str())) - .inner_join(schema::playlist_kara::table) - .select(schema::playlist_kara::kara_id) - .load::<i64>(&mut self.sqlite)?; - plt_ids.retain(|plt_id| karas.contains(plt_id)); - plt_ids + for filter in table!(playlist) + .filter(field!(playlist.name).is(plt_name.as_str())) + .inner_join(table!(playlist_kara)) + .select(field!(playlist_kara.kara_id)) + .order_by(field!(playlist_kara.kara_id)) + .load_iter::<i64, DefaultLoadingMode>(&mut self.sqlite)? + { + kara_filter(filter?) + } } LktDatabaseSearchType::History => with_dsl!(history => { - let mut history_ids: Vec<_> = history.order(epoch.desc()).select(id).load::<i64>(&mut self.sqlite)?; - history_ids.retain(|history_id| karas.contains(history_id)); - history_ids + for filter in history + .order(epoch.desc()) + .select(id) + .load_iter::<i64, DefaultLoadingMode>(&mut self.sqlite)? + { + kara_filter(filter?) + } }), - LktDatabaseSearchType::Database => karas, - }; + LktDatabaseSearchType::Database => {} + } Ok(search .range @@ -346,22 +386,22 @@ impl LktDatabaseConnection { pub fn get_kara_tags(&mut self, local_id: i64) -> LktDatabaseResult<KaraTags> { let (repo, repo_id) = with_dsl!(repo_kara => repo_kara .filter(local_kara_id.is(local_id)) - .inner_join(schema::repo::table) - .select((schema::repo::name, repo_kara_id)) + .inner_join(table!(repo)) + .select((field!(repo.name), repo_kara_id)) .first::<(String, i64)>(&mut self.sqlite)? ); let kara_makers: Vec<String> = with_dsl!(kara => kara .filter(id.is(local_id)) - .inner_join(schema::kara_maker::table) - .select(schema::kara_maker::name) + .inner_join(table!(kara_maker)) + .select(field!(kara_maker.name)) .load::<String>(&mut self.sqlite)? ); let tags_raw: Vec<(String, Option<String>)> = with_dsl!(kara_tag => kara_tag .filter(kara_id.is(local_id)) - .inner_join(schema::tag::table) - .select((schema::tag::name, value)) + .inner_join(table!(tag)) + .select((field!(tag.name), value)) .load::<(String, Option<String>)>(&mut self.sqlite)? ); let mut tags: HashMap<String, Vec<String>> = Default::default();