From 2fd5bfb11a320607a7c087595f9d3274cefb52a1 Mon Sep 17 00:00:00 2001 From: CalDescent Date: Sun, 7 May 2023 17:57:14 +0100 Subject: [PATCH] Support title/description metadata searching in `GET /arbitrary/resources/search` "query" searches name, identifier, title and description fields "title" searches title only "description" searches description only All support "&prefix=true", to indicate searching by prefix only. --- .../api/resource/ArbitraryResource.java | 6 ++-- .../repository/ArbitraryRepository.java | 2 +- .../hsqldb/HSQLDBArbitraryRepository.java | 31 ++++++++++++++----- src/main/resources/q-apps/q-apps.js | 2 ++ 4 files changed, 30 insertions(+), 11 deletions(-) diff --git a/src/main/java/org/qortal/api/resource/ArbitraryResource.java b/src/main/java/org/qortal/api/resource/ArbitraryResource.java index 08a9b73c..8762a7f1 100644 --- a/src/main/java/org/qortal/api/resource/ArbitraryResource.java +++ b/src/main/java/org/qortal/api/resource/ArbitraryResource.java @@ -162,9 +162,11 @@ public class ArbitraryResource { @ApiErrors({ApiError.REPOSITORY_ISSUE}) public List searchResources( @QueryParam("service") Service service, - @Parameter(description = "Query (searches both name and identifier fields)") @QueryParam("query") String query, + @Parameter(description = "Query (searches name, identifier, title and description fields)") @QueryParam("query") String query, @Parameter(description = "Identifier (searches identifier field only)") @QueryParam("identifier") String identifier, @Parameter(description = "Name (searches name field only)") @QueryParam("name") List names, + @Parameter(description = "Title (searches title metadata field only)") @QueryParam("title") String title, + @Parameter(description = "Description (searches description metadata field only)") @QueryParam("description") String description, @Parameter(description = "Prefix only (if true, only the beginning of fields are matched)") @QueryParam("prefix") Boolean prefixOnly, @Parameter(description = "Exact match names only (if true, partial name matches are excluded)") @QueryParam("exactmatchnames") Boolean exactMatchNamesOnly, @Parameter(description = "Default resources (without identifiers) only") @QueryParam("default") Boolean defaultResource, @@ -201,7 +203,7 @@ public class ArbitraryResource { } List resources = repository.getArbitraryRepository() - .searchArbitraryResources(service, query, identifier, names, usePrefixOnly, exactMatchNames, + .searchArbitraryResources(service, query, identifier, names, title, description, usePrefixOnly, exactMatchNames, defaultRes, followedOnly, excludeBlocked, includeMetadata, includeStatus, limit, offset, reverse); if (resources == null) { diff --git a/src/main/java/org/qortal/repository/ArbitraryRepository.java b/src/main/java/org/qortal/repository/ArbitraryRepository.java index 572d2a59..590aa3b8 100644 --- a/src/main/java/org/qortal/repository/ArbitraryRepository.java +++ b/src/main/java/org/qortal/repository/ArbitraryRepository.java @@ -39,7 +39,7 @@ public interface ArbitraryRepository { public List getArbitraryResources(Service service, String identifier, List names, boolean defaultResource, Boolean followedOnly, Boolean excludeBlocked, Boolean includeMetadata, Boolean includeStatus, Integer limit, Integer offset, Boolean reverse) throws DataException; - public List searchArbitraryResources(Service service, String query, String identifier, List names, boolean prefixOnly, List namesFilter, boolean defaultResource, Boolean followedOnly, Boolean excludeBlocked, Boolean includeMetadata, Boolean includeStatus, Integer limit, Integer offset, Boolean reverse) throws DataException; + public List searchArbitraryResources(Service service, String query, String identifier, List names, String title, String description, boolean prefixOnly, List namesFilter, boolean defaultResource, Boolean followedOnly, Boolean excludeBlocked, Boolean includeMetadata, Boolean includeStatus, Integer limit, Integer offset, Boolean reverse) throws DataException; // Arbitrary resources cache save/load diff --git a/src/main/java/org/qortal/repository/hsqldb/HSQLDBArbitraryRepository.java b/src/main/java/org/qortal/repository/hsqldb/HSQLDBArbitraryRepository.java index 71428cb6..45706e11 100644 --- a/src/main/java/org/qortal/repository/hsqldb/HSQLDBArbitraryRepository.java +++ b/src/main/java/org/qortal/repository/hsqldb/HSQLDBArbitraryRepository.java @@ -638,7 +638,7 @@ public class HSQLDBArbitraryRepository implements ArbitraryRepository { } @Override - public List searchArbitraryResources(Service service, String query, String identifier, List names, boolean prefixOnly, + public List searchArbitraryResources(Service service, String query, String identifier, List names, String title, String description, boolean prefixOnly, List exactMatchNames, boolean defaultResource, Boolean followedOnly, Boolean excludeBlocked, Boolean includeMetadata, Boolean includeStatus, Integer limit, Integer offset, Boolean reverse) throws DataException { StringBuilder sql = new StringBuilder(512); @@ -669,9 +669,8 @@ public class HSQLDBArbitraryRepository implements ArbitraryRepository { } else { // Non-default resource requested // In this case we search the identifier as well as the name - sql.append(" AND (LCASE(name) LIKE ? OR LCASE(identifier) LIKE ?)"); - bindParams.add(queryWildcard); - bindParams.add(queryWildcard); + sql.append(" AND (LCASE(name) LIKE ? OR LCASE(identifier) LIKE ? OR LCASE(title) LIKE ? OR LCASE(description) LIKE ?)"); + bindParams.add(queryWildcard); bindParams.add(queryWildcard); bindParams.add(queryWildcard); bindParams.add(queryWildcard); } } @@ -683,6 +682,22 @@ public class HSQLDBArbitraryRepository implements ArbitraryRepository { bindParams.add(queryWildcard); } + // Handle title metadata matches + if (title != null) { + // Search anywhere in the title, unless "prefixOnly" has been requested + String queryWildcard = prefixOnly ? String.format("%s%%", title.toLowerCase()) : String.format("%%%s%%", title.toLowerCase()); + sql.append(" AND LCASE(title) LIKE ?"); + bindParams.add(queryWildcard); + } + + // Handle description metadata matches + if (description != null) { + // Search anywhere in the description, unless "prefixOnly" has been requested + String queryWildcard = prefixOnly ? String.format("%s%%", description.toLowerCase()) : String.format("%%%s%%", description.toLowerCase()); + sql.append(" AND LCASE(description) LIKE ?"); + bindParams.add(queryWildcard); + } + // Handle name searches if (names != null && !names.isEmpty()) { sql.append(" AND ("); @@ -763,8 +778,8 @@ public class HSQLDBArbitraryRepository implements ArbitraryRepository { Long updated = resultSet.getLong(7); // Optional metadata fields - String title = resultSet.getString(8); - String description = resultSet.getString(9); + String titleResult = resultSet.getString(8); + String descriptionResult = resultSet.getString(9); String category = resultSet.getString(10); String tag1 = resultSet.getString(11); String tag2 = resultSet.getString(12); @@ -792,8 +807,8 @@ public class HSQLDBArbitraryRepository implements ArbitraryRepository { if (includeMetadata != null && includeMetadata) { // TODO: we could avoid the join altogether ArbitraryResourceMetadata metadata = new ArbitraryResourceMetadata(); - metadata.setTitle(title); - metadata.setDescription(description); + metadata.setTitle(titleResult); + metadata.setDescription(descriptionResult); metadata.setCategory(Category.uncategorizedValueOf(category)); List tags = new ArrayList<>(); diff --git a/src/main/resources/q-apps/q-apps.js b/src/main/resources/q-apps/q-apps.js index 86493b48..6c72a410 100644 --- a/src/main/resources/q-apps/q-apps.js +++ b/src/main/resources/q-apps/q-apps.js @@ -218,6 +218,8 @@ window.addEventListener("message", (event) => { if (data.identifier != null) url = url.concat("&identifier=" + data.identifier); if (data.name != null) url = url.concat("&name=" + data.name); if (data.names != null) data.names.forEach((x, i) => url = url.concat("&name=" + x)); + if (data.title != null) url = url.concat("&title=" + data.title); + if (data.description != null) url = url.concat("&description=" + data.description); if (data.prefix != null) url = url.concat("&prefix=" + new Boolean(data.prefix).toString()); if (data.exactMatchNames != null) url = url.concat("&exactmatchnames=" + new Boolean(data.exactMatchNames).toString()); if (data.default != null) url = url.concat("&default=" + new Boolean(data.default).toString());