Commit 38a2b060 authored by Hugo Beauzée-Luyssen's avatar Hugo Beauzée-Luyssen

Use a common search method for all media

parent 18602f8b
......@@ -115,7 +115,7 @@ class IMediaLibrary
/**
* Search
*/
virtual std::vector<MediaPtr> searchAlbumTracks( const std::string& title ) const = 0;
virtual std::vector<MediaPtr> searchMedia( const std::string& pattern ) const = 0;
virtual std::vector<PlaylistPtr> searchPlaylists( const std::string& name ) const = 0;
virtual std::vector<AlbumPtr> searchAlbums( const std::string& pattern ) const = 0;
......
......@@ -112,25 +112,9 @@ bool AlbumTrack::createTable( DBConnection dbConnection )
" BEGIN"
" UPDATE " + policy::AlbumTrackTable::Name + " SET is_present = new.is_present WHERE media_id = new.id_media;"
" END";
static const std::string vtableReq = "CREATE VIRTUAL TABLE IF NOT EXISTS "
+ policy::AlbumTrackTable::Name + "Fts USING FTS3("
"title"
")";
static const std::string vtableTrigger = "CREATE TRIGGER IF NOT EXISTS delete_fts_track"
" AFTER DELETE ON " + policy::MediaTable::Name +
" BEGIN"
" DELETE FROM " + policy::AlbumTrackTable::Name + "Fts WHERE rowid = old.id_media;"
" END";
static const std::string vtableTrigger2 = "CREATE TRIGGER IF NOT EXISTS update_fts_track"
" AFTER UPDATE OF title ON " + policy::MediaTable::Name +
" BEGIN"
" UPDATE " + policy::AlbumTrackTable::Name + "Fts SET title = new.title WHERE rowid = new.id_media;"
" END";
return sqlite::Tools::executeRequest( dbConnection, req ) &&
sqlite::Tools::executeRequest( dbConnection, triggerReq ) &&
sqlite::Tools::executeRequest( dbConnection, vtableReq ) &&
sqlite::Tools::executeRequest( dbConnection, vtableTrigger ) &&
sqlite::Tools::executeRequest( dbConnection, vtableTrigger2 );
sqlite::Tools::executeRequest( dbConnection, triggerReq );
}
std::shared_ptr<AlbumTrack> AlbumTrack::create( DBConnection dbConnection, unsigned int albumId, const Media& media, unsigned int trackNb, unsigned int discNumber )
......@@ -141,9 +125,6 @@ std::shared_ptr<AlbumTrack> AlbumTrack::create( DBConnection dbConnection, unsig
if ( insert( dbConnection, self, req, media.id(), trackNb, albumId, discNumber ) == false )
return nullptr;
self->m_dbConnection = dbConnection;
static const std::string ftsReq = "INSERT INTO " + policy::AlbumTrackTable::Name + "Fts"
"(rowid, title) VALUES(?, ?)";
sqlite::Tools::insert( dbConnection, ftsReq, self->m_id, media.title() );
return self;
}
......@@ -161,14 +142,6 @@ std::vector<AlbumTrackPtr> AlbumTrack::fromGenre( DBConnection dbConn, unsigned
return fetchAll<IAlbumTrack>( dbConn, req, genreId );
}
std::vector<MediaPtr> AlbumTrack::search( DBConnection dbConn, const std::string& title )
{
static const std::string req = "SELECT * FROM " + policy::MediaTable::Name + " WHERE"
" id_media IN (SELECT rowid FROM " + policy::AlbumTrackTable::Name + "Fts"
" WHERE title MATCH ?)";
return Media::fetchAll<IMedia>( dbConn, req, title + "*" );
}
GenrePtr AlbumTrack::genre()
{
auto l = m_genre.lock();
......
......@@ -346,7 +346,12 @@ bool Media::createTable( DBConnection connection )
"title TEXT,"
"is_present BOOLEAN NOT NULL DEFAULT 1"
")";
return sqlite::Tools::executeRequest( connection, req );
static const std::string vtableReq = "CREATE VIRTUAL TABLE IF NOT EXISTS "
+ policy::MediaTable::Name + "Fts USING FTS3("
"title"
")";
return sqlite::Tools::executeRequest( connection, req ) &&
sqlite::Tools::executeRequest( connection, vtableReq );
}
bool Media::createTriggers( DBConnection connection )
......@@ -365,8 +370,27 @@ bool Media::createTriggers( DBConnection connection )
"(SELECT COUNT(id_file) FROM " + policy::FileTable::Name + " WHERE media_id=old.media_id) = 0"
" AND id_media=old.media_id;"
" END;";
static const std::string vtableInsertTrigger = "CREATE TRIGGER IF NOT EXISTS insert_media_fts"
" AFTER INSERT ON " + policy::MediaTable::Name +
" BEGIN"
" INSERT INTO " + policy::MediaTable::Name + "Fts(rowid,title) VALUES(new.id_media, new.title);"
" END";
static const std::string vtableDeleteTrigger = "CREATE TRIGGER IF NOT EXISTS delete_media_fts"
" AFTER DELETE ON " + policy::MediaTable::Name +
" BEGIN"
" DELETE FROM " + policy::MediaTable::Name + "Fts WHERE rowid = old.id_media;"
" END";
static const std::string vtableUpdateTitleTrigger2 = "CREATE TRIGGER IF NOT EXISTS update_media_title_fts"
" AFTER UPDATE OF title ON " + policy::MediaTable::Name +
" BEGIN"
" UPDATE " + policy::MediaTable::Name + "Fts SET title = new.title WHERE rowid = new.id_media;"
" END";
return sqlite::Tools::executeRequest( connection, triggerReq ) &&
sqlite::Tools::executeRequest( connection, triggerReq2 );
sqlite::Tools::executeRequest( connection, triggerReq2 ) &&
sqlite::Tools::executeRequest( connection, vtableInsertTrigger ) &&
sqlite::Tools::executeRequest( connection, vtableDeleteTrigger ) &&
sqlite::Tools::executeRequest( connection, vtableUpdateTitleTrigger2 );
}
bool Media::addLabel( LabelPtr label )
......@@ -390,3 +414,12 @@ bool Media::removeLabel( LabelPtr label )
const char* req = "DELETE FROM LabelFileRelation WHERE label_id = ? AND media_id = ?";
return sqlite::Tools::executeDelete( m_dbConnection, req, label->id(), m_id );
}
std::vector<MediaPtr> Media::search( DBConnection dbConn, const std::string& title )
{
static const std::string req = "SELECT * FROM " + policy::MediaTable::Name + " WHERE"
" id_media IN (SELECT rowid FROM " + policy::MediaTable::Name + "Fts"
" WHERE " + policy::MediaTable::Name + "Fts MATCH ?)";
return Media::fetchAll<IMedia>( dbConn, req, title + "*" );
}
......@@ -107,7 +107,10 @@ class Media : public IMedia, public DatabaseHelpers<Media, policy::MediaTable>
std::shared_ptr<File> addFile(const fs::IFile& fileFs, Folder& parentFolder, fs::IDirectory& parentFolderFs , IFile::Type type);
void removeFile( File& file );
private:
static std::vector<MediaPtr> search( DBConnection dbConn, const std::string& title );
private:
DBConnection m_dbConnection;
// DB fields:
......
......@@ -410,11 +410,11 @@ std::vector<HistoryPtr> MediaLibrary::history() const
return History::fetch( m_dbConnection.get() );
}
std::vector<MediaPtr> MediaLibrary::searchAlbumTracks( const std::string& title ) const
std::vector<MediaPtr> MediaLibrary::searchMedia( const std::string& title ) const
{
if ( validateSearchPattern( title ) == false )
return {};
return AlbumTrack::search( m_dbConnection.get(), title );
return Media::search( m_dbConnection.get(), title );
}
std::vector<PlaylistPtr> MediaLibrary::searchPlaylists( const std::string& name ) const
......
......@@ -90,7 +90,7 @@ class MediaLibrary : public IMediaLibrary
virtual bool addToHistory( const std::string& mrl );
virtual std::vector<HistoryPtr> history() const;
virtual std::vector<MediaPtr> searchAlbumTracks( const std::string& title ) const override;
virtual std::vector<MediaPtr> searchMedia( const std::string& title ) const override;
virtual std::vector<PlaylistPtr> searchPlaylists( const std::string& name ) const override;
virtual std::vector<AlbumPtr> searchAlbums( const std::string& pattern ) const override;
......
......@@ -112,59 +112,3 @@ TEST_F( AlbumTracks, SetGenre )
auto t2 = tracks[0];
ASSERT_EQ( t->genre()->id(), t2->albumTrack()->genre()->id() );
}
TEST_F( AlbumTracks, Search )
{
auto a = ml->createAlbum( "album" );
for ( auto i = 1u; i <= 10u; ++i )
{
auto m = ml->addFile( "track " + std::to_string( i ) + ".mp3" );
a->addTrack( m, i, 1 );
}
auto tracks = ml->searchAlbumTracks( "tra" );
ASSERT_EQ( 10u, tracks.size() );
tracks = ml->searchAlbumTracks( "track 1" );
ASSERT_EQ( 2u, tracks.size() );
tracks = ml->searchAlbumTracks( "grouik" );
ASSERT_EQ( 0u, tracks.size() );
tracks = ml->searchAlbumTracks( "rack" );
ASSERT_EQ( 0u, tracks.size() );
}
TEST_F( AlbumTracks, SearchAfterEdit )
{
auto m = ml->addFile( "media.mp3" );
auto a = ml->createAlbum( "album" );
a->addTrack( m, 1, 1 );
auto tracks = ml->searchAlbumTracks( "media" );
ASSERT_EQ( 1u, tracks.size() );
m->setTitle( "otters are awesome" );
m->save();
tracks = ml->searchAlbumTracks( "media" );
ASSERT_EQ( 0u, tracks.size() );
tracks = ml->searchAlbumTracks( "otters" );
ASSERT_EQ( 1u, tracks.size() );
}
TEST_F( AlbumTracks, SearchAfterDelete )
{
auto m = ml->addFile( "media.mp3" );
auto a = ml->createAlbum( "album" );
a->addTrack( m, 1, 1 );
auto tracks = ml->searchAlbumTracks( "media" );
ASSERT_EQ( 1u, tracks.size() );
auto f = m->files()[0];
m->removeFile( static_cast<File&>( *f ) );
tracks = ml->searchAlbumTracks( "media" );
ASSERT_EQ( 0u, tracks.size() );
}
......@@ -144,3 +144,53 @@ TEST_F( Medias, Rating )
f = ml->media( f->id() );
ASSERT_EQ( 12345, f->rating() );
}
TEST_F( Medias, Search )
{
for ( auto i = 1u; i <= 10u; ++i )
{
auto m = ml->addFile( "track " + std::to_string( i ) + ".mp3" );
}
auto media = ml->searchMedia( "tra" );
ASSERT_EQ( 10u, media.size() );
media = ml->searchMedia( "track 1" );
ASSERT_EQ( 2u, media.size() );
media = ml->searchMedia( "grouik" );
ASSERT_EQ( 0u, media.size() );
media = ml->searchMedia( "rack" );
ASSERT_EQ( 0u, media.size() );
}
TEST_F( Medias, SearchAfterEdit )
{
auto m = ml->addFile( "media.mp3" );
auto media = ml->searchMedia( "media" );
ASSERT_EQ( 1u, media.size() );
m->setTitle( "otters are awesome" );
m->save();
media = ml->searchMedia( "media" );
ASSERT_EQ( 0u, media.size() );
media = ml->searchMedia( "otters" );
ASSERT_EQ( 1u, media.size() );
}
TEST_F( Medias, SearchAfterDelete )
{
auto m = ml->addFile( "media.mp3" );
auto media = ml->searchMedia( "media" );
ASSERT_EQ( 1u, media.size() );
auto f = m->files()[0];
m->removeFile( static_cast<File&>( *f ) );
media = ml->searchMedia( "media" );
ASSERT_EQ( 0u, media.size() );
}
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment