Skip to content

Commit

Permalink
Homepage: improve performance by adjusting loading options
Browse files Browse the repository at this point in the history
  • Loading branch information
rubenwardy committed Nov 18, 2023
1 parent 588945d commit 12016ea
Show file tree
Hide file tree
Showing 7 changed files with 81 additions and 39 deletions.
61 changes: 37 additions & 24 deletions app/blueprints/homepage/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,11 @@
from sqlalchemy import and_

from app.models import Package, PackageReview, Thread, User, PackageState, db, PackageType, PackageRelease, Tags, Tag, \
Collection
Collection, License

bp = Blueprint("homepage", __name__)

from sqlalchemy.orm import joinedload, subqueryload
from sqlalchemy.orm import joinedload, subqueryload, load_only, noload
from sqlalchemy.sql.expression import func


Expand All @@ -38,32 +38,44 @@ def gamejam():
def home():
def package_load(query):
return query.options(
joinedload(Package.author),
subqueryload(Package.main_screenshot),
load_only(Package.name, Package.title, Package.short_desc, Package.state, Package.main_screenshot, raiseload=True),
joinedload(Package.author).load_only(User.username, User.display_name, raiseload=True),
joinedload(Package.license).load_only(License.name, License.is_foss, raiseload=True),
joinedload(Package.media_license).load_only(License.name, License.is_foss, raiseload=True))

def package_spotlight_load(query):
return query.options(
load_only(Package.name, Package.title, Package.type, Package.short_desc, Package.state, Package.main_screenshot, Package.cover_image_id, raiseload=True),
joinedload(Package.tags),
joinedload(Package.content_warnings),
joinedload(Package.author).load_only(User.username, User.display_name, raiseload=True),
subqueryload(Package.cover_image),
joinedload(Package.license),
joinedload(Package.media_license))
joinedload(Package.license).load_only(License.name, License.is_foss, raiseload=True),
joinedload(Package.media_license).load_only(License.name, License.is_foss, raiseload=True))

def review_load(query):
return query.options(
joinedload(PackageReview.author),
joinedload(PackageReview.thread).subqueryload(Thread.first_reply),
joinedload(PackageReview.package).joinedload(Package.author).load_only(User.username, User.display_name),
joinedload(PackageReview.package).load_only(Package.title, Package.name).subqueryload(Package.main_screenshot))
load_only(PackageReview.id, PackageReview.rating, PackageReview.created_at, raiseload=True),
joinedload(PackageReview.author).load_only(User.username, User.rank, User.email, User.display_name, User.profile_pic, User.is_active, raiseload=True),
joinedload(PackageReview.votes),
joinedload(PackageReview.thread).load_only(Thread.title, Thread.replies_count, raiseload=True).subqueryload(Thread.first_reply),
joinedload(PackageReview.package).joinedload(Package.author).load_only(User.username, User.display_name, raiseload=True),
joinedload(PackageReview.package).load_only(Package.title, Package.name, Package.main_screenshot, raiseload=True))

query = Package.query.filter_by(state=PackageState.APPROVED)
count = query.count()
count = db.session.query(Package.id).filter(Package.state == PackageState.APPROVED).count()

spotlight_pkgs = query.filter(
Package.collections.any(and_(Collection.name == "spotlight", Collection.author.has(username="ContentDB")))) \
.order_by(func.random()).limit(6).all()
spotlight_pkgs = package_spotlight_load(query.filter(
Package.collections.any(and_(Collection.name == "spotlight", Collection.author.has(username="ContentDB"))))
.order_by(func.random())).limit(6).all()

