mirror of
https://github.com/HangarMC/Hangar.git
synced 2024-11-21 01:21:54 +08:00
ProjectAPIDao expansion (#92)
Signed-off-by: MiniDigger <admin@minidigger.me>
This commit is contained in:
parent
3f8e1b6ff7
commit
7da99df639
@ -1207,3 +1207,28 @@ END;
|
||||
$$;
|
||||
|
||||
alter function logged_action_type_from_int(integer) owner to hangar;
|
||||
|
||||
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;
|
||||
$$;
|
||||
|
||||
alter function websearch_to_tsquery_postfix(regconfig, text) owner to hangar;
|
||||
|
@ -29,8 +29,10 @@ import org.springframework.stereotype.Controller;
|
||||
import java.io.IOException;
|
||||
import java.time.LocalDate;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@Controller
|
||||
public class ProjectsApiController implements ProjectsApi {
|
||||
@ -54,7 +56,7 @@ public class ProjectsApiController implements ProjectsApi {
|
||||
|
||||
@Override
|
||||
@PreAuthorize("@authenticationService.authApiRequest(T(io.papermc.hangar.model.Permission).ViewPublicInfo, T(io.papermc.hangar.controller.util.ApiScope).forGlobal())")
|
||||
public ResponseEntity<PaginatedProjectResult> listProjects(String q, List<Category> categories, List<String> tags, String owner, ProjectSortingStrategy sort, boolean relevance, Long inLimit, Long inOffset, ApiAuthInfo apiAuthInfo) {
|
||||
public ResponseEntity<PaginatedProjectResult> listProjects(String q, List<Category> categories, List<String> tags, String owner, ProjectSortingStrategy sort, boolean orderWithRelevance, Long inLimit, Long inOffset, ApiAuthInfo apiAuthInfo) {
|
||||
// handle input
|
||||
long limit = ApiUtil.limitOrDefault(inLimit, hangarConfig.getProjects().getInitLoad());
|
||||
long offset = ApiUtil.offsetOrZero(inOffset);
|
||||
@ -74,8 +76,10 @@ public class ProjectsApiController implements ProjectsApi {
|
||||
boolean seeHidden = currentUser != null && permissionService.getGlobalPermissions(currentUser.getId()).has(Permission.SeeHidden);
|
||||
Long requesterId = currentUser == null ? null : currentUser.getId();
|
||||
|
||||
String pluginId = null;
|
||||
|
||||
List<Project> projects = projectService.getProjects(
|
||||
null,
|
||||
pluginId,
|
||||
categories,
|
||||
parsedTags,
|
||||
q,
|
||||
@ -83,13 +87,13 @@ public class ProjectsApiController implements ProjectsApi {
|
||||
seeHidden,
|
||||
requesterId,
|
||||
sort,
|
||||
relevance,
|
||||
orderWithRelevance,
|
||||
limit,
|
||||
offset
|
||||
);
|
||||
|
||||
long count = projectService.countProjects(
|
||||
null,
|
||||
pluginId,
|
||||
categories,
|
||||
parsedTags,
|
||||
q,
|
||||
@ -140,5 +144,4 @@ public class ProjectsApiController implements ProjectsApi {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
@ -1,26 +1,72 @@
|
||||
package io.papermc.hangar.db.dao.api;
|
||||
|
||||
import io.papermc.hangar.db.mappers.PromotedVersionMapper;
|
||||
import io.papermc.hangar.model.Category;
|
||||
import io.papermc.hangar.model.generated.Project;
|
||||
import io.papermc.hangar.model.generated.Tag;
|
||||
|
||||
import org.jdbi.v3.core.enums.EnumByOrdinal;
|
||||
import org.jdbi.v3.sqlobject.config.RegisterBeanMapper;
|
||||
import org.jdbi.v3.sqlobject.config.RegisterColumnMapper;
|
||||
import org.jdbi.v3.sqlobject.customizer.AllowUnusedBindings;
|
||||
import org.jdbi.v3.sqlobject.customizer.BindList;
|
||||
import org.jdbi.v3.sqlobject.customizer.Define;
|
||||
import org.jdbi.v3.sqlobject.customizer.DefineNamedBindings;
|
||||
import org.jdbi.v3.sqlobject.statement.SqlQuery;
|
||||
import org.jdbi.v3.stringtemplate4.UseStringTemplateEngine;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import io.papermc.hangar.model.Category;
|
||||
import io.papermc.hangar.model.generated.Project;
|
||||
|
||||
@Repository
|
||||
@RegisterBeanMapper(Project.class)
|
||||
public interface ProjectApiDao {
|
||||
|
||||
@UseStringTemplateEngine
|
||||
@SqlQuery("SELECT p.created_at," +
|
||||
" p.plugin_id," +
|
||||
" p.name," +
|
||||
" p.owner_name," +
|
||||
" p.slug," +
|
||||
" p.promoted_versions," +
|
||||
" p.views," +
|
||||
" p.downloads," +
|
||||
" p.recent_views," +
|
||||
" p.recent_downloads," +
|
||||
" p.stars," +
|
||||
" p.watchers," +
|
||||
" p.category," +
|
||||
" p.description," +
|
||||
" COALESCE(p.last_updated, p.created_at) AS last_updated," +
|
||||
" p.visibility, " +
|
||||
" <if(requesterId)> " +
|
||||
" EXISTS(SELECT * FROM project_stars s WHERE s.project_id = p.id AND s.user_id = :requesterId) AS user_stared, " +
|
||||
" EXISTS(SELECT * FROM project_watchers s WHERE s.project_id = p.id AND s.user_id = :requesterId) AS user_watching, " +
|
||||
" <endif>" +
|
||||
" ps.homepage," +
|
||||
" ps.issues," +
|
||||
" ps.source," +
|
||||
" ps.support," +
|
||||
" ps.license_name," +
|
||||
" ps.license_url," +
|
||||
" ps.forum_sync" +
|
||||
" FROM home_projects p" +
|
||||
" JOIN projects ps ON p.id = ps.id" +
|
||||
" WHERE true " + //Not sure how else to get here a single Where
|
||||
" <if(pluginId)> AND (p.plugin_id = :pluginId) <endif> " +
|
||||
" <if(owner)> AND (p.owner_name = :owner) <endif> " +
|
||||
" <if(!seeHidden)> AND (p.visibility = 1 OR (:requesterId = ANY(p.project_members) AND p.visibility != 5)) <endif> " +
|
||||
" <if(categories)> AND (p.category in (<categories>)) <endif> " +
|
||||
" <if(query)> AND ( <queryStatement> ) <endif> " +
|
||||
" <if(tags)> AND EXISTS ( SELECT pv.tag_name FROM jsonb_to_recordset(p.promoted_versions) " +
|
||||
" AS pv(tag_name TEXT, tag_version TEXT) WHERE (pv.tag_name) in (<tags>) ) <endif> " +
|
||||
" ORDER BY <orderBy> " +
|
||||
" LIMIT :limit" +
|
||||
" OFFSET :offset")
|
||||
@DefineNamedBindings
|
||||
List<Project> listProjects(String pluginId, String owner, boolean seeHidden, Long requesterId, @Define String orderBy,
|
||||
@BindList(onEmpty = BindList.EmptyHandling.NULL_VALUE) @EnumByOrdinal List<Category> categories,
|
||||
@BindList(onEmpty = BindList.EmptyHandling.NULL_VALUE) List<String> tags, //TODO: implement tags with mc_version('data')
|
||||
String query, @Define String queryStatement, long limit, long offset);
|
||||
|
||||
@UseStringTemplateEngine
|
||||
@SqlQuery("SELECT COUNT(*) FROM ( SELECT p.created_at," +
|
||||
" p.plugin_id," +
|
||||
" p.name," +
|
||||
" p.owner_name," +
|
||||
@ -35,7 +81,11 @@ public interface ProjectApiDao {
|
||||
" p.category," +
|
||||
" p.description," +
|
||||
" COALESCE(p.last_updated, p.created_at) AS last_updated," +
|
||||
" p.visibility, " +//"<user_actions_taken>" +
|
||||
" p.visibility, " +
|
||||
" <if(requesterId)> " +
|
||||
" EXISTS(SELECT * FROM project_stars s WHERE s.project_id = p.id AND s.user_id = :requesterId) AS user_stared, " +
|
||||
" EXISTS(SELECT * FROM project_watchers s WHERE s.project_id = p.id AND s.user_id = :requesterId) AS user_watching, " +
|
||||
" <endif> "+
|
||||
" ps.homepage," +
|
||||
" ps.issues," +
|
||||
" ps.source," +
|
||||
@ -44,8 +94,19 @@ public interface ProjectApiDao {
|
||||
" ps.license_url," +
|
||||
" ps.forum_sync" +
|
||||
" FROM home_projects p" +
|
||||
" JOIN projects ps ON p.id = ps.id") // TODO add all the missing filters
|
||||
@RegisterColumnMapper(PromotedVersionMapper.class)
|
||||
@AllowUnusedBindings // todo remove
|
||||
List<Project> listProjects(String pluginId, List<Category> categories, List<Tag> tags, String query, String owner, boolean seeHidden, Long requesterId, String ordering, long limit, long offset);
|
||||
" JOIN projects ps ON p.id = ps.id" +
|
||||
" WHERE true " + //Not sure how else to get here a single Where
|
||||
" <if(pluginId)> AND (p.plugin_id = :pluginId) <endif> " +
|
||||
" <if(owner)> AND (p.owner_name = :owner) <endif> " +
|
||||
" <if(!seeHidden)> AND (p.visibility = 1 OR (:requesterId = ANY(p.project_members) AND p.visibility != 5)) <endif> " +
|
||||
" <if(categories)> AND (p.category in (<categories>)) <endif> " +
|
||||
" <if(query)> AND ( <queryStatement> ) <endif> " +
|
||||
" <if(tags)> AND EXISTS ( SELECT pv.tag_name FROM jsonb_to_recordset(p.promoted_versions) " +
|
||||
" AS pv(tag_name TEXT, tag_version TEXT) WHERE (pv.tag_name) in (<tags>) ) <endif> " +
|
||||
" ) sq")
|
||||
@DefineNamedBindings
|
||||
long countProjects(String pluginId, String owner, @Define boolean seeHidden, Long requesterId,
|
||||
@BindList(onEmpty = BindList.EmptyHandling.NULL_VALUE) @EnumByOrdinal List<Category> categories,
|
||||
@BindList(onEmpty = BindList.EmptyHandling.NULL_VALUE) List<String> tags, //TODO: implement tags with mc_version('data')
|
||||
String query, @Define String queryStatement);
|
||||
}
|
||||
|
@ -194,19 +194,63 @@ public class ProjectService {
|
||||
}
|
||||
|
||||
// TODO move to API daos
|
||||
public List<Project> getProjects(String pluginId, List<Category> categories, List<Tag> tags, String query, String owner, boolean seeHidden, Long requesterId, ProjectSortingStrategy sort, boolean relevance, long limit, long offset) {
|
||||
String ordering;
|
||||
if (relevance && query != null && !query.isEmpty()) {
|
||||
// TODO implement ordering by relevance, see APIVV2Queries#199
|
||||
ordering = sort.getSql();
|
||||
} else {
|
||||
ordering = sort.getSql();
|
||||
public List<Project> getProjects(String pluginId, List<Category> categories, List<Tag> tags, String query, String owner, boolean seeHidden, Long requesterId, ProjectSortingStrategy sort, boolean orderWithRelevance, long limit, long offset) {
|
||||
String ordering = sort.getSql();
|
||||
if (orderWithRelevance && query != null && !query.isEmpty()) {
|
||||
String relevance = "ts_rank(p.search_words, websearch_to_tsquery_postfix('english', :query)) DESC";
|
||||
if(query.endsWith(" ")) {
|
||||
relevance = "ts_rank(p.search_words, websearch_to_tsquery('english', :query)) DESC";
|
||||
}
|
||||
String orderingFirstHalf;
|
||||
// 1483056000 is the Ore epoch
|
||||
// 86400 seconds to days
|
||||
// 604800 seconds to weeks
|
||||
switch(sort){
|
||||
case STARS: orderingFirstHalf = "p.starts * "; break;
|
||||
case DOWNLOADS: orderingFirstHalf ="(p.downloads / 100) * "; break;
|
||||
case VIEWS: orderingFirstHalf ="(p.views / 200) *"; break;
|
||||
case NEWEST: orderingFirstHalf ="((EXTRACT(EPOCH FROM p.created_at) - 1483056000) / 86400) *"; break;
|
||||
case UPDATED: orderingFirstHalf ="((EXTRACT(EPOCH FROM p.last_updated) - 1483056000) / 604800) *"; break;
|
||||
case ONLY_RELEVANCE: orderingFirstHalf =""; break;
|
||||
case RECENT_DOWNLOADS : orderingFirstHalf ="p.recent_views *"; break;
|
||||
case RECENT_VIEWS: orderingFirstHalf ="p.recent_downloads*"; break;
|
||||
default:
|
||||
orderingFirstHalf = " "; //Just in case and so that the ide doesnt complain
|
||||
}
|
||||
ordering = orderingFirstHalf + relevance;
|
||||
}
|
||||
|
||||
return projectApiDao.get().listProjects(pluginId, categories, tags, query, owner, seeHidden, requesterId, ordering, limit, offset);
|
||||
return projectApiDao.get().listProjects(pluginId, owner, seeHidden, requesterId, ordering, categories, getTagsNames(tags), trimQuery(query), getQueryStatement(query), limit, offset);
|
||||
}
|
||||
|
||||
public long countProjects(String pluginId, List<Category> categories, List<Tag> parsedTags, String query, String owner, boolean seeHidden, Long requesterId) {
|
||||
return 1; // TODO count projects query
|
||||
public long countProjects(String pluginId, List<Category> categories, List<Tag> tags, String query, String owner, boolean seeHidden, Long requesterId) {
|
||||
return projectApiDao.get().countProjects(pluginId, owner, seeHidden, requesterId, categories, getTagsNames(tags), trimQuery(query), getQueryStatement(query));
|
||||
}
|
||||
|
||||
private String trimQuery(String query){
|
||||
String trimmedQuery = null;
|
||||
if(query != null && !query.isBlank()) {
|
||||
trimmedQuery = query.trim(); //Ore#APIV2Queries line 169 && 200
|
||||
}
|
||||
return trimmedQuery;
|
||||
}
|
||||
|
||||
private String getQueryStatement(String query){
|
||||
String queryStatement = null;
|
||||
if(query != null && !query.isBlank()){
|
||||
if(query.endsWith(" ")) {
|
||||
queryStatement = "p.search_words @@ websearch_to_tsquery('english', :query)";
|
||||
} else {
|
||||
queryStatement = "p.search_words @@ websearch_to_tsquery_postfix('english', :query)";
|
||||
}
|
||||
}
|
||||
return queryStatement;
|
||||
}
|
||||
|
||||
private List<String> getTagsNames(List<Tag> tags){
|
||||
return tags == null ? null : tags.stream().filter(tag -> tag.getData() == null).map(Tag::getName).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
private List<String> getTagsNamesAndVersion(List<Tag> tags){
|
||||
return tags == null ? null : tags.stream().filter(tag -> tag.getData() != null).map(tag -> " (" + tag.getName() + "," + tag.getData() + ") ").collect(Collectors.toList());
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user