mirror of
https://github.com/HangarMC/Hangar.git
synced 2025-01-06 13:56:14 +08:00
homepage search/sort/filter
This commit is contained in:
parent
debd074388
commit
877d939e0a
@ -122,7 +122,9 @@
|
||||
"mostDownloads": "Most Downloads",
|
||||
"mostViews": "Most Views",
|
||||
"newest": "Newest",
|
||||
"recentlyUpdated": "Recently Updated"
|
||||
"recentlyUpdated": "Recently Updated",
|
||||
"recentViews": "Recent Views",
|
||||
"recentDownloads": "Recent Downloads"
|
||||
},
|
||||
"category": {
|
||||
"info": "Category",
|
||||
|
@ -32,6 +32,8 @@ const sorters = [
|
||||
{ id: "views", label: i18n.t("project.sorting.mostViews") },
|
||||
{ id: "newest", label: i18n.t("project.sorting.newest") },
|
||||
{ id: "updated", label: i18n.t("project.sorting.recentlyUpdated") },
|
||||
{ id: "recent_views", label: i18n.t("project.sorting.recentViews") },
|
||||
{ id: "recent_downloads", label: i18n.t("project.sorting.recentDownloads") },
|
||||
];
|
||||
|
||||
const versions = backendData.platforms
|
||||
@ -45,20 +47,29 @@ const filters = ref({
|
||||
versions: [],
|
||||
categories: [],
|
||||
platforms: [],
|
||||
licences: [],
|
||||
licenses: [],
|
||||
});
|
||||
|
||||
const query = ref<string>((route.query.q as string) || "");
|
||||
const loggedOut = ref<boolean>("loggedOut" in route.query);
|
||||
const projects = ref<PaginatedResult<Project> | null>();
|
||||
const activeSorter = ref<string>("updated");
|
||||
|
||||
const requestParams = computed(() => {
|
||||
// TODO add filters for licence and version
|
||||
// TODO implement sorting, see https://github.com/HangarMC/Hangar/pull/597
|
||||
const params: Record<string, any> = { limit: 25, offset: 0, category: filters.value.categories, platform: filters.value.platforms };
|
||||
if (query.value && query.value) {
|
||||
// TODO add filter for mc version
|
||||
const params: Record<string, any> = {
|
||||
limit: 25,
|
||||
offset: 0,
|
||||
category: filters.value.categories,
|
||||
platform: filters.value.platforms,
|
||||
license: filters.value.licenses,
|
||||
};
|
||||
if (query.value) {
|
||||
params.q = query.value;
|
||||
}
|
||||
if (activeSorter.value) {
|
||||
params.sort = activeSorter.value;
|
||||
}
|
||||
return params;
|
||||
});
|
||||
const p = await useProjects(requestParams.value).catch((e) => handleRequestError(e, ctx, i18n));
|
||||
@ -67,9 +78,8 @@ if (p && p.value) {
|
||||
}
|
||||
|
||||
watch(filters, async () => updateProjects(), { deep: true });
|
||||
watch(query, async () => {
|
||||
await updateProjects();
|
||||
});
|
||||
watch(query, async () => updateProjects());
|
||||
watch(activeSorter, async () => updateProjects());
|
||||
|
||||
async function updateProjects() {
|
||||
projects.value = await useApi<PaginatedResult<Project>>("projects", false, "get", requestParams.value);
|
||||
@ -127,7 +137,7 @@ useHead(meta);
|
||||
>
|
||||
<MenuItems class="absolute right-0 top-16 flex flex-col z-10 background-default drop-shadow-md rounded-md border-top-primary">
|
||||
<MenuItem v-for="sorter in sorters" :key="sorter.id" v-slot="{ active }">
|
||||
<button :class="{ 'bg-gradient-to-r from-[#004ee9] to-[#367aff] text-white': active }" class="p-2 text-left">
|
||||
<button :class="{ 'bg-gradient-to-r from-[#004ee9] to-[#367aff] text-white': active }" class="p-2 text-left" @click="activeSorter = sorter.id">
|
||||
{{ sorter.label }}
|
||||
</button>
|
||||
</MenuItem>
|
||||
@ -175,7 +185,7 @@ useHead(meta);
|
||||
<div class="licenses">
|
||||
<h3 class="font-bold mb-1">Licenses</h3>
|
||||
<div class="flex flex-col gap-1">
|
||||
<InputCheckbox v-for="license in backendData.licenses" :key="license" v-model="filters.licences" :value="license" :label="license" />
|
||||
<InputCheckbox v-for="license in backendData.licenses" :key="license" v-model="filters.licenses" :value="license" :label="license" />
|
||||
</div>
|
||||
</div>
|
||||
</Card>
|
||||
|
@ -2,7 +2,9 @@ package io.papermc.hangar.controller.api.v1;
|
||||
|
||||
import io.papermc.hangar.HangarComponent;
|
||||
import io.papermc.hangar.controller.api.v1.interfaces.IProjectsController;
|
||||
import io.papermc.hangar.controller.extras.pagination.SorterRegistry;
|
||||
import io.papermc.hangar.controller.extras.pagination.annotations.ApplicableFilters;
|
||||
import io.papermc.hangar.controller.extras.pagination.annotations.ApplicableSorters;
|
||||
import io.papermc.hangar.controller.extras.pagination.filters.projects.*;
|
||||
import io.papermc.hangar.model.api.PaginatedResult;
|
||||
import io.papermc.hangar.model.api.User;
|
||||
@ -52,9 +54,10 @@ public class ProjectsController extends HangarComponent implements IProjectsCont
|
||||
}
|
||||
|
||||
@Override
|
||||
@ApplicableFilters({ProjectCategoryFilter.class, ProjectPlatformFilter.class, ProjectAuthorFilter.class, ProjectQueryFilter.class, ProjectTagFilter.class})
|
||||
public ResponseEntity<PaginatedResult<Project>> getProjects(String q, ProjectSortingStrategy sort, boolean orderWithRelevance, @NotNull RequestPagination pagination) {
|
||||
return ResponseEntity.ok(projectsApiService.getProjects(q, sort, orderWithRelevance, pagination));
|
||||
@ApplicableFilters({ProjectCategoryFilter.class, ProjectPlatformFilter.class, ProjectAuthorFilter.class, ProjectQueryFilter.class, ProjectTagFilter.class, ProjectLicenseFilter.class})
|
||||
@ApplicableSorters({SorterRegistry.VIEWS, SorterRegistry.DOWNLOADS, SorterRegistry.NEWEST, SorterRegistry.STARS, SorterRegistry.UPDATED, SorterRegistry.RECENT_DOWNLOADS, SorterRegistry.RECENT_VIEWS})
|
||||
public ResponseEntity<PaginatedResult<Project>> getProjects(String q, boolean orderWithRelevance, @NotNull RequestPagination pagination) {
|
||||
return ResponseEntity.ok(projectsApiService.getProjects(q, orderWithRelevance, pagination));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -79,7 +79,6 @@ public interface IProjectsController {
|
||||
@GetMapping("/projects")
|
||||
ResponseEntity<PaginatedResult<Project>> getProjects(
|
||||
@ApiParam("The query to use when searching") @RequestParam(required = false) String q,
|
||||
@ApiParam("How to sort the projects") @RequestParam(defaultValue = "updated") ProjectSortingStrategy sort,
|
||||
@ApiParam("If how relevant the project is to the given query should be used when sorting the projects") @RequestParam(defaultValue = "true") boolean relevance,
|
||||
@ApiParam("Pagination information") @NotNull RequestPagination pagination
|
||||
);
|
||||
|
@ -9,7 +9,16 @@ public enum SorterRegistry implements Sorter {
|
||||
|
||||
USER_JOIN_DATE("joinDate", simpleSorter("u.join_date")),
|
||||
USER_NAME("username", simpleSorter("username")),
|
||||
USER_PROJECT_COUNT("projectCount", simpleSorter("project_count"));
|
||||
USER_PROJECT_COUNT("projectCount", simpleSorter("project_count")),
|
||||
|
||||
// For Projects
|
||||
VIEWS("views", simpleSorter("hp.views")),
|
||||
STARS("stars", simpleSorter("hp.stars")),
|
||||
DOWNLOADS("downloads", simpleSorter("hp.downloads")),
|
||||
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"));
|
||||
|
||||
private static final Map<String, SorterRegistry> SORTERS = new HashMap<>();
|
||||
|
||||
@ -47,7 +56,7 @@ public enum SorterRegistry implements Sorter {
|
||||
throw new IllegalArgumentException(name + " is not a registered sorter");
|
||||
}
|
||||
|
||||
enum SortDirection {
|
||||
public enum SortDirection {
|
||||
ASCENDING(" ASC"),
|
||||
DESCENDING(" DESC");
|
||||
|
||||
|
@ -0,0 +1,69 @@
|
||||
package io.papermc.hangar.controller.extras.pagination.filters.projects;
|
||||
|
||||
import org.jdbi.v3.core.statement.SqlStatement;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.core.convert.ConversionService;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.web.context.request.NativeWebRequest;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Set;
|
||||
|
||||
import io.papermc.hangar.controller.extras.pagination.Filter;
|
||||
|
||||
@Component
|
||||
public class ProjectLicenseFilter implements Filter<ProjectLicenseFilter.ProjectLicenseFilterInstance> {
|
||||
|
||||
private final ConversionService conversionService;
|
||||
|
||||
@Autowired
|
||||
public ProjectLicenseFilter(ConversionService conversionService) {
|
||||
this.conversionService = conversionService;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<String> getQueryParamNames() {
|
||||
return Set.of("license");
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDescription() {
|
||||
return "A license to filter for";
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public ProjectLicenseFilterInstance create(NativeWebRequest webRequest) {
|
||||
return new ProjectLicenseFilterInstance(conversionService.convert(webRequest.getParameterValues(getSingleQueryParam()), String[].class));
|
||||
}
|
||||
|
||||
static class ProjectLicenseFilterInstance implements FilterInstance {
|
||||
|
||||
private final String[] licenses;
|
||||
|
||||
public ProjectLicenseFilterInstance(String[] licenses) {
|
||||
this.licenses = licenses;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void createSql(StringBuilder sb, SqlStatement<?> q) {
|
||||
sb.append(" AND p.license_type").append(" IN (");
|
||||
for (int i = 0; i < licenses.length; i++) {
|
||||
sb.append(":__licenses__").append(i);
|
||||
if (i + 1 != licenses.length) {
|
||||
sb.append(",");
|
||||
}
|
||||
q.bind("__licenses__" + i, licenses[i]);
|
||||
}
|
||||
sb.append(")");
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "ProjectLicenseFilterInstance{" +
|
||||
"licenses=" + Arrays.toString(licenses) +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
}
|
@ -48,7 +48,7 @@ public class ProjectPlatformFilter implements Filter<ProjectPlatformFilter.Proje
|
||||
|
||||
@Override
|
||||
public void createSql(StringBuilder sb, SqlStatement<?> q) {
|
||||
sb.append("AND v.platform").append(" IN (");
|
||||
sb.append(" AND v.platform").append(" IN (");
|
||||
for (int i = 0; i < platforms.length; i++) {
|
||||
sb.append(":__platform__").append(i);
|
||||
if (i + 1 != platforms.length) {
|
||||
|
@ -38,14 +38,12 @@ public class ProjectQueryFilter implements Filter<ProjectQueryFilterInstance> {
|
||||
|
||||
@Override
|
||||
public void createSql(StringBuilder sb, SqlStatement<?> q) {
|
||||
/*sb.append(" AND (hp.search_words @@ websearch_to_tsquery");
|
||||
sb.append(" AND (hp.search_words @@ websearch_to_tsquery");
|
||||
if (!query.endsWith(" ")) {
|
||||
sb.append("_postfix");
|
||||
}
|
||||
sb.append("('english', :query)").append(")");
|
||||
q.bind("query", query.trim());*/
|
||||
// TODO broken. Full-text search is not implemented in cockroachdb yet. See: https://go.crdb.dev/issue-v/7821/v21.2. Until it's done, we'll just do simple search
|
||||
sb.append(" AND (hp.name ILIKE '%" + query + "%')");
|
||||
q.bind("query", query.trim());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -49,6 +49,7 @@ public interface HangarProjectsDAO {
|
||||
" ps.source," +
|
||||
" ps.support," +
|
||||
" ps.license_name," +
|
||||
" ps.license_type," +
|
||||
" ps.license_url," +
|
||||
" ps.keywords," +
|
||||
" ps.forum_sync," +
|
||||
|
@ -21,13 +21,13 @@ public interface ProjectsDAO {
|
||||
|
||||
@Timestamped
|
||||
@GetGeneratedKeys
|
||||
@SqlUpdate("insert into projects (created_at, name, slug, owner_name, owner_id, category, description, visibility, homepage, issues, source, support, keywords, license_name, license_url, donation_enabled, donation_subject, sponsors) " +
|
||||
"values (:now, :name, :slug, :ownerName,:ownerId, :category, :description, :visibility, :homepage, :issues, :source, :support, :keywords, :licenseName, :licenseUrl, :donationEnabled, :donationSubject, :sponsors)")
|
||||
@SqlUpdate("insert into projects (created_at, name, slug, owner_name, owner_id, category, description, visibility, homepage, issues, source, support, keywords, license_type, license_name, license_url, donation_enabled, donation_subject, sponsors) " +
|
||||
"values (:now, :name, :slug, :ownerName,:ownerId, :category, :description, :visibility, :homepage, :issues, :source, :support, :keywords, :licenseType, :licenseName, :licenseUrl, :donationEnabled, :donationSubject, :sponsors)")
|
||||
ProjectTable insert(@BindBean ProjectTable project);
|
||||
|
||||
@GetGeneratedKeys
|
||||
@SqlUpdate("UPDATE projects SET name = :name, slug = :slug, category = :category, keywords = :keywords, issues = :issues, source = :source, " +
|
||||
"license_name = :licenseName, license_url = :licenseUrl, forum_sync = :forumSync, description = :description, visibility = :visibility, " +
|
||||
"license_type = :licenseType, license_name = :licenseName, license_url = :licenseUrl, forum_sync = :forumSync, description = :description, visibility = :visibility, " +
|
||||
"support = :support, homepage = :homepage, post_id = :postId, topic_id = :topicId, donation_enabled = :donationEnabled, donation_subject = :donationSubject, " +
|
||||
"sponsors = :sponsors WHERE id = :id")
|
||||
ProjectTable update(@BindBean ProjectTable project);
|
||||
|
@ -86,6 +86,7 @@ public interface ProjectsApiDAO {
|
||||
COALESCE(hp.last_updated, hp.created_at) AS last_updated,
|
||||
((EXTRACT(EPOCH FROM COALESCE(hp.last_updated, hp.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.
|
||||
hp.visibility,
|
||||
<relevance>
|
||||
exists(SELECT * FROM project_stars s WHERE s.project_id = p.id AND s.user_id = :requesterId) AS starred,
|
||||
exists(SELECT * FROM project_watchers s WHERE s.project_id = p.id AND s.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,
|
||||
@ -94,6 +95,7 @@ public interface ProjectsApiDAO {
|
||||
p.source,
|
||||
p.support,
|
||||
p.license_name,
|
||||
p.license_type,
|
||||
p.license_url,
|
||||
p.keywords,
|
||||
p.forum_sync,
|
||||
@ -109,12 +111,12 @@ public interface ProjectsApiDAO {
|
||||
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 (hp.visibility = 0 <if(requesterId)>OR (:requesterId = ANY(hp.project_members) AND hp.visibility != 4)<endif>) <endif>
|
||||
ORDER BY <orderBy>
|
||||
<sorters>
|
||||
<offsetLimit>""")
|
||||
@RegisterColumnMapper(PromotedVersionMapper.class)
|
||||
@DefineNamedBindings
|
||||
List<Project> getProjects(@Define boolean seeHidden, Long requesterId, @Define String orderBy,
|
||||
@BindPagination RequestPagination pagination);
|
||||
List<Project> getProjects(@Define boolean seeHidden, Long requesterId,
|
||||
@BindPagination RequestPagination pagination, @Define String relevance);
|
||||
|
||||
// This query can be shorter because it doesnt need all those column values as above does, just a single column for the amount of rows to be counted
|
||||
@UseStringTemplateEngine
|
||||
|
@ -1,7 +1,6 @@
|
||||
package io.papermc.hangar.model.api.requests;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import io.papermc.hangar.controller.extras.ApiUtils;
|
||||
import io.papermc.hangar.controller.extras.pagination.Filter.FilterInstance;
|
||||
import io.swagger.annotations.ApiModelProperty;
|
||||
|
||||
|
@ -31,6 +31,7 @@ public class ProjectTable extends Table implements Visitable, ModelVisible, Proj
|
||||
private String issues;
|
||||
private String source;
|
||||
private String support;
|
||||
private String licenseType;
|
||||
private String licenseName;
|
||||
private String licenseUrl;
|
||||
private boolean forumSync;
|
||||
@ -54,6 +55,7 @@ public class ProjectTable extends Table implements Visitable, ModelVisible, Proj
|
||||
this.source = form.getSettings().getSource();
|
||||
this.support = form.getSettings().getSupport();
|
||||
this.keywords = form.getSettings().getKeywords();
|
||||
this.licenseType = form.getSettings().getLicense().getType();
|
||||
this.licenseName = form.getSettings().getLicense().getName();
|
||||
this.licenseUrl = form.getSettings().getLicense().getUrl();
|
||||
this.donationEnabled = form.getSettings().getDonation().isEnable();
|
||||
@ -76,6 +78,7 @@ public class ProjectTable extends Table implements Visitable, ModelVisible, Proj
|
||||
this.issues = other.issues;
|
||||
this.source = other.source;
|
||||
this.support = other.support;
|
||||
this.licenseType = other.licenseType;
|
||||
this.licenseName = other.licenseName;
|
||||
this.licenseUrl = other.licenseUrl;
|
||||
this.forumSync = other.forumSync;
|
||||
@ -87,7 +90,7 @@ public class ProjectTable extends Table implements Visitable, ModelVisible, Proj
|
||||
@JdbiConstructor
|
||||
public ProjectTable(OffsetDateTime createdAt, long id, String name, String slug, String ownerName, long ownerId, Long topicId,
|
||||
Long postId, @EnumByOrdinal Category category, String description, @EnumByOrdinal Visibility visibility, Collection<String> keywords,
|
||||
String homepage, String issues, String source, String support, String licenseName, String licenseUrl, boolean forumSync,
|
||||
String homepage, String issues, String source, String support, String licenseType, String licenseName, String licenseUrl, boolean forumSync,
|
||||
boolean donationEnabled, String donationSubject, String sponsors) {
|
||||
super(createdAt, id);
|
||||
this.name = name;
|
||||
@ -104,6 +107,7 @@ public class ProjectTable extends Table implements Visitable, ModelVisible, Proj
|
||||
this.issues = issues;
|
||||
this.source = source;
|
||||
this.support = support;
|
||||
this.licenseType = licenseType;
|
||||
this.licenseName = licenseName;
|
||||
this.licenseUrl = licenseUrl;
|
||||
this.forumSync = forumSync;
|
||||
@ -231,6 +235,14 @@ public class ProjectTable extends Table implements Visitable, ModelVisible, Proj
|
||||
this.support = support;
|
||||
}
|
||||
|
||||
public String getLicenseType() {
|
||||
return licenseType;
|
||||
}
|
||||
|
||||
public void setLicenseType(String licenseType) {
|
||||
this.licenseType = licenseType;
|
||||
}
|
||||
|
||||
public String getLicenseName() {
|
||||
return licenseName;
|
||||
}
|
||||
@ -306,6 +318,7 @@ public class ProjectTable extends Table implements Visitable, ModelVisible, Proj
|
||||
", issues='" + issues + '\'' +
|
||||
", source='" + source + '\'' +
|
||||
", support='" + support + '\'' +
|
||||
", licenseType='" + licenseType + '\'' +
|
||||
", licenseName='" + licenseName + '\'' +
|
||||
", licenseUrl='" + licenseUrl + '\'' +
|
||||
", forumSync=" + forumSync +
|
||||
|
@ -1,6 +1,7 @@
|
||||
package io.papermc.hangar.service.api;
|
||||
|
||||
import io.papermc.hangar.HangarComponent;
|
||||
import io.papermc.hangar.controller.extras.pagination.SorterRegistry;
|
||||
import io.papermc.hangar.db.dao.v1.ProjectsApiDAO;
|
||||
import io.papermc.hangar.model.api.PaginatedResult;
|
||||
import io.papermc.hangar.model.api.Pagination;
|
||||
@ -16,8 +17,11 @@ import org.springframework.stereotype.Service;
|
||||
|
||||
import java.time.OffsetDateTime;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@Service
|
||||
public class ProjectsApiService extends HangarComponent {
|
||||
@ -53,37 +57,20 @@ public class ProjectsApiService extends HangarComponent {
|
||||
return new PaginatedResult<>(new Pagination(projectsApiDAO.getProjectWatchersCount(author, slug), pagination), watchers);
|
||||
}
|
||||
|
||||
public PaginatedResult<Project> getProjects(String query, ProjectSortingStrategy sort, boolean orderWithRelevance, RequestPagination pagination) {
|
||||
|
||||
String ordering = sort.getSql();
|
||||
public PaginatedResult<Project> getProjects(String query, boolean orderWithRelevance, RequestPagination pagination) {
|
||||
String relevance = "";
|
||||
if (orderWithRelevance && query != null && !query.isEmpty()) {
|
||||
String relevance = "ts_rank(hp.search_words, websearch_to_tsquery_postfix('english', :query)) DESC";
|
||||
if(query.endsWith(" ")) {
|
||||
relevance = "ts_rank(hp.search_words, websearch_to_tsquery('english', :query)) DESC";
|
||||
relevance = "ts_rank(hp.search_words, websearch_to_tsquery('english', :query)) AS relevance,";
|
||||
} else {
|
||||
relevance = "ts_rank(hp.search_words, websearch_to_tsquery_postfix('english', :query)) AS relevance,";
|
||||
}
|
||||
relevance = "ASC";
|
||||
String orderingFirstHalf;
|
||||
// 1609459200 is the hangar epoch
|
||||
// 86400 seconds to days
|
||||
// 604800 seconds to weeks
|
||||
switch(sort){
|
||||
case STARS: orderingFirstHalf = "hp.stars * "; break;
|
||||
case DOWNLOADS: orderingFirstHalf ="(hp.downloads / 100) * "; break;
|
||||
case VIEWS: orderingFirstHalf ="(hp.views / 200) *"; break;
|
||||
case NEWEST: orderingFirstHalf ="((EXTRACT(EPOCH FROM hp.created_at) - 1609459200) / 86400) *"; break;
|
||||
case UPDATED: orderingFirstHalf ="last_updated_double "; break;
|
||||
case ONLY_RELEVANCE: orderingFirstHalf = ""; break;
|
||||
case RECENT_DOWNLOADS : orderingFirstHalf ="hp.recent_views *"; break;
|
||||
case RECENT_VIEWS: orderingFirstHalf ="hp.recent_downloads *"; break;
|
||||
default:
|
||||
orderingFirstHalf = " "; // Just in case and so that the ide doesn't complain
|
||||
}
|
||||
ordering = orderingFirstHalf + relevance;
|
||||
pagination.getSorters().put("relevance", sb -> sb.append(" relevance DESC"));
|
||||
}
|
||||
|
||||
boolean seeHidden = getGlobalPermissions().has(Permission.SeeHidden);
|
||||
|
||||
List<Project> projects = projectsApiDAO.getProjects(seeHidden, getHangarUserId(), ordering, pagination);
|
||||
List<Project> projects = projectsApiDAO.getProjects(seeHidden, getHangarUserId(), pagination, relevance);
|
||||
return new PaginatedResult<>(new Pagination(projectsApiDAO.countProjects(seeHidden, getHangarUserId(), pagination), pagination), projects);
|
||||
}
|
||||
}
|
||||
|
@ -131,6 +131,7 @@ public class ProjectService extends HangarComponent {
|
||||
if (licenseName == null) {
|
||||
licenseName = settingsForm.getSettings().getLicense().getType();
|
||||
}
|
||||
projectTable.setLicenseName(settingsForm.getSettings().getLicense().getType());
|
||||
projectTable.setLicenseName(licenseName);
|
||||
projectTable.setLicenseUrl(settingsForm.getSettings().getLicense().getUrl());
|
||||
projectTable.setForumSync(settingsForm.getSettings().isForumSync());
|
||||
|
@ -95,6 +95,7 @@ CREATE TABLE projects
|
||||
issues varchar(255),
|
||||
source varchar(255),
|
||||
support varchar(255),
|
||||
license_type varchar(255),
|
||||
license_name varchar(255),
|
||||
license_url varchar(255),
|
||||
forum_sync boolean DEFAULT TRUE NOT NULL,
|
||||
@ -960,6 +961,7 @@ SELECT p.id,
|
||||
p.description,
|
||||
p.name,
|
||||
p.created_at,
|
||||
p.license_type,
|
||||
max(lv.created_at) AS last_updated,
|
||||
to_jsonb(ARRAY(SELECT jsonb_build_object('version_string', tags.version_string, 'tag_name', tags.tag_name,
|
||||
'tag_version', tags.tag_version, 'tag_color',
|
||||
@ -967,21 +969,18 @@ SELECT p.id,
|
||||
FROM tags
|
||||
WHERE tags.project_id = p.id
|
||||
LIMIT 5)) AS promoted_versions,
|
||||
--TODO fix homepage view--
|
||||
-- ((setweight((to_tsvector('english'::regconfig, p.name::text) ||
|
||||
-- to_tsvector('english'::regconfig, regexp_replace(p.name::text, '([a-z])([A-Z]+)'::text,
|
||||
-- '\1_\2'::text, 'g'::text))), 'A'::"char") ||
|
||||
-- setweight(to_tsvector('english'::regconfig, p.description::text), 'B'::"char")) ||
|
||||
-- setweight(to_tsvector('english'::regconfig, array_to_string(p.keywords, ' '::text)), 'C'::"char")) || setweight(
|
||||
-- to_tsvector('english'::regconfig, p.owner_name::text) || to_tsvector('english'::regconfig,
|
||||
-- regexp_replace(
|
||||
-- p.owner_name::text,
|
||||
-- '([a-z])([A-Z]+)'::text,
|
||||
-- '\1_\2'::text,
|
||||
-- 'g'::text)),
|
||||
-- 'D'::"char") AS search_words
|
||||
-- TODO fix homepage view
|
||||
'DUM' AS search_words
|
||||
((setweight((to_tsvector('english'::regconfig, p.name::text) ||
|
||||
to_tsvector('english'::regconfig, regexp_replace(p.name::text, '([a-z])([A-Z]+)'::text,
|
||||
'\1_\2'::text, 'g'::text))), 'A'::"char") ||
|
||||
setweight(to_tsvector('english'::regconfig, p.description::text), 'B'::"char")) ||
|
||||
setweight(to_tsvector('english'::regconfig, array_to_string(p.keywords, ' '::text)), 'C'::"char")) || setweight(
|
||||
to_tsvector('english'::regconfig, p.owner_name::text) || to_tsvector('english'::regconfig,
|
||||
regexp_replace(
|
||||
p.owner_name::text,
|
||||
'([a-z])([A-Z]+)'::text,
|
||||
'\1_\2'::text,
|
||||
'g'::text)),
|
||||
'D'::"char") AS search_words
|
||||
FROM projects p
|
||||
LEFT JOIN project_versions lv ON p.id = lv.project_id
|
||||
JOIN project_members_all pm ON p.id = pm.id
|
||||
@ -1174,69 +1173,69 @@ FROM logged_actions_organization a
|
||||
LEFT JOIN users u ON a.user_id = u.id
|
||||
LEFT JOIN users s ON o.user_id = s.id;
|
||||
|
||||
-- CREATE FUNCTION delete_old_project_version_download_warnings() RETURNS TRIGGER
|
||||
-- LANGUAGE plpgsql
|
||||
-- AS $$
|
||||
-- BEGIN
|
||||
-- DELETE FROM project_version_download_warnings WHERE created_at < current_date - interval '30' day;
|
||||
-- RETURN NEW;
|
||||
-- END
|
||||
-- $$;
|
||||
--
|
||||
-- CREATE TRIGGER clean_old_project_version_download_warnings
|
||||
-- AFTER INSERT
|
||||
-- ON project_version_download_warnings
|
||||
-- EXECUTE PROCEDURE delete_old_project_version_download_warnings();
|
||||
--
|
||||
-- CREATE FUNCTION delete_old_project_version_unsafe_downloads() RETURNS TRIGGER
|
||||
-- LANGUAGE plpgsql
|
||||
-- AS $$
|
||||
-- BEGIN
|
||||
-- DELETE FROM project_version_unsafe_downloads WHERE created_at < current_date - interval '30' day;
|
||||
-- RETURN NEW;
|
||||
-- END
|
||||
-- $$;
|
||||
--
|
||||
-- CREATE TRIGGER clean_old_project_version_unsafe_downloads
|
||||
-- AFTER INSERT
|
||||
-- ON project_version_unsafe_downloads
|
||||
-- EXECUTE PROCEDURE delete_old_project_version_unsafe_downloads();
|
||||
--
|
||||
-- CREATE FUNCTION update_project_name_trigger() RETURNS TRIGGER
|
||||
-- LANGUAGE plpgsql
|
||||
-- AS $$
|
||||
-- BEGIN
|
||||
-- UPDATE projects p SET name = u.name FROM users u WHERE p.id = new.id AND u.id = new.owner_id;
|
||||
-- END;
|
||||
-- $$;
|
||||
--
|
||||
-- CREATE TRIGGER project_owner_name_updater
|
||||
-- AFTER UPDATE
|
||||
-- OF owner_id
|
||||
-- ON projects
|
||||
-- FOR EACH ROW
|
||||
-- WHEN (old.owner_id <> new.owner_id)
|
||||
-- EXECUTE PROCEDURE update_project_name_trigger();
|
||||
--
|
||||
-- CREATE FUNCTION websearch_to_tsquery_postfix(dictionary regconfig, query text) RETURNS tsquery
|
||||
-- IMMUTABLE
|
||||
-- STRICT
|
||||
-- LANGUAGE plpgsql
|
||||
-- AS $$
|
||||
-- DECLARE
|
||||
-- arr TEXT[] := regexp_split_to_array(query, '\s+');
|
||||
-- last TEXT := websearch_to_tsquery('simple', arr[array_length(arr, 1)])::TEXT;
|
||||
-- init TSQUERY := websearch_to_tsquery(dictionary, regexp_replace(query, '\S+$', ''));
|
||||
-- BEGIN
|
||||
-- IF last = '' THEN
|
||||
-- BEGIN
|
||||
-- RETURN init && $2::TSQUERY;
|
||||
-- EXCEPTION
|
||||
-- WHEN SYNTAX_ERROR THEN
|
||||
-- RETURN init && websearch_to_tsquery('');
|
||||
-- END;
|
||||
-- END IF;
|
||||
--
|
||||
-- RETURN init && (websearch_to_tsquery(dictionary, last) || to_tsquery('simple', last || ':*'));
|
||||
-- END;
|
||||
-- $$;
|
||||
CREATE FUNCTION delete_old_project_version_download_warnings() RETURNS TRIGGER
|
||||
LANGUAGE plpgsql
|
||||
AS $$
|
||||
BEGIN
|
||||
DELETE FROM project_version_download_warnings WHERE created_at < current_date - interval '30' day;
|
||||
RETURN NEW;
|
||||
END
|
||||
$$;
|
||||
|
||||
CREATE TRIGGER clean_old_project_version_download_warnings
|
||||
AFTER INSERT
|
||||
ON project_version_download_warnings
|
||||
EXECUTE PROCEDURE delete_old_project_version_download_warnings();
|
||||
|
||||
CREATE FUNCTION delete_old_project_version_unsafe_downloads() RETURNS TRIGGER
|
||||
LANGUAGE plpgsql
|
||||
AS $$
|
||||
BEGIN
|
||||
DELETE FROM project_version_unsafe_downloads WHERE created_at < current_date - interval '30' day;
|
||||
RETURN NEW;
|
||||
END
|
||||
$$;
|
||||
|
||||
CREATE TRIGGER clean_old_project_version_unsafe_downloads
|
||||
AFTER INSERT
|
||||
ON project_version_unsafe_downloads
|
||||
EXECUTE PROCEDURE delete_old_project_version_unsafe_downloads();
|
||||
|
||||
CREATE FUNCTION update_project_name_trigger() RETURNS TRIGGER
|
||||
LANGUAGE plpgsql
|
||||
AS $$
|
||||
BEGIN
|
||||
UPDATE projects p SET name = u.name FROM users u WHERE p.id = new.id AND u.id = new.owner_id;
|
||||
END;
|
||||
$$;
|
||||
|
||||
CREATE TRIGGER project_owner_name_updater
|
||||
AFTER UPDATE
|
||||
OF owner_id
|
||||
ON projects
|
||||
FOR EACH ROW
|
||||
WHEN (old.owner_id <> new.owner_id)
|
||||
EXECUTE PROCEDURE update_project_name_trigger();
|
||||
|
||||
CREATE FUNCTION websearch_to_tsquery_postfix(dictionary regconfig, query text) RETURNS tsquery
|
||||
IMMUTABLE
|
||||
STRICT
|
||||
LANGUAGE plpgsql
|
||||
AS $$
|
||||
DECLARE
|
||||
arr TEXT[] := regexp_split_to_array(query, '\s+');
|
||||
last TEXT := websearch_to_tsquery('simple', arr[array_length(arr, 1)])::TEXT;
|
||||
init TSQUERY := websearch_to_tsquery(dictionary, regexp_replace(query, '\S+$', ''));
|
||||
BEGIN
|
||||
IF last = '' THEN
|
||||
BEGIN
|
||||
RETURN init && $2::TSQUERY;
|
||||
EXCEPTION
|
||||
WHEN SYNTAX_ERROR THEN
|
||||
RETURN init && websearch_to_tsquery('');
|
||||
END;
|
||||
END IF;
|
||||
|
||||
RETURN init && (websearch_to_tsquery(dictionary, last) || to_tsquery('simple', last || ':*'));
|
||||
END;
|
||||
$$;
|
||||
|
Loading…
Reference in New Issue
Block a user