Do not attempt extra data fetch when hitting the end of a query

This commit is contained in:
emeric
2024-03-14 13:51:28 +01:00
parent a769909ebc
commit e4ad0a5406
19 changed files with 177 additions and 148 deletions
+8
View File
@@ -343,6 +343,14 @@ namespace lms::db
utils::execQuery(query, params.range, func);
}
void Track::find(Session& session, const FindParameters& params, bool& moreResults, std::function<void(const Track::pointer&)> func)
{
session.checkReadTransaction();
auto query{ createQuery<Wt::Dbo::ptr<Track>>(session, params)};
utils::execQuery(query, params.range, moreResults, func);
}
RangeResults<TrackId> Track::findSimilarTrackIds(Session& session, const std::vector<TrackId>& tracks, std::optional<Range> range)
{
assert(!tracks.empty());
+6 -21
View File
@@ -142,25 +142,21 @@ namespace lms::db
TrackListEntry::pointer res;
auto entries = getEntries(Range{ pos, 1 });
if (!entries.empty())
res = entries.front();
if (!entries.results.empty())
res = entries.results.front();
return res;
}
std::vector<TrackListEntry::pointer> TrackList::getEntries(std::optional<Range> range) const
RangeResults<ObjectPtr<TrackListEntry>> TrackList::getEntries(std::optional<Range> range) const
{
assert(session());
auto entries{
session()->find<TrackListEntry>()
auto query{session()->find<TrackListEntry>()
.where("tracklist_id = ?").bind(getId())
.orderBy("id")
.limit(range ? static_cast<int>(range->size) + 1 : -1)
.offset(range ? static_cast<int>(range->offset) : -1)
.resultList() };
.orderBy("id") };
return std::vector<TrackListEntry::pointer>(entries.begin(), entries.end());
return utils::execQuery<TrackListEntry::pointer>(query, range);
}
TrackListEntry::pointer TrackList::getEntryByTrackAndDateTime(ObjectPtr<Track> track, const Wt::WDateTime& dateTime) const
@@ -237,17 +233,6 @@ namespace lms::db
return res;
}
bool TrackList::hasTrack(TrackId trackId) const
{
assert(session());
Wt::Dbo::collection<TrackListEntry::pointer> res = session()->query<TrackListEntry::pointer>("SELECT p_e from tracklist_entry p_e INNER JOIN tracklist p ON p_e.tracklist_id = p.id")
.where("p_e.track_id = ?").bind(trackId)
.where("p.id = ?").bind(getId());
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());
+31 -4
View File
@@ -56,15 +56,20 @@ namespace lms::db::utils
{
res.range.offset = range->offset;
applyRange(query, Range{ range->offset, range->size + 1 });
res.results.reserve(range->size);
}
auto collection{ query.resultList() };
res.results.assign(collection.begin(), collection.end());
if (range && res.results.size() == static_cast<std::size_t>(range->size) + 1)
for (auto itResult{ collection.begin() }; itResult != collection.end(); ++itResult)
{
if (range && res.results.size() == range->size)
{
// TODO may optim by not actually requesting the last one
res.moreResults = true;
res.results.pop_back();
break;
}
res.results.push_back(std::move(*itResult));
}
res.range.size = res.results.size();
@@ -85,6 +90,28 @@ namespace lms::db::utils
}
}
template <typename ResultType, typename Query>
void execQuery(Query& query, std::optional<Range> range, bool& moreResults, std::function<void(const ResultType&)> func)
{
if (range)
applyRange(query, Range{ range->offset, range->size + 1 });
moreResults = false;
std::size_t count{};
for (const auto& res : query.resultList())
{
if (range && (count++ == static_cast<std::size_t>(range->size)))
{
moreResults = true;
break;
}
LMS_SCOPED_TRACE_DETAILED("Database", "ExecQueryResult");
func(res);
}
}
Wt::WDateTime normalizeDateTime(const Wt::WDateTime& dateTime);
}
@@ -118,6 +118,7 @@ namespace lms::db
static RangeResults<TrackId> findIds(Session& session, const FindParameters& parameters);
static RangeResults<pointer> find(Session& session, const FindParameters& parameters);
static void find(Session& session, const FindParameters& parameters, std::function<void(const Track::pointer&)> func);
static void find(Session& session, const FindParameters& parameters, bool& moreResults, std::function<void(const Track::pointer&)> func);
static RangeResults<PathResult> findPaths(Session& session, std::optional<Range> range = std::nullopt);
static RangeResults<TrackId> findIdsTrackMBIDDuplicates(Session& session, std::optional<Range> range = std::nullopt);
static RangeResults<TrackId> findIdsWithRecordingMBIDAndMissingFeatures(Session& session, std::optional<Range> range = std::nullopt);
@@ -85,7 +85,7 @@ namespace lms::db
bool isEmpty() const;
std::size_t getCount() const;
ObjectPtr<TrackListEntry> getEntry(std::size_t pos) const;
std::vector<ObjectPtr<TrackListEntry>> getEntries(std::optional<Range> range = {}) const;
RangeResults<ObjectPtr<TrackListEntry>> getEntries(std::optional<Range> range = {}) const;
ObjectPtr<TrackListEntry> getEntryByTrackAndDateTime(ObjectPtr<Track> track, const Wt::WDateTime& dateTime) const;
std::vector<TrackId> getTrackIds() const;
@@ -97,8 +97,6 @@ namespace lms::db
std::vector<ObjectPtr<Cluster>> getClusters() const;
std::vector<std::vector<ObjectPtr<Cluster>>> getClusterGroups(const std::vector<ClusterTypeId>& clusterTypeIds, std::size_t size) const;
bool hasTrack(TrackId trackId) const;
// Ordered from most clusters in common
std::vector<ObjectPtr<Track>> getSimilarTracks(std::optional<std::size_t> offset = {}, std::optional<std::size_t> size = {}) const;
+7 -7
View File
@@ -198,8 +198,8 @@ namespace lms::db::tests
{
auto transaction{ session.createReadTransaction() };
auto entries{ trackList.get()->getEntries() };
ASSERT_EQ(entries.size(), 1);
EXPECT_EQ(entries.front()->getTrack()->getId(), track1.getId());
ASSERT_EQ(entries.results.size(), 1);
EXPECT_EQ(entries.results.front()->getTrack()->getId(), track1.getId());
}
{
@@ -210,16 +210,16 @@ namespace lms::db::tests
{
auto transaction{ session.createReadTransaction() };
auto entries{ trackList.get()->getEntries() };
ASSERT_EQ(entries.size(), 2);
EXPECT_EQ(entries[0]->getTrack()->getId(), track1.getId());
EXPECT_EQ(entries[1]->getTrack()->getId(), track2.getId());
ASSERT_EQ(entries.results.size(), 2);
EXPECT_EQ(entries.results[0]->getTrack()->getId(), track1.getId());
EXPECT_EQ(entries.results[1]->getTrack()->getId(), track2.getId());
}
{
auto transaction{ session.createReadTransaction() };
auto entries{ trackList.get()->getEntries(Range {1, 1}) };
ASSERT_EQ(entries.size(), 1);
EXPECT_EQ(entries[0]->getTrack()->getId(), track2.getId());
ASSERT_EQ(entries.results.size(), 1);
EXPECT_EQ(entries.results[0]->getTrack()->getId(), track2.getId());
}
}
}
@@ -72,7 +72,7 @@ namespace lms::api::subsonic
Response::Node playlistNode{ createPlaylistNode(tracklist, context.dbSession) };
auto entries{ tracklist->getEntries() };
for (const TrackListEntry::pointer& entry : entries)
for (const TrackListEntry::pointer& entry : entries.results)
playlistNode.addArrayChild("entry", createSongNode(context, entry->getTrack(), user));
response.addNode("playlist", std::move(playlistNode));
@@ -129,7 +129,7 @@ namespace lms::api::subsonic
Response::Node playlistNode{ createPlaylistNode(tracklist, context.dbSession) };
auto entries{ tracklist->getEntries() };
for (const TrackListEntry::pointer& entry : entries)
for (const TrackListEntry::pointer& entry : entries.results)
playlistNode.addArrayChild("entry", createSongNode(context, entry->getTrack(), user));
response.addNode("playlist", std::move(playlistNode));
+9 -6
View File
@@ -148,10 +148,11 @@ namespace lms::ui
shuffleBtn->clicked().connect([this]
{
{
// TODO write scope could be reduced
auto transaction{ LmsApp->getDbSession().createWriteTransaction() };
db::TrackList::pointer queue{ getQueue() };
auto entries{ queue->getEntries() };
auto entries{ queue->getEntries().results };
core::random::shuffleContainer(entries);
queue.modify()->clear();
@@ -430,9 +431,9 @@ namespace lms::ui
auto transaction{ LmsApp->getDbSession().createWriteTransaction() };
db::TrackList::pointer queue{ getQueue() };
std::vector<db::TrackListEntry::pointer> entries{ queue->getEntries(db::Range {_trackPos ? *_trackPos + 1 : 0, getCapacity()}) };
tracks.reserve(entries.size());
for (db::TrackListEntry::pointer entry : entries)
auto entries{ queue->getEntries(db::Range {_trackPos ? *_trackPos + 1 : 0, getCapacity()}) };
tracks.reserve(entries.results.size());
for (db::TrackListEntry::pointer& entry : entries.results)
{
tracks.push_back(entry->getTrack()->getId());
entry.remove();
@@ -492,9 +493,11 @@ namespace lms::ui
auto transaction{ LmsApp->getDbSession().createReadTransaction() };
const db::TrackList::pointer queue{ getQueue() };
const auto tracklistEntries{ queue->getEntries(db::Range {_entriesContainer->getCount(), _batchSize}) };
for (const db::TrackListEntry::pointer& tracklistEntry : tracklistEntries)
const auto entries{ queue->getEntries(db::Range {_entriesContainer->getCount(), _batchSize}) };
for (const db::TrackListEntry::pointer& tracklistEntry : entries.results)
addEntry(tracklistEntry);
_entriesContainer->setHasMore(entries.moreResults);
}
void PlayQueue::addEntry(const db::TrackListEntry::pointer& tracklistEntry)
@@ -32,39 +32,33 @@ namespace lms::ui
reset();
}
void
InfiniteScrollingContainer::clear()
void InfiniteScrollingContainer::clear()
{
assert(false);
}
void
InfiniteScrollingContainer::reset()
void InfiniteScrollingContainer::reset()
{
_elements->clear();
setHasMore(true);
}
std::size_t
InfiniteScrollingContainer::getCount()
std::size_t InfiniteScrollingContainer::getCount()
{
return _elements->count();
}
void
InfiniteScrollingContainer::add(std::unique_ptr<Wt::WWidget> result)
void InfiniteScrollingContainer::add(std::unique_ptr<Wt::WWidget> result)
{
return _elements->addWidget(std::move(result));
}
void
InfiniteScrollingContainer::setHasMore()
void InfiniteScrollingContainer::setHasMore()
{
setHasMore(true);
}
void
InfiniteScrollingContainer::setHasMore(bool hasMore)
void InfiniteScrollingContainer::setHasMore(bool hasMore)
{
if (hasMore)
displayLoadingIndicator();
@@ -72,21 +66,19 @@ namespace lms::ui
hideLoadingIndicator();
}
void
InfiniteScrollingContainer::remove(Wt::WWidget& widget)
void InfiniteScrollingContainer::remove(Wt::WWidget& widget)
{
_elements->removeWidget(&widget);
}
void
InfiniteScrollingContainer::remove(std::size_t first, std::size_t last)
void InfiniteScrollingContainer::remove(std::size_t first, std::size_t last)
{
assert(last >= first);
assert(last < static_cast<std::size_t>(_elements->count()));
// remove from end as API is quite uneffective (minimize moves)
std::size_t i{ last };
while (Wt::WWidget* widget {_elements->widget(i)})
while (Wt::WWidget * widget{ _elements->widget(static_cast<int>(i)) })
{
_elements->removeWidget(widget);
if (i-- == first)
@@ -94,21 +86,18 @@ namespace lms::ui
}
}
Wt::WWidget*
InfiniteScrollingContainer::getWidget(std::size_t pos) const
Wt::WWidget* InfiniteScrollingContainer::getWidget(std::size_t pos) const
{
return _elements->widget(pos);
return _elements->widget(static_cast<int>(pos));
}
std::optional<std::size_t>
InfiniteScrollingContainer::getIndexOf(Wt::WWidget& widget) const
std::optional<std::size_t> InfiniteScrollingContainer::getIndexOf(Wt::WWidget& widget) const
{
return _elements->indexOf(&widget);
}
void
InfiniteScrollingContainer::displayLoadingIndicator()
void InfiniteScrollingContainer::displayLoadingIndicator()
{
_loadingIndicator = bindWidget<Wt::WTemplate>("loading-indicator", createLoadingIndicator());
_loadingIndicator->scrollVisibilityChanged().connect([this](bool visible)
@@ -116,17 +105,13 @@ namespace lms::ui
if (!visible)
return;
const auto previousCount {_elements->count()};
onRequestElements.emit();
setHasMore(previousCount != _elements->count());
});
}
void
InfiniteScrollingContainer::hideLoadingIndicator()
void InfiniteScrollingContainer::hideLoadingIndicator()
{
_loadingIndicator = nullptr;
bindEmpty("loading-indicator");
}
}
@@ -57,11 +57,12 @@ namespace lms::ui
Wt::Signal<> onRequestElements;
void setHasMore(bool hasMore); // can be used to add elements afterwards
private:
void clear() override;
void displayLoadingIndicator();
void hideLoadingIndicator();
void setHasMore(bool hasMore); // can be used to add elements afterwards
Wt::WContainerWidget* _elements;
Wt::WTemplate* _loadingIndicator;
+1 -2
View File
@@ -21,8 +21,7 @@
namespace lms::ui
{
std::unique_ptr<Wt::WTemplate>
createLoadingIndicator()
std::unique_ptr<Wt::WTemplate> createLoadingIndicator()
{
auto res {std::make_unique<Wt::WTemplate>(Wt::WString::tr("Lms.LoadingIndicator.template"))};
+6 -1
View File
@@ -332,12 +332,13 @@ namespace lms::ui
releaseContainer.container->add(releaseListHelpers::createEntryForArtist(release, artist));
}
}
releaseContainer.container->setHasMore(false);
}
bool Artist::addSomeNonReleaseTracks()
{
bool areTracksAdded{};
auto transaction{ LmsApp->getDbSession().createReadTransaction() };
const Range range{ static_cast<std::size_t>(_trackContainer->getCount()), _tracksBatchSize };
@@ -348,6 +349,8 @@ namespace lms::ui
params.setSortMethod(TrackSortMethod::Name);
params.setNonRelease(true);
auto transaction{ LmsApp->getDbSession().createReadTransaction() };
const auto tracks{ Track::find(LmsApp->getDbSession(), params) };
for (const Track::pointer& track : tracks.results)
{
@@ -360,6 +363,8 @@ namespace lms::ui
areTracksAdded = true;
}
_trackContainer->setHasMore(tracks.moreResults);
return areTracksAdded;
}
+2
View File
@@ -125,6 +125,8 @@ namespace lms::ui
_container->add(ArtistListHelpers::createEntry(artist));
}
}
_container->setHasMore(artistIds.moreResults);
}
} // namespace lms::ui
+1 -1
View File
@@ -463,7 +463,7 @@ namespace lms::ui
auto isStarred{ [=] { return core::Service<feedback::IFeedbackService>::get()->isStarred(LmsApp->getUserId(), trackId); } };
Wt::WPushButton* starBtn{ entry->bindNew<Wt::WPushButton>("star", Wt::WString::tr(isStarred() ? "Lms.Explore.unstar" : "Lms.Explore.star")) };
starBtn->clicked().connect([=, this]
starBtn->clicked().connect([=]
{
if (isStarred())
{
+2
View File
@@ -129,6 +129,8 @@ namespace lms::ui
_container->add(releaseListHelpers::createEntry(release));
}
}
_container->setHasMore(releaseIds.moreResults);
}
std::vector<ReleaseId> Releases::getAllReleases()
+6
View File
@@ -159,6 +159,8 @@ namespace lms::ui
_artists->add(ArtistListHelpers::createEntry(artist));
}
}
_artists->setHasMore(artistIds.moreResults);
}
void SearchView::addSomeReleases()
@@ -177,6 +179,8 @@ namespace lms::ui
_releases->add(releaseListHelpers::createEntry(release));
}
}
_releases->setHasMore(releaseIds.moreResults);
}
void SearchView::addSomeTracks()
@@ -196,5 +200,7 @@ namespace lms::ui
_tracks->add(TrackListHelpers::createEntry(track, _playQueueController, _filters));
}
}
_tracks->setHasMore(trackIds.moreResults);
}
}
+4 -1
View File
@@ -188,10 +188,13 @@ namespace lms::ui
params.setSortMethod(db::TrackSortMethod::TrackList);
params.setRange(db::Range{ static_cast<std::size_t>(_container->getCount()), _batchSize });
db::Track::find(LmsApp->getDbSession(), params, [this](const Track::pointer& track)
bool moreResults{};
db::Track::find(LmsApp->getDbSession(), params, moreResults, [this](const Track::pointer& track)
{
_container->add(TrackListHelpers::createEntry(track, _playQueueController, _filters));
});
_container->setHasMore(moreResults);
}
} // namespace lms::ui
+2
View File
@@ -121,6 +121,8 @@ namespace lms::ui
if (const TrackList::pointer trackList{ TrackList::find(LmsApp->getDbSession(), trackListId) })
addTracklist(trackList);
}
_container->setHasMore(trackListIds.moreResults);
}
void TrackLists::addTracklist(const ObjectPtr<TrackList>& trackList)
+2
View File
@@ -128,6 +128,8 @@ namespace lms::ui
if (const Track::pointer track{ Track::find(LmsApp->getDbSession(), trackId) })
_container->add(TrackListHelpers::createEntry(track, _playQueueController, _filters));
}
_container->setHasMore(trackIds.moreResults);
}
std::vector<db::TrackId> Tracks::getAllTracks()