Skip to content

Commit

Permalink
fix: don't ever join projects and home_projects (partly addresses #1393)
Browse files Browse the repository at this point in the history
  • Loading branch information
MiniDigger committed Jul 31, 2024
1 parent 060d99b commit c8a137f
Show file tree
Hide file tree
Showing 12 changed files with 192 additions and 153 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
import io.papermc.hangar.controller.extras.pagination.filters.projects.ProjectAuthorFilter;
import io.papermc.hangar.controller.extras.pagination.filters.projects.ProjectCategoryFilter;
import io.papermc.hangar.controller.extras.pagination.filters.projects.ProjectLicenseFilter;
import io.papermc.hangar.controller.extras.pagination.filters.projects.ProjectMCVersionFilter;
import io.papermc.hangar.controller.extras.pagination.filters.projects.ProjectPlatformVersionFilter;
import io.papermc.hangar.controller.extras.pagination.filters.projects.ProjectMemberFilter;
import io.papermc.hangar.controller.extras.pagination.filters.projects.ProjectPlatformFilter;
import io.papermc.hangar.controller.extras.pagination.filters.projects.ProjectQueryFilter;
Expand Down Expand Up @@ -64,7 +64,7 @@ public ResponseEntity<PaginatedResult<ProjectMember>> getProjectMembers(final St
}