new = package_load(query.order_by(db.desc(Package.approved_at))).limit(PKGS_PER_ROW).all()
pop_mod = package_load(query.filter_by(type=PackageType.MOD).order_by(db.desc(Package.score))).limit(2*PKGS_PER_ROW).all()
pop_gam = package_load(query.filter_by(type=PackageType.GAME).order_by(db.desc(Package.score))).limit(2*PKGS_PER_ROW).all()
pop_txp = package_load(query.filter_by(type=PackageType.TXP).order_by(db.desc(Package.score))).limit(2*PKGS_PER_ROW).all()
high_reviewed = package_load(query.order_by(db.desc(Package.score - Package.score_downloads))) \
.filter(Package.reviews.any()).limit(PKGS_PER_ROW).all()
new = package_load(query).order_by(db.desc(Package.approved_at)).limit(PKGS_PER_ROW).all() # 0.06
pop_mod = package_load(query).filter_by(type=PackageType.MOD).order_by(db.desc(Package.score)).limit(2*PKGS_PER_ROW).all()
pop_gam = package_load(query).filter_by(type=PackageType.GAME).order_by(db.desc(Package.score)).limit(2*PKGS_PER_ROW).all()
pop_txp = package_load(query).filter_by(type=PackageType.TXP).order_by(db.desc(Package.score)).limit(2*PKGS_PER_ROW).all()

high_reviewed = package_load(query.order_by(db.desc(Package.score - Package.score_downloads))
.filter(Package.reviews.any()).limit(PKGS_PER_ROW)).all()

recent_releases_query = (
db.session.query(
Expand All @@ -75,11 +87,12 @@ def review_load(query):
.order_by(db.desc("max_created_at"))
.limit(PKGS_PER_ROW)
.subquery())

updated = (
db.session.query(Package)
.join(recent_releases_query, and_(Package.id == recent_releases_query.c.id,
Package.releases.any(releaseDate=recent_releases_query.c.max_created_at)))
.all())
package_load(db.session.query(Package)
.join(recent_releases_query, and_(Package.id == recent_releases_query.c.id,
Package.releases.any(releaseDate=recent_releases_query.c.max_created_at))))
.all())

reviews = review_load(PackageReview.query.filter(PackageReview.rating > 3)
.order_by(db.desc(PackageReview.created_at))).limit(5).all()
Expand Down
37 changes: 31 additions & 6 deletions app/models/packages.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,8 @@
from flask import url_for
from flask_babel import lazy_gettext
from flask_sqlalchemy import BaseQuery
from sqlalchemy import or_
from sqlalchemy import or_, select, text, func
from sqlalchemy.orm import column_property
from sqlalchemy_searchable import SearchQueryMixin
from sqlalchemy_utils.types import TSVectorType
from sqlalchemy.dialects.postgresql import insert
Expand Down Expand Up @@ -426,9 +427,12 @@ def donate_url_actual(self):
screenshots = db.relationship("PackageScreenshot", back_populates="package", foreign_keys="PackageScreenshot.package_id",
lazy="dynamic", order_by=db.asc("package_screenshot_order"), cascade="all, delete, delete-orphan")

main_screenshot = db.relationship("PackageScreenshot", uselist=False, foreign_keys="PackageScreenshot.package_id",
lazy=True, order_by=db.asc("package_screenshot_order"), viewonly=True,
primaryjoin="and_(Package.id==PackageScreenshot.package_id, PackageScreenshot.approved)")
main_screenshot = column_property(select(text("package_screenshot"))
.select_from(text("package_screenshot"))
.where(id == text("package_screenshot.id"))
.order_by(db.asc(text("package_screenshot.order")))
.limit(1)
.as_scalar())

cover_image_id = db.Column(db.Integer, db.ForeignKey("package_screenshot.id"), nullable=True, default=None)
cover_image = db.relationship("PackageScreenshot", uselist=False, foreign_keys=[cover_image_id])
Expand All @@ -438,8 +442,8 @@ def donate_url_actual(self):
threads = db.relationship("Thread", back_populates="package", order_by=db.desc("thread_created_at"),
foreign_keys="Thread.package_id", cascade="all, delete, delete-orphan", lazy="dynamic")

reviews = db.relationship("PackageReview", back_populates="package",
order_by=[db.desc("package_review_score"),db.desc("package_review_created_at")],
reviews = db.relationship("PackageReview", back_populates="package", lazy="dynamic",
order_by=[db.desc("package_review_score"), db.desc("package_review_created_at")],
cascade="all, delete, delete-orphan")

audit_log_entries = db.relationship("AuditLogEntry", foreign_keys="AuditLogEntry.package_id",
Expand Down Expand Up @@ -785,6 +789,27 @@ def get_conf_file_name(self):
elif self.type == PackageType.GAME:
return "game.conf"

def get_review_summary(self):
from app.models import PackageReview
rows = (db.session.query(PackageReview.rating, func.count(PackageReview.id))
.select_from(PackageReview)
.where(PackageReview.package_id == self.id)
.group_by(PackageReview.rating)
.all())

negative = 0
neutral = 0
positive = 0
for rating, count in rows:
if rating > 3:
positive += count
elif rating == 3:
neutral += count
else:
negative += count

return [positive, neutral, negative]


class MetaPackage(db.Model):
id = db.Column(db.Integer, primary_key=True)
Expand Down
7 changes: 7 additions & 0 deletions app/models/threads.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@
from typing import Tuple, List

from flask import url_for
from sqlalchemy import select, func, text
from sqlalchemy.orm import column_property

from . import db
from .users import Permission, UserRank, User
Expand Down Expand Up @@ -59,6 +61,11 @@ class Thread(db.Model):
lazy=True, order_by=db.asc("id"), viewonly=True,
primaryjoin="Thread.id==ThreadReply.thread_id")

replies_count = column_property(select(func.count(text("thread_reply.id")))
.select_from(text("thread_reply"))
.where(text("thread_reply.thread_id") == id)
.as_scalar())

def get_description(self):
comment = self.first_reply.comment.replace("\r\n", " ").replace("\n", " ").replace(" ", " ")
if len(comment) > 100:
Expand Down
2 changes: 1 addition & 1 deletion app/querybuilder.py
Original file line number Diff line number Diff line change
Expand Up @@ -154,7 +154,7 @@ def build_package_query(self):
else:
query = Package.query.filter_by(state=PackageState.APPROVED)

query = query.options(subqueryload(Package.main_screenshot), subqueryload(Package.aliases))
query = query.options(subqueryload(Package.aliases))

query = self.order_package_query(self.filter_package_query(query))

Expand Down
7 changes: 2 additions & 5 deletions app/templates/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -77,11 +77,8 @@ <h3 class="mt-0 mb-3">
<span class="btn" title="{{ _('Reviews') }}">
<i class="fas fa-star-half-alt"></i>
<span class="count">
+{{ package.reviews | selectattr("rating", "equalto", 5) | list | length }}
/
{{ package.reviews | selectattr("rating", "equalto", 3) | list | length }}
/
-{{ package.reviews | selectattr("rating", "equalto", 1) | list | length }}
{% set summary = package.get_review_summary() %}
+{{ summary[0] }} / {{ summary[1] }} / -{{ summary[2] }}
</span>
</span>
</div>
Expand Down
4 changes: 2 additions & 2 deletions app/templates/macros/reviews.html
Original file line number Diff line number Diff line change
Expand Up @@ -79,10 +79,10 @@
</a>
{% endif %}

<a class="btn {% if review.thread.replies.count() > 1 %} btn-primary {% else %} btn-secondary {% endif %} me-1"
<a class="btn {% if review.thread.replies_count > 1 %} btn-primary {% else %} btn-secondary {% endif %} me-1"
href="{{ url_for('threads.view', id=review.thread.id) }}">
<i class="fas fa-comments me-2"></i>
{{ _("%(num)d comments", num=review.thread.replies.count() - 1) }}
{{ _("%(num)d comments", num=review.thread.replies_count - 1) }}
</a>

{{ render_review_vote(review, current_user, url_set_anchor(review_anchor)) }}
Expand Down
2 changes: 1 addition & 1 deletion app/templates/macros/threads.html
Original file line number Diff line number Diff line change
Expand Up @@ -216,7 +216,7 @@
</div>

{% for t in threads %}
{% set replies = t.replies.count() - 1 %}
{% set replies = t.replies_count - 1 %}

<a class="list-group-item list-group-item-action"
href="{{ url_for('threads.view', id=t.id) }}">
Expand Down

0 comments on commit 12016ea

Please sign in to comment.