Reloading the recommendation engine once scan is complete

This commit is contained in:
emeric
2020-02-15 16:22:28 +01:00
parent d1bdaf6203
commit cfb8bbe2ce
6 changed files with 55 additions and 30 deletions
+4 -4
View File
@@ -47,14 +47,14 @@
<div class="form-horizontal"> <div class="form-horizontal">
<div class="form-group"> <div class="form-group">
<label class="control-label col-sm-2" for="${id:similarity-engine-type}"> <label class="control-label col-sm-2" for="${id:recommendation-engine-type}">
${tr:Lms.Admin.Database.similarity-engine-type} ${tr:Lms.Admin.Database.recommendation-engine-type}
</label> </label>
<div class="col-sm-5"> <div class="col-sm-5">
${similarity-engine-type} ${recommendation-engine-type}
</div> </div>
<div class="help-block col-sm-5"> <div class="help-block col-sm-5">
${similarity-engine-type-info} ${recommendation-engine-type-info}
</div> </div>
</div> </div>
+26 -19
View File
@@ -36,11 +36,11 @@ createEngine(Database::Session& session)
Engine::Engine(Database::Session& session) Engine::Engine(Database::Session& session)
{ {
reloadSettings(session); reload(session);
} }
void void
Engine::reloadSettings(Database::Session& session) Engine::reload(Database::Session& session)
{ {
using namespace Database; using namespace Database;
@@ -50,56 +50,57 @@ Engine::reloadSettings(Database::Session& session)
return ScanSettings::get(session)->getRecommendationEngineType(); return ScanSettings::get(session)->getRecommendationEngineType();
}()}; }()};
// TODO: just replace the classifier once it is ready
clearClassifiers(); clearClassifiers();
switch (engineType) switch (engineType)
{ {
case ScanSettings::RecommendationEngineType::Features: case ScanSettings::RecommendationEngineType::Features:
// _classifiers.emplace(0, createFeaturesClassifier()); // higher priority // addClassifier(createFeaturesClassifier(), 0); // higher priority
// [[fallthrough]]; // [[fallthrough]];
case ScanSettings::RecommendationEngineType::Clusters: case ScanSettings::RecommendationEngineType::Clusters:
_classifiers.emplace(1, createClustersClassifier(session)); // lower priority addClassifier(createClustersClassifier(session), 1); // lower priority
break; break;
} }
} }
std::vector<Database::IdType> std::vector<Database::IdType>
Engine::getSimilarTracksFromTrackList(Database::Session& /*session*/, Database::IdType /*trackListId*/, std::size_t /*maxCount*/) Engine::getSimilarTracksFromTrackList(Database::Session& session, Database::IdType trackListId, std::size_t maxCount)
{ {
#if 0 const std::unordered_set<Database::IdType> trackIds {[&]() -> std::unordered_set<Database::IdType>
auto engineType {getEngineType(session)};
auto somSearcher {_somAddon.getSearcher()};
std::set<Database::IdType> trackIds;
{ {
auto transaction {session.createSharedTransaction()}; auto transaction {session.createSharedTransaction()};
Database::TrackList::pointer trackList {Database::TrackList::getById(session, trackListId)}; Database::TrackList::pointer trackList {Database::TrackList::getById(session, trackListId)};
if (trackList) if (trackList)
{ {
const std::vector<Database::IdType> orderedTrackIds {trackList->getTrackIds()}; const std::vector<Database::IdType> orderedTrackIds {trackList->getTrackIds()};
trackIds = std::set<Database::IdType> {std::cbegin(orderedTrackIds), std::cend(orderedTrackIds)}; return std::unordered_set<Database::IdType> {std::cbegin(orderedTrackIds), std::cend(orderedTrackIds)};
} }
}
return {};
}()};
if (trackIds.empty()) if (trackIds.empty())
return {}; return {};
if (engineType == Database::ScanSettings::SimilarityEngineType::Features std::shared_lock lock {_mutex};
&& somSearcher
&& std::any_of(std::cbegin(trackIds), std::cend(trackIds), [&](Database::IdType trackId) { return somSearcher->isTrackClassified(trackId); } )) for (const auto& [priority, classifier] : _classifiers)
{ {
return somSearcher->getSimilarTracks(trackIds, maxCount); if (std::any_of(std::cbegin(trackIds), std::cend(trackIds), [&](Database::IdType trackId) { return classifier->isTrackClassified(trackId); } ))
return classifier->getSimilarTracksFromTrackList(session, trackListId, maxCount);
} }
else
return ClusterEngine::getSimilarTracksFromTrackList(session, trackListId, maxCount);
#endif
return {}; return {};
} }
std::vector<Database::IdType> std::vector<Database::IdType>
Engine::getSimilarTracks(Database::Session& dbSession, const std::unordered_set<Database::IdType>& trackIds, std::size_t maxCount) Engine::getSimilarTracks(Database::Session& dbSession, const std::unordered_set<Database::IdType>& trackIds, std::size_t maxCount)
{ {
std::shared_lock lock {_mutex};
for (const auto& [priority, classifier] : _classifiers) for (const auto& [priority, classifier] : _classifiers)
{ {
if (std::any_of(std::cbegin(trackIds), std::cend(trackIds), [&](Database::IdType trackId) { return classifier->isTrackClassified(trackId); } )) if (std::any_of(std::cbegin(trackIds), std::cend(trackIds), [&](Database::IdType trackId) { return classifier->isTrackClassified(trackId); } ))
@@ -112,6 +113,8 @@ Engine::getSimilarTracks(Database::Session& dbSession, const std::unordered_set<
std::vector<Database::IdType> std::vector<Database::IdType>
Engine::getSimilarReleases(Database::Session& dbSession, Database::IdType releaseId, std::size_t maxCount) Engine::getSimilarReleases(Database::Session& dbSession, Database::IdType releaseId, std::size_t maxCount)
{ {
std::shared_lock lock {_mutex};
for (const auto& [priority, classifier] : _classifiers) for (const auto& [priority, classifier] : _classifiers)
{ {
if (classifier->isReleaseClassified(releaseId)) if (classifier->isReleaseClassified(releaseId))
@@ -124,6 +127,8 @@ Engine::getSimilarReleases(Database::Session& dbSession, Database::IdType releas
std::vector<Database::IdType> std::vector<Database::IdType>
Engine::getSimilarArtists(Database::Session& dbSession, Database::IdType artistId, std::size_t maxCount) Engine::getSimilarArtists(Database::Session& dbSession, Database::IdType artistId, std::size_t maxCount)
{ {
std::shared_lock lock {_mutex};
for (const auto& [priority, classifier] : _classifiers) for (const auto& [priority, classifier] : _classifiers)
{ {
if (classifier->isArtistClassified(artistId)) if (classifier->isArtistClassified(artistId))
@@ -136,12 +141,14 @@ Engine::getSimilarArtists(Database::Session& dbSession, Database::IdType artistI
void void
Engine::clearClassifiers() Engine::clearClassifiers()
{ {
std::unique_lock lock {_mutex};
_classifiers.clear(); _classifiers.clear();
} }
void void
Engine::addClassifier(std::unique_ptr<IClassifier> classifier, unsigned priority) Engine::addClassifier(std::unique_ptr<IClassifier> classifier, unsigned priority)
{ {
std::unique_lock lock {_mutex};
_classifiers.emplace(priority, std::move(classifier)); _classifiers.emplace(priority, std::move(classifier));
} }
+3 -1
View File
@@ -20,6 +20,7 @@
#pragma once #pragma once
#include <map> #include <map>
#include <shared_mutex>
#include "recommendation/IEngine.hpp" #include "recommendation/IEngine.hpp"
#include "recommendation/IClassifier.hpp" #include "recommendation/IClassifier.hpp"
@@ -38,7 +39,7 @@ namespace Recommendation
private: private:
void reloadSettings(Database::Session& session) override; void reload(Database::Session& session) override;
// Closest results first // Closest results first
std::vector<Database::IdType> getSimilarTracksFromTrackList(Database::Session& session, Database::IdType tracklistId, std::size_t maxCount) override; std::vector<Database::IdType> getSimilarTracksFromTrackList(Database::Session& session, Database::IdType tracklistId, std::size_t maxCount) override;
@@ -49,6 +50,7 @@ namespace Recommendation
void clearClassifiers(); void clearClassifiers();
void addClassifier(std::unique_ptr<IClassifier> classifier, unsigned priority); void addClassifier(std::unique_ptr<IClassifier> classifier, unsigned priority);
std::shared_mutex _mutex;
std::map<unsigned, std::unique_ptr<IClassifier>> _classifiers; std::map<unsigned, std::unique_ptr<IClassifier>> _classifiers;
}; };
@@ -36,7 +36,7 @@ namespace Recommendation
public: public:
virtual ~IEngine() = default; virtual ~IEngine() = default;
virtual void reloadSettings(Database::Session& session) = 0; virtual void reload(Database::Session& session) = 0;
// Closest results first // Closest results first
virtual std::vector<Database::IdType> getSimilarTracksFromTrackList(Database::Session& session, Database::IdType tracklistId, std::size_t maxCount) = 0; virtual std::vector<Database::IdType> getSimilarTracksFromTrackList(Database::Session& session, Database::IdType tracklistId, std::size_t maxCount) = 0;
+4 -4
View File
@@ -427,6 +427,10 @@ MediaScanner::scan(boost::system::error_code err)
addon->preScanComplete(); addon->preScanComplete();
} }
LMS_LOG(DBUPDATER, INFO) << "Optimizing db...";
_dbSession.optimize();
LMS_LOG(DBUPDATER, INFO) << "Optimize db done!";
if (_running) if (_running)
{ {
stats.stopTime = Wt::WLocalDateTime::currentDateTime().toUTC(); stats.stopTime = Wt::WLocalDateTime::currentDateTime().toUTC();
@@ -448,10 +452,6 @@ MediaScanner::scan(boost::system::error_code err)
_curState = State::NotScheduled; _curState = State::NotScheduled;
_inProgressScanStats.reset(); _inProgressScanStats.reset();
} }
LMS_LOG(DBUPDATER, INFO) << "Optimizing db...";
_dbSession.optimize();
LMS_LOG(DBUPDATER, INFO) << "Optimize db done!";
} }
void void
+17 -1
View File
@@ -145,7 +145,23 @@ int main(int argc, char* argv[])
{ {
Database::Session session {database}; Database::Session session {database};
ServiceProvider<Recommendation::IEngine>::assign(Recommendation::createEngine(session)); Recommendation::IEngine& recommendationEngine {ServiceProvider<Recommendation::IEngine>::assign(Recommendation::createEngine(session))};
mediaScanner.scanComplete().connect([&]()
{
auto status = mediaScanner.getStatus();
if (status.lastCompleteScanStats->nbChanges() > 0)
{
LMS_LOG(MAIN, INFO) << "Scanner changed some files, reloading the recommendation engine...";
Database::Session session {database};
recommendationEngine.reload(session);
}
else
{
LMS_LOG(MAIN, INFO) << "Scanner did not change files, not reloading the recommendation engine...";
}
});
} }
CoverArt::IGrabber& coverArtGrabber {ServiceProvider<CoverArt::IGrabber>::assign(CoverArt::createGrabber(argv[0]))}; CoverArt::IGrabber& coverArtGrabber {ServiceProvider<CoverArt::IGrabber>::assign(CoverArt::createGrabber(argv[0]))};