@Override
@ApplicableFilters({ProjectCategoryFilter.class, ProjectPlatformFilter.class, ProjectAuthorFilter.class, ProjectQueryFilter.class, ProjectLicenseFilter.class, ProjectMCVersionFilter.class, ProjectTagFilter.class, ProjectMemberFilter.class})
@ApplicableFilters({ProjectCategoryFilter.class, ProjectPlatformFilter.class, ProjectAuthorFilter.class, ProjectQueryFilter.class, ProjectLicenseFilter.class, ProjectPlatformVersionFilter.class, ProjectTagFilter.class, ProjectMemberFilter.class})
@ApplicableSorters({SorterRegistry.VIEWS, SorterRegistry.DOWNLOADS, SorterRegistry.NEWEST, SorterRegistry.STARS, SorterRegistry.UPDATED, SorterRegistry.RECENT_DOWNLOADS, SorterRegistry.RECENT_VIEWS, SorterRegistry.SLUG})
public ResponseEntity<PaginatedResult<Project>> getProjects(final boolean prioritizeExactMatch, final @NotNull RequestPagination pagination) {
final boolean seeHidden = this.getGlobalPermissions().has(Permission.SeeHidden);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,11 @@ public enum SorterRegistry implements Sorter {
VIEWS("views", simpleSorter("hp.views")),
STARS("stars", simpleSorter("hp.stars")),
DOWNLOADS("downloads", simpleSorter("hp.downloads")),
NEWEST("newest", simpleSorter("p.created_at")),
NEWEST("newest", simpleSorter("hp.created_at")),
UPDATED("updated", simpleSorter("last_updated_double")),
RECENT_VIEWS("recent_views", simpleSorter("hp.recent_views")),
RECENT_DOWNLOADS("recent_downloads", simpleSorter("hp.recent_downloads")),
SLUG("slug", simpleSorter("LOWER(p.slug)"));
SLUG("slug", simpleSorter("LOWER(hp.slug)"));

private static final Map<String, SorterRegistry> SORTERS = new HashMap<>();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ static class ProjectAuthorFilterInstance implements Filter.FilterInstance {

@Override
public void createSql(final StringBuilder sb, final SqlStatement<?> q) {
sb.append(" AND ").append("lower(p.owner_name)").append(" = ").append("lower(:ownerName)");
sb.append(" AND ").append("lower(hp.owner_name)").append(" = ").append("lower(:ownerName)");
q.bind("ownerName", this.ownerName);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ public ProjectCategoryFilterInstance(final Category[] categories) {

@Override
public void createSql(final StringBuilder sb, final SqlStatement<?> q) {
sb.append(" AND ").append("p.category").append(" IN (");
sb.append(" AND ").append("hp.category").append(" IN (");
for (int i = 0; i < this.categories.length; i++) {
sb.append(":__category__").append(i);
if (i + 1 != this.categories.length) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ public ProjectLicenseFilterInstance(final String[] licenses) {

@Override
public void createSql(final StringBuilder sb, final SqlStatement<?> q) {
sb.append(" AND p.license_type").append(" IN (");
sb.append(" AND hp.license_type").append(" IN (");
for (int i = 0; i < this.licenses.length; i++) {
sb.append(":__licenses__").append(i);
if (i + 1 != this.licenses.length) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,15 +51,21 @@ public ProjectPlatformFilterInstance(final Platform[] platforms) {

@Override
public void createSql(final StringBuilder sb, final SqlStatement<?> q) {
sb.append(" AND v.platform").append(" IN (");
sb.append(" ");
sb.append("""
AND EXISTS (
SELECT 1
FROM jsonb_array_elements(hp.supported_platforms) AS sp
WHERE sp->>'platform' IN (
""");
for (int i = 0; i < this.platforms.length; i++) {
sb.append(":__platform__").append(i);
if (i + 1 != this.platforms.length) {
sb.append(',');
}
q.bind("__platform__" + i, this.platforms[i]);
q.bind("__platform__" + i, this.platforms[i].ordinal() + "");
}
sb.append(')');
sb.append("))");
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,12 @@
import org.springframework.web.context.request.NativeWebRequest;

@Component
public class ProjectMCVersionFilter implements Filter<ProjectMCVersionFilter.ProjectMCVersionFilterInstance, String[]> {
public class ProjectPlatformVersionFilter implements Filter<ProjectPlatformVersionFilter.ProjectPlatformVersionFilterInstance, String[]> {

private final ConversionService conversionService;

@Autowired
public ProjectMCVersionFilter(final ConversionService conversionService) {
public ProjectPlatformVersionFilter(final ConversionService conversionService) {
this.conversionService = conversionService;
}

Expand All @@ -27,7 +27,7 @@ public Set<String> getQueryParamNames() {

@Override
public String getDescription() {
return "A Minecraft version to filter for";
return "A platform version to filter for";
}

@Override
Expand All @@ -36,34 +36,41 @@ public String[] getValue(final NativeWebRequest webRequest) {
}

@Override
public @NotNull ProjectMCVersionFilterInstance create(final NativeWebRequest webRequest) {
return new ProjectMCVersionFilterInstance(this.conversionService.convert(this.getValue(webRequest), String[].class));
public @NotNull ProjectPlatformVersionFilterInstance create(final NativeWebRequest webRequest) {
return new ProjectPlatformVersionFilterInstance(this.conversionService.convert(this.getValue(webRequest), String[].class));
}

static class ProjectMCVersionFilterInstance implements Filter.FilterInstance {
static class ProjectPlatformVersionFilterInstance implements Filter.FilterInstance {

private final String[] versions;

ProjectMCVersionFilterInstance(final String[] versions) {
ProjectPlatformVersionFilterInstance(final String[] versions) {
this.versions = versions;
}

@Override
public void createSql(final StringBuilder sb, final SqlStatement<?> q) {
sb.append(" AND v.version").append(" IN (");
sb.append(" ");
sb.append("""
AND EXISTS (
SELECT 1
FROM jsonb_array_elements(hp.supported_platforms) AS sp,
jsonb_array_elements_text(sp->'versions') AS version
WHERE version IN (
""");
for (int i = 0; i < this.versions.length; i++) {
sb.append(":__version__").append(i);
if (i + 1 != this.versions.length) {
sb.append(',');
}
q.bind("__version__" + i, this.versions[i]);
}
sb.append(')');
sb.append("))");
}

@Override
public String toString() {
return "ProjectMCVersionFilterInstance{" +
return "ProjectPlatformVersionFilter{" +
"versions=" + Arrays.toString(this.versions) +
'}';
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ public ProjectTagFilterInstance(final Tag[] tags) {

@Override
public void createSql(final StringBuilder sb, final SqlStatement<?> q) {
sb.append(" AND p.tags").append(" @> '{");
sb.append(" AND hp.tags").append(" @> '{");
boolean first = true;
for (final Tag tag : this.tags) {
if (first) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -74,61 +74,57 @@ public interface ProjectsApiDAO {

@UseStringTemplateEngine
@SqlQuery("""
SELECT DISTINCT (hp.id),
p.created_at,
p.name,
p.owner_name "owner",
p.slug,
hp.views,
hp.downloads,
hp.recent_views,
hp.recent_downloads,
hp.stars,
hp.watchers,
p.category,
p.description,
hp.last_updated AS dum, --- need to add this so we can use it in order by, special constraint on distinct queries
LOWER(p.slug) AS dum2,
coalesce(hp.last_updated, p.created_at) AS last_updated,
((extract(EPOCH FROM coalesce(hp.last_updated, p.created_at)) - 1609459200) / 604800) *1 AS last_updated_double, --- We can order with this. That "dum" does not work. It only orders it with this.
p.visibility,
EXISTS(SELECT * FROM project_stars ps WHERE ps.project_id = p.id AND ps.user_id = :requesterId) AS starred,
EXISTS(SELECT * FROM project_watchers pw WHERE pw.project_id = p.id AND pw.user_id = :requesterId) AS watching,
EXISTS(SELECT * FROM project_flags pf WHERE pf.project_id = p.id AND pf.user_id = :requesterId AND pf.resolved IS FALSE) AS flagged,
p.links,
p.tags,
p.license_name,
p.license_type,
p.license_url,
p.keywords,
p.donation_enabled,
p.donation_subject,
p.sponsors,
CASE WHEN :query IS NULL THEN 1 WHEN lower(p.name) = :query THEN 2 ELSE 3 END AS exact_match
SELECT hp.id,
hp.created_at,
hp.name,
hp.owner_name AS owner,
hp.slug,
hp.views,
hp.downloads,
hp.recent_views,
hp.recent_downloads,
hp.stars,
hp.watchers,
hp.category,
hp.description,
hp.visibility,
hp.links,
hp.tags,
hp.license_name,
hp.license_type,
hp.license_url,
hp.keywords,
hp.donation_enabled,
hp.donation_subject,
hp.sponsors,
hp.last_updated,
hp.supported_platforms,
((extract(EPOCH FROM hp.last_updated) - 1609459200) / 604800) * 1 AS last_updated_double,-- for ordering --
exists(SELECT 1 FROM project_stars ps WHERE ps.project_id = hp.id AND ps.user_id = :requesterId) AS starred,
exists(SELECT 1 FROM project_watchers pw WHERE pw.project_id = hp.id AND pw.user_id = :requesterId) AS watching,
exists(SELECT 1 FROM project_flags pf WHERE pf.project_id = hp.id AND pf.user_id = :requesterId AND pf.resolved IS FALSE) AS flagged,
CASE
WHEN :query IS NULL THEN 1
WHEN lower(hp.name) = :query THEN 2
ELSE 3
END AS exact_match
FROM home_projects hp
JOIN projects p ON hp.id = p.id
LEFT JOIN project_versions pv ON p.id = pv.project_id
LEFT JOIN project_version_platform_dependencies pvpd ON pv.id = pvpd.version_id
LEFT JOIN platform_versions v ON pvpd.platform_version_id = v.id
WHERE TRUE <filters> -- Not sure how else to get here a single Where
<if(!seeHidden)> AND (p.visibility = 0 <if(requesterId)>OR (:requesterId = ANY(hp.project_members) AND p.visibility != 4)<endif>) <endif>
<sorters>
<offsetLimit>""")
WHERE TRUE <filters>
<if(!seeHidden)> AND (hp.visibility = 0 <if(requesterId)>OR (:requesterId = ANY(hp.project_members) AND hp.visibility != 4)<endif>) <endif>
<sorters>
<offsetLimit>""")
@DefineNamedBindings
List<Project> getProjects(@Define boolean seeHidden, Long requesterId, @BindPagination RequestPagination pagination, @Nullable String query);

// This query can be shorter because it doesn't need all those column values as above does, just a single column for the amount of rows to be counted
@UseStringTemplateEngine
@SqlQuery("SELECT count(DISTINCT hp.id) " +
" FROM home_projects hp" +
" JOIN projects p ON hp.id = p.id" +
" LEFT JOIN project_versions pv ON p.id = pv.project_id " +
" LEFT JOIN project_version_platform_dependencies pvpd ON pv.id = pvpd.version_id " +
" LEFT JOIN platform_versions v ON pvpd.platform_version_id = v.id " +
" WHERE TRUE <filters>" + // Not sure how else to get here a single Where
" <if(!seeHidden)> AND (p.visibility = 0 <if(requesterId)>OR (<requesterId> = ANY(hp.project_members) AND p.visibility != 4)<endif>) <endif> ")
long countProjects(@Define boolean seeHidden, @Define Long requesterId,
@BindPagination(isCount = true) RequestPagination pagination);
@SqlQuery(
"""
SELECT count(hp.id)
FROM home_projects hp
WHERE TRUE <filters>
<if(!seehidden)> AND (hp.visibility = 0 <if(requesterid)>OR (<requesterId> = ANY(hp.project_members) AND hp.visibility != 4)<endif>) <endif>""")
long countProjects(@Define boolean seeHidden, @Define Long requesterId, @BindPagination(isCount = true) RequestPagination pagination);

@RegisterConstructorMapper(ProjectMember.class)
@RegisterColumnMapperFactory(CompactRoleColumnMapperFactory.class)
Expand Down
Loading

0 comments on commit c8a137f

Please sign in to comment.