Improved the tag based similarity results
This commit is contained in:
+30
-1
@@ -140,8 +140,10 @@ getQuery(Session& session,
|
||||
}
|
||||
|
||||
std::vector<Artist::pointer>
|
||||
Artist::getByFilter(Session& session, const std::set<IdType>& clusters)
|
||||
Artist::getByClusters(Session& session, const std::set<IdType>& clusters)
|
||||
{
|
||||
assert(!clusters.empty());
|
||||
|
||||
session.checkSharedLocked();
|
||||
bool more;
|
||||
return getByFilter(session, clusters, {}, {}, {}, more);
|
||||
@@ -296,6 +298,33 @@ Artist::getRandomTracks(std::optional<std::size_t> count) const
|
||||
return std::vector<Wt::Dbo::ptr<Track>>(tracks.begin(), tracks.end());
|
||||
}
|
||||
|
||||
std::vector<Wt::Dbo::ptr<Artist>>
|
||||
Artist::getSimilarArtists(std::optional<std::size_t> offset, std::optional<std::size_t> count) const
|
||||
{
|
||||
assert(self());
|
||||
assert(IdIsValid(self()->id()));
|
||||
assert(session());
|
||||
|
||||
Wt::Dbo::Query<pointer> query {session()->query<pointer>(
|
||||
"SELECT a FROM artist a"
|
||||
" INNER JOIN track_artist_link t_a_l ON t_a_l.artist_id = a.id"
|
||||
" INNER JOIN track t ON t.id = t_a_l.track_id"
|
||||
" INNER JOIN track_cluster t_c ON t_c.track_id = t.id"
|
||||
" WHERE "
|
||||
" t_c.cluster_id IN (SELECT c.id from cluster c INNER JOIN track t ON c.id = t_c.cluster_id INNER JOIN track_cluster t_c ON t_c.track_id = t.id INNER JOIN artist a ON a.id = t_a_l.artist_id INNER JOIN track_artist_link t_a_l ON t_a_l.track_id = t.id WHERE a.id = ?)"
|
||||
" AND a.id <> ?"
|
||||
)
|
||||
.bind(self()->id())
|
||||
.bind(self()->id())
|
||||
.groupBy("a.id")
|
||||
.orderBy("COUNT(*) DESC")
|
||||
.limit(count ? static_cast<int>(*count) : -1)
|
||||
.offset(offset ? static_cast<int>(*offset) : -1)};
|
||||
|
||||
Wt::Dbo::collection<pointer> res = query;
|
||||
return std::vector<pointer>(res.begin(), res.end());
|
||||
}
|
||||
|
||||
std::vector<std::vector<Wt::Dbo::ptr<Cluster>>>
|
||||
Artist::getClusterGroups(std::vector<ClusterType::pointer> clusterTypes, std::size_t size) const
|
||||
{
|
||||
|
||||
@@ -52,11 +52,11 @@ class Artist : public Wt::Dbo::Dbo<Artist>
|
||||
static pointer getByMBID(Session& session, const std::string& MBID);
|
||||
static pointer getById(Session& session, IdType id);
|
||||
static std::vector<pointer> getByName(Session& session, const std::string& name);
|
||||
static std::vector<pointer> getByFilter(Session& session,
|
||||
static std::vector<pointer> getByClusters(Session& session,
|
||||
const std::set<IdType>& clusters); // at least one track that belongs to these clusters
|
||||
static std::vector<pointer> getByFilter(Session& session,
|
||||
const std::set<IdType>& clusters, // at least one track that belongs to these clusters
|
||||
const std::vector<std::string>& keywords, // name must match all of these keywords
|
||||
const std::set<IdType>& clusters, // if non empty, at least one artist that belongs to these clusters
|
||||
const std::vector<std::string>& keywords, // if non empty, name must match all of these keywords
|
||||
std::optional<std::size_t> offset,
|
||||
std::optional<std::size_t> size,
|
||||
bool& moreExpected);
|
||||
@@ -69,11 +69,12 @@ class Artist : public Wt::Dbo::Dbo<Artist>
|
||||
const std::string& getName(void) const { return _name; }
|
||||
const std::string& getMBID(void) const { return _MBID; }
|
||||
|
||||
std::vector<Wt::Dbo::ptr<Release>> getReleases(const std::set<IdType>& clusterIds = std::set<IdType>()) const;
|
||||
std::vector<Wt::Dbo::ptr<Release>> getReleases(const std::set<IdType>& clusterIds = {}) const; // if non empty, get the releases that match all these clusters
|
||||
std::size_t getReleaseCount() const;
|
||||
std::vector<Wt::Dbo::ptr<Track>> getTracks(std::optional<TrackArtistLink::Type> linkType = {}) const;
|
||||
std::vector<Wt::Dbo::ptr<Track>> getTracksWithRelease(std::optional<TrackArtistLink::Type> linkType = {}) const;
|
||||
std::vector<Wt::Dbo::ptr<Track>> getRandomTracks(std::optional<std::size_t> count) const;
|
||||
std::vector<pointer> getSimilarArtists(std::optional<std::size_t> offset = {}, std::optional<std::size_t> count = {}) const;
|
||||
|
||||
// Get the cluster of the tracks made by this artist
|
||||
// Each clusters are grouped by cluster type, sorted by the number of occurence
|
||||
|
||||
@@ -63,8 +63,7 @@ std::vector<Cluster::pointer>
|
||||
Cluster::getAllOrphans(Session& session)
|
||||
{
|
||||
session.checkSharedLocked();
|
||||
|
||||
Wt::Dbo::collection<Cluster::pointer> res {session.getDboSession().query<Cluster::pointer>("SELECT DISTINCT c FROM cluster c WHERE NOT EXISTS(SELECT 1 FROM track t INNER JOIN track_cluster t_c ON t.id = t_c.track_id)")};
|
||||
Wt::Dbo::collection<Cluster::pointer> res {session.getDboSession().query<Cluster::pointer>("SELECT DISTINCT c FROM cluster c WHERE NOT EXISTS(SELECT 1 FROM track_cluster t_c WHERE t_c.cluster_id = c.id)")};
|
||||
|
||||
return std::vector<Cluster::pointer>(res.begin(), res.end());
|
||||
}
|
||||
@@ -84,15 +83,16 @@ Cluster::addTrack(Wt::Dbo::ptr<Track> track)
|
||||
}
|
||||
|
||||
std::vector<Wt::Dbo::ptr<Track>>
|
||||
Cluster::getTracks(int offset, int limit) const
|
||||
Cluster::getTracks(std::optional<std::size_t> offset, std::optional<std::size_t> limit) const
|
||||
{
|
||||
assert(session());
|
||||
assert(IdIsValid(self()->id()));
|
||||
|
||||
Wt::Dbo::collection<Track::pointer> res = session()->query<Track::pointer>("SELECT t FROM track t INNER JOIN cluster c ON c.id = t_c.cluster_id INNER JOIN track_cluster t_c ON t_c.track_id = t.id")
|
||||
.where("c.id = ?").bind(self()->id())
|
||||
.offset(offset)
|
||||
.limit(limit);
|
||||
Wt::Dbo::collection<Track::pointer> res
|
||||
{session()->query<Track::pointer>("SELECT t FROM track t INNER JOIN cluster c ON c.id = t_c.cluster_id INNER JOIN track_cluster t_c ON t_c.track_id = t.id")
|
||||
.where("c.id = ?").bind(self()->id())
|
||||
.offset(offset ? static_cast<int>(*offset) : -1)
|
||||
.limit(limit ? static_cast<int>(*limit) : -1)};
|
||||
|
||||
return std::vector<Wt::Dbo::ptr<Track>>(res.begin(), res.end());
|
||||
}
|
||||
|
||||
@@ -55,7 +55,7 @@ class Cluster : public Wt::Dbo::Dbo<Cluster>
|
||||
const std::string& getName() const { return _name; }
|
||||
Wt::Dbo::ptr<ClusterType> getType() const { return _clusterType; }
|
||||
std::size_t getTracksCount() const { return _tracks.size(); }
|
||||
std::vector<Wt::Dbo::ptr<Track>> getTracks(int offset, int limit) const;
|
||||
std::vector<Wt::Dbo::ptr<Track>> getTracks(std::optional<std::size_t> offset = {}, std::optional<std::size_t> limit = {}) const;
|
||||
std::set<IdType> getTrackIds() const;
|
||||
std::size_t getReleasesCount() const;
|
||||
|
||||
|
||||
@@ -206,10 +206,14 @@ getQuery(Session& session,
|
||||
}
|
||||
|
||||
std::vector<Release::pointer>
|
||||
Release::getByFilter(Session& session, const std::set<IdType>& clusterIds)
|
||||
Release::getByClusters(Session& session, const std::set<IdType>& clusters)
|
||||
{
|
||||
assert(!clusters.empty());
|
||||
|
||||
session.checkSharedLocked();
|
||||
|
||||
bool moreResults;
|
||||
return getByFilter(session, clusterIds, {}, {}, {}, moreResults);
|
||||
return getByFilter(session, clusters, {}, {}, {}, moreResults);
|
||||
}
|
||||
|
||||
std::vector<Release::pointer>
|
||||
@@ -220,6 +224,8 @@ Release::getByFilter(Session& session,
|
||||
std::optional<std::size_t> size,
|
||||
bool& moreResults)
|
||||
{
|
||||
session.checkSharedLocked();
|
||||
|
||||
Wt::Dbo::collection<pointer> collection = getQuery(session, clusterIds, keywords)
|
||||
.limit(size ? static_cast<int>(*size) + 1 : -1)
|
||||
.offset(offset ? static_cast<int>(*offset) : -1);
|
||||
@@ -332,6 +338,32 @@ Release::getArtists(TrackArtistLink::Type linkType) const
|
||||
return std::vector<Wt::Dbo::ptr<Artist>>(res.begin(), res.end());
|
||||
}
|
||||
|
||||
std::vector<Release::pointer>
|
||||
Release::getSimilarReleases(std::optional<std::size_t> offset, std::optional<std::size_t> count) const
|
||||
{
|
||||
assert(self());
|
||||
assert(IdIsValid(self()->id()));
|
||||
assert(session());
|
||||
|
||||
Wt::Dbo::Query<pointer> query {session()->query<pointer>(
|
||||
"SELECT r FROM release r"
|
||||
" INNER JOIN track t ON t.release_id = r.id"
|
||||
" INNER JOIN track_cluster t_c ON t_c.track_id = t.id"
|
||||
" WHERE "
|
||||
" t_c.cluster_id IN (SELECT c.id from cluster c INNER JOIN track t ON c.id = t_c.cluster_id INNER JOIN track_cluster t_c ON t_c.track_id = t.id INNER JOIN release r ON r.id = t.release_id WHERE r.id = ?)"
|
||||
" AND r.id <> ?"
|
||||
)
|
||||
.bind(self()->id())
|
||||
.bind(self()->id())
|
||||
.groupBy("r.id")
|
||||
.orderBy("COUNT(*) DESC")
|
||||
.limit(count ? static_cast<int>(*count) : -1)
|
||||
.offset(offset ? static_cast<int>(*offset) : -1)};
|
||||
|
||||
Wt::Dbo::collection<pointer> res = query;
|
||||
return std::vector<pointer>(res.begin(), res.end());
|
||||
}
|
||||
|
||||
bool
|
||||
Release::hasVariousArtists() const
|
||||
{
|
||||
|
||||
@@ -57,10 +57,10 @@ class Release : public Wt::Dbo::Dbo<Release>
|
||||
static std::vector<pointer> getLastAdded(Session& session, const Wt::WDateTime& after, std::optional<std::size_t> offset = {}, std::optional<std::size_t> size = {});
|
||||
static std::vector<pointer> getByYear(Session& session, int yearFrom, int yearTo, std::optional<std::size_t> offset = {}, std::optional<std::size_t> size = {});
|
||||
|
||||
static std::vector<pointer> getByFilter(Session& session, const std::set<IdType>& clusters);
|
||||
static std::vector<pointer> getByClusters(Session& session, const std::set<IdType>& clusters);
|
||||
static std::vector<pointer> getByFilter(Session& session,
|
||||
const std::set<IdType>& clusters, // at least one track that belongs to these clusters
|
||||
const std::vector<std::string>& keywords, // name must match all of these keywords
|
||||
const std::set<IdType>& clusters, // if non empty, at least one release that belongs to these clusters
|
||||
const std::vector<std::string>& keywords, // if non empty, name must match all of these keywords
|
||||
std::optional<std::size_t> offset,
|
||||
std::optional<std::size_t> size,
|
||||
bool& moreExpected);
|
||||
@@ -96,6 +96,7 @@ class Release : public Wt::Dbo::Dbo<Release>
|
||||
std::vector<Wt::Dbo::ptr<Artist> > getArtists(TrackArtistLink::Type type = TrackArtistLink::Type::Artist) const;
|
||||
std::vector<Wt::Dbo::ptr<Artist> > getReleaseArtists() const { return getArtists(TrackArtistLink::Type::ReleaseArtist); }
|
||||
bool hasVariousArtists() const;
|
||||
std::vector<pointer> getSimilarReleases(std::optional<std::size_t> offset = {}, std::optional<std::size_t> count = {}) const;
|
||||
|
||||
void setMBID(std::string mbid) { _MBID = mbid; }
|
||||
|
||||
|
||||
+39
-1
@@ -248,9 +248,47 @@ Track::getByFilter(Session& session,
|
||||
}
|
||||
|
||||
std::vector<Track::pointer>
|
||||
Track::getByFilter(Session& session,
|
||||
Track::getSimilarTracks(Session& session,
|
||||
const std::set<IdType>& tracks,
|
||||
std::optional<std::size_t> offset,
|
||||
std::optional<std::size_t> size)
|
||||
{
|
||||
assert(!tracks.empty());
|
||||
session.checkSharedLocked();
|
||||
|
||||
std::ostringstream oss;
|
||||
for (std::size_t i {}; i < tracks.size(); ++i)
|
||||
{
|
||||
if (!oss.str().empty())
|
||||
oss << ", ";
|
||||
oss << "?";
|
||||
}
|
||||
|
||||
Wt::Dbo::Query<pointer> query {session.getDboSession().query<pointer>(
|
||||
"SELECT t FROM track t"
|
||||
" INNER JOIN track_cluster t_c ON t_c.track_id = t.id"
|
||||
" AND t_c.cluster_id IN (SELECT c.id FROM cluster c INNER JOIN track_cluster t_c ON t_c.cluster_id = c.id WHERE t_c.track_id IN (" + oss.str() + "))"
|
||||
" AND t.id NOT IN (" + oss.str() + ")")
|
||||
.groupBy("t.id")
|
||||
.orderBy("COUNT(*) DESC")
|
||||
.limit(size ? static_cast<int>(*size) : -1)
|
||||
.offset(offset ? static_cast<int>(*offset) : -1)};
|
||||
|
||||
for (IdType trackId : tracks)
|
||||
query.bind(trackId );
|
||||
|
||||
for (IdType trackId : tracks)
|
||||
query.bind(trackId );
|
||||
|
||||
Wt::Dbo::collection<pointer> res = query;
|
||||
return std::vector<pointer>(res.begin(), res.end());
|
||||
}
|
||||
|
||||
std::vector<Track::pointer>
|
||||
Track::getByClusters(Session& session,
|
||||
const std::set<IdType>& clusters)
|
||||
{
|
||||
assert(!clusters.empty());
|
||||
session.checkSharedLocked();
|
||||
|
||||
bool moreResults;
|
||||
|
||||
@@ -55,11 +55,15 @@ class Track : public Wt::Dbo::Dbo<Track>
|
||||
static pointer getByPath(Session& session, const std::filesystem::path& p);
|
||||
static pointer getById(Session& session, IdType id);
|
||||
static pointer getByMBID(Session& session, const std::string& MBID);
|
||||
static std::vector<pointer> getByFilter(Session& session,
|
||||
static std::vector<pointer> getSimilarTracks(Session& session,
|
||||
const std::set<IdType>& trackIds,
|
||||
std::optional<std::size_t> offset = {},
|
||||
std::optional<std::size_t> size = {});
|
||||
static std::vector<pointer> getByClusters(Session& session,
|
||||
const std::set<IdType>& clusters); // tracks that belong to these clusters
|
||||
static std::vector<pointer> getByFilter(Session& session,
|
||||
const std::set<IdType>& clusters, // tracks that belong to these clusters
|
||||
const std::vector<std::string>& keywords, // name must match all of these keywords
|
||||
const std::set<IdType>& clusters, // if non empty, tracks that belong to these clusters
|
||||
const std::vector<std::string>& keywords, // if non empty, name must match all of these keywords
|
||||
std::optional<std::size_t> offset,
|
||||
std::optional<std::size_t> size,
|
||||
bool& moreExpected);
|
||||
|
||||
@@ -65,6 +65,15 @@ TrackList::get(Session& session, const std::string& name, Type type, Wt::Dbo::pt
|
||||
.where("user_id = ?").bind(user.id());
|
||||
}
|
||||
|
||||
std::vector<TrackList::pointer>
|
||||
TrackList::getAll(Session& session)
|
||||
{
|
||||
session.checkSharedLocked();
|
||||
Wt::Dbo::collection<TrackList::pointer> res = session.getDboSession().find<TrackList>();
|
||||
|
||||
return std::vector<TrackList::pointer>(res.begin(), res.end());
|
||||
}
|
||||
|
||||
std::vector<TrackList::pointer>
|
||||
TrackList::getAll(Session& session, Wt::Dbo::ptr<User> user)
|
||||
{
|
||||
@@ -176,6 +185,30 @@ TrackList::hasTrack(IdType trackId) const
|
||||
return res.size() > 0;
|
||||
}
|
||||
|
||||
std::vector<Track::pointer>
|
||||
TrackList::getSimilarTracks(std::optional<std::size_t> offset, std::optional<std::size_t> size) const
|
||||
{
|
||||
assert(session());
|
||||
assert(IdIsValid(self()->id()));
|
||||
|
||||
Wt::Dbo::Query<Track::pointer> query {session()->query<Track::pointer>(
|
||||
"SELECT t FROM track t"
|
||||
" INNER JOIN track_cluster t_c ON t_c.track_id = t.id"
|
||||
" WHERE "
|
||||
" (t_c.cluster_id IN (SELECT c.id from cluster c INNER JOIN track t ON c.id = t_c.cluster_id INNER JOIN track_cluster t_c ON t_c.track_id = t.id INNER JOIN tracklist_entry p_e ON p_e.track_id = t.id INNER JOIN tracklist p ON p.id = p_e.tracklist_id WHERE p.id = ?)"
|
||||
" AND t.id NOT IN (SELECT tracklist_t.id FROM track tracklist_t INNER JOIN tracklist_entry t_e ON t_e.track_id = tracklist_t.id WHERE t_e.tracklist_id = ?))"
|
||||
)
|
||||
.bind(self()->id())
|
||||
.bind(self()->id())
|
||||
.groupBy("t.id")
|
||||
.orderBy("COUNT(*) DESC")
|
||||
.limit(size ? static_cast<int>(*size) : -1)
|
||||
.offset(offset ? static_cast<int>(*offset) : -1)};
|
||||
|
||||
Wt::Dbo::collection<Track::pointer> tracks = query;
|
||||
return std::vector<Track::pointer>(tracks.begin(), tracks.end());
|
||||
}
|
||||
|
||||
std::vector<IdType>
|
||||
TrackList::getTrackIds() const
|
||||
{
|
||||
|
||||
@@ -58,6 +58,7 @@ class TrackList : public Wt::Dbo::Dbo<TrackList>
|
||||
// Search utility
|
||||
static pointer get(Session& session, const std::string& name, Type type, Wt::Dbo::ptr<User> user);
|
||||
static pointer getById(Session& session, IdType tracklistId);
|
||||
static std::vector<pointer> getAll(Session& session);
|
||||
static std::vector<pointer> getAll(Session& session, Wt::Dbo::ptr<User> user);
|
||||
static std::vector<pointer> getAll(Session& session, Wt::Dbo::ptr<User> user, Type type);
|
||||
|
||||
@@ -90,6 +91,9 @@ class TrackList : public Wt::Dbo::Dbo<TrackList>
|
||||
|
||||
bool hasTrack(IdType trackId) const;
|
||||
|
||||
// Ordered from most clusters in common
|
||||
std::vector<Wt::Dbo::ptr<Track>> getSimilarTracks(std::optional<std::size_t> offset = {}, std::optional<std::size_t> size = {}) const;
|
||||
|
||||
template<class Action>
|
||||
void persist(Action& a)
|
||||
{
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
#include "cluster/SimilarityClusterSearcher.hpp"
|
||||
|
||||
#include "database/SimilaritySettings.hpp"
|
||||
#include "database/TrackList.hpp"
|
||||
|
||||
namespace Similarity {
|
||||
|
||||
@@ -37,6 +38,36 @@ Database::SimilaritySettings::EngineType getEngineType(Database::Session& dbSess
|
||||
return Database::SimilaritySettings::get(dbSession)->getEngineType();
|
||||
}
|
||||
|
||||
std::vector<Database::IdType>
|
||||
Searcher::getSimilarTracksFromTrackList(Database::Session& session, Database::IdType trackListId, std::size_t maxCount)
|
||||
{
|
||||
auto engineType {getEngineType(session)};
|
||||
auto somSearcher {_somAddon.getSearcher()};
|
||||
|
||||
std::set<Database::IdType> trackIds;
|
||||
{
|
||||
auto transaction {session.createSharedTransaction()};
|
||||
Database::TrackList::pointer trackList {Database::TrackList::getById(session, trackListId)};
|
||||
if (trackList)
|
||||
{
|
||||
const std::vector<Database::IdType> orderedTrackIds {trackList->getTrackIds()};
|
||||
trackIds = std::set<Database::IdType> {std::cbegin(orderedTrackIds), std::cend(orderedTrackIds)};
|
||||
}
|
||||
}
|
||||
|
||||
if (trackIds.empty())
|
||||
return {};
|
||||
|
||||
if (engineType == Database::SimilaritySettings::EngineType::Features
|
||||
&& somSearcher
|
||||
&& std::any_of(std::cbegin(trackIds), std::cend(trackIds), [&](Database::IdType trackId) { return somSearcher->isTrackClassified(trackId); } ))
|
||||
{
|
||||
return somSearcher->getSimilarTracks(trackIds, maxCount);
|
||||
}
|
||||
else
|
||||
return ClusterSearcher::getSimilarTracksFromTrackList(session, trackListId, maxCount);
|
||||
}
|
||||
|
||||
std::vector<Database::IdType>
|
||||
Searcher::getSimilarTracks(Database::Session& dbSession, const std::set<Database::IdType>& trackIds, std::size_t maxCount)
|
||||
{
|
||||
|
||||
@@ -39,6 +39,7 @@ class Searcher
|
||||
Searcher(FeaturesScannerAddon& somAddon);
|
||||
|
||||
// Closest results first
|
||||
std::vector<Database::IdType> getSimilarTracksFromTrackList(Database::Session& session, Database::IdType tracklistId, std::size_t maxCount);
|
||||
std::vector<Database::IdType> getSimilarTracks(Database::Session& session, const std::set<Database::IdType>& tracksId, std::size_t maxCount);
|
||||
std::vector<Database::IdType> getSimilarReleases(Database::Session& session, Database::IdType releaseId, std::size_t maxCount);
|
||||
std::vector<Database::IdType> getSimilarArtists(Database::Session& session, Database::IdType artistId, std::size_t maxCount);
|
||||
|
||||
@@ -27,71 +27,42 @@
|
||||
#include "database/Release.hpp"
|
||||
#include "database/Session.hpp"
|
||||
#include "database/Track.hpp"
|
||||
#include "database/TrackList.hpp"
|
||||
#include "utils/Utils.hpp"
|
||||
|
||||
namespace Similarity {
|
||||
namespace ClusterSearcher {
|
||||
|
||||
static
|
||||
std::vector<Database::IdType>
|
||||
getSimilarTracksLocked(Database::Session& dbSession, const std::set<Database::IdType>& trackIds, std::size_t maxCount)
|
||||
{
|
||||
std::vector<Database::IdType> clusterIds;
|
||||
for (auto trackId : trackIds)
|
||||
{
|
||||
auto track {Database::Track::getById(dbSession, trackId)};
|
||||
if (!track)
|
||||
continue;
|
||||
|
||||
auto clusters = track->getClusters();
|
||||
if (clusters.empty())
|
||||
continue;
|
||||
|
||||
for (const auto& cluster : clusters)
|
||||
clusterIds.push_back(cluster.id());
|
||||
}
|
||||
|
||||
std::vector<Database::IdType> sortedClusterIds;
|
||||
uniqueAndSortedByOccurence(clusterIds.begin(), clusterIds.end(), std::back_inserter(sortedClusterIds));
|
||||
|
||||
std::vector<Database::IdType> res;
|
||||
for (auto clusterId : clusterIds)
|
||||
{
|
||||
auto cluster {Database::Cluster::getById(dbSession, clusterId)};
|
||||
if (!cluster)
|
||||
continue;
|
||||
|
||||
std::set<Database::IdType> clusterTrackIds = cluster->getTrackIds();
|
||||
|
||||
std::set<Database::IdType> candidateTrackIds;
|
||||
std::set_difference(clusterTrackIds.begin(), clusterTrackIds.end(),
|
||||
trackIds.begin(), trackIds.end(),
|
||||
std::inserter(candidateTrackIds, candidateTrackIds.end()));
|
||||
|
||||
if (candidateTrackIds.empty())
|
||||
continue;
|
||||
|
||||
for (auto trackId : candidateTrackIds)
|
||||
{
|
||||
if (res.size() >= maxCount)
|
||||
break;
|
||||
|
||||
res.push_back(trackId);
|
||||
}
|
||||
|
||||
if (res.size() >= maxCount)
|
||||
break;
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
std::vector<Database::IdType>
|
||||
getSimilarTracks(Database::Session& dbSession, const std::set<Database::IdType>& trackIds, std::size_t maxCount)
|
||||
{
|
||||
auto transaction {dbSession.createSharedTransaction()};
|
||||
|
||||
return getSimilarTracksLocked(dbSession, trackIds, maxCount);
|
||||
auto tracks {Database::Track::getSimilarTracks(dbSession, trackIds, 0, maxCount)};
|
||||
std::vector<Database::IdType> res;
|
||||
res.reserve(tracks.size());
|
||||
|
||||
std::transform(std::cbegin(tracks), std::cend(tracks), std::back_inserter(res), [](const auto& track) { return track.id(); });
|
||||
return res;
|
||||
}
|
||||
|
||||
std::vector<Database::IdType>
|
||||
getSimilarTracksFromTrackList(Database::Session& session, Database::IdType tracklistId, std::size_t maxCount)
|
||||
{
|
||||
std::vector<Database::IdType> res;
|
||||
|
||||
auto transaction {session.createSharedTransaction()};
|
||||
|
||||
const Database::TrackList::pointer trackList {Database::TrackList::getById(session, tracklistId)};
|
||||
if (!trackList)
|
||||
return res;
|
||||
|
||||
const std::vector<Database::Track::pointer> tracks {trackList->getSimilarTracks(0, maxCount)};
|
||||
res.reserve(tracks.size());
|
||||
std::transform(std::cbegin(tracks), std::cend(tracks), std::back_inserter(res),
|
||||
[](const Database::Track::pointer& track) { return track.id(); });
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
std::vector<Database::IdType>
|
||||
@@ -105,31 +76,9 @@ getSimilarReleases(Database::Session& dbSession, Database::IdType releaseId, std
|
||||
if (!release)
|
||||
return res;
|
||||
|
||||
auto releaseTracks = release->getTracks();
|
||||
std::set<Database::IdType> releaseTrackIds;
|
||||
|
||||
for (const auto& releaseTrack : releaseTracks)
|
||||
releaseTrackIds.insert(releaseTrack.id());
|
||||
|
||||
auto trackIds {getSimilarTracksLocked(dbSession, releaseTrackIds, maxCount * 5)};
|
||||
for (auto trackId : trackIds)
|
||||
{
|
||||
auto track {Database::Track::getById(dbSession, trackId)};
|
||||
if (!track)
|
||||
continue;
|
||||
|
||||
auto trackRelease = track->getRelease();
|
||||
if (!trackRelease || trackRelease.id() == releaseId)
|
||||
continue;
|
||||
|
||||
if (std::find(res.begin(), res.end(), trackRelease.id()) != res.end())
|
||||
continue;
|
||||
|
||||
res.push_back(trackRelease.id());
|
||||
|
||||
if (res.size() == maxCount)
|
||||
break;
|
||||
}
|
||||
const auto releases {release->getSimilarReleases(0, maxCount)};
|
||||
res.reserve(releases.size());
|
||||
std::transform(std::cbegin(releases), std::cend(releases), std::back_inserter(res), [](const auto& release) { return release.id(); });
|
||||
|
||||
return res;
|
||||
}
|
||||
@@ -145,36 +94,9 @@ getSimilarArtists(Database::Session& dbSession, Database::IdType artistId, std::
|
||||
if (!artist)
|
||||
return res;
|
||||
|
||||
auto artistTracks {artist->getTracks()};
|
||||
std::set<Database::IdType> artistTrackIds;
|
||||
|
||||
for (const auto& artistTrack : artistTracks)
|
||||
artistTrackIds.insert(artistTrack.id());
|
||||
|
||||
auto trackIds {getSimilarTracksLocked(dbSession, artistTrackIds, maxCount * 5)};
|
||||
for (auto trackId : trackIds)
|
||||
{
|
||||
auto track {Database::Track::getById(dbSession, trackId)};
|
||||
if (!track)
|
||||
continue;
|
||||
|
||||
for (const auto& trackArtist : track->getArtists())
|
||||
{
|
||||
if (!trackArtist || trackArtist.id() == artistId)
|
||||
continue;
|
||||
|
||||
if (std::find(res.begin(), res.end(), trackArtist.id()) != res.end())
|
||||
continue;
|
||||
|
||||
res.push_back(trackArtist.id());
|
||||
|
||||
if (res.size() == maxCount)
|
||||
break;
|
||||
}
|
||||
|
||||
if (res.size() == maxCount)
|
||||
break;
|
||||
}
|
||||
const auto artists {artist->getSimilarArtists(0, maxCount)};
|
||||
res.reserve(artists.size());
|
||||
std::transform(std::cbegin(artists), std::cend(artists), std::back_inserter(res), [](const auto& artist) { return artist.id(); });
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
@@ -31,6 +31,7 @@ namespace Similarity {
|
||||
|
||||
namespace ClusterSearcher
|
||||
{
|
||||
std::vector<Database::IdType> getSimilarTracksFromTrackList(Database::Session& session, Database::IdType tracklistId, std::size_t maxCount);
|
||||
std::vector<Database::IdType> getSimilarTracks(Database::Session& session, const std::set<Database::IdType>& tracksId, std::size_t maxCount);
|
||||
std::vector<Database::IdType> getSimilarReleases(Database::Session& session, Database::IdType releaseId, std::size_t maxCount);
|
||||
std::vector<Database::IdType> getSimilarArtists(Database::Session& session, Database::IdType artistId, std::size_t maxCount);
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
#include <Wt/WText.h>
|
||||
#include <Wt/WText.h>
|
||||
|
||||
#include "database/Cluster.hpp"
|
||||
#include "database/Track.hpp"
|
||||
#include "database/TrackList.hpp"
|
||||
#include "database/User.hpp"
|
||||
@@ -411,19 +412,7 @@ PlayQueue::addSome()
|
||||
void
|
||||
PlayQueue::enqueueRadioTrack()
|
||||
{
|
||||
std::vector<Database::IdType> trackIds;
|
||||
|
||||
{
|
||||
auto transaction {LmsApp->getDbSession().createSharedTransaction()};
|
||||
Database::TrackList::pointer tracklist {getTrackList()};
|
||||
|
||||
trackIds = getTrackList()->getTrackIds();
|
||||
}
|
||||
|
||||
if (trackIds.empty())
|
||||
return;
|
||||
|
||||
const std::vector<Database::IdType> trackToAddIds {getService<Similarity::Searcher>()->getSimilarTracks(LmsApp->getDbSession(), std::set<Database::IdType>(std::cbegin(trackIds), std::cend(trackIds)), 1)};
|
||||
const std::vector<Database::IdType> trackToAddIds {getService<Similarity::Searcher>()->getSimilarTracksFromTrackList(LmsApp->getDbSession(), _tracklistId, 1)};
|
||||
enqueueTracks(trackToAddIds);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user