From 85c61c1bc1d3f48760f6e6ee0647811d326c4cc2 Mon Sep 17 00:00:00 2001 From: CalDescent Date: Fri, 21 Jan 2022 10:04:18 +0000 Subject: [PATCH] Added GET /arbitrary/resources/search API Example usage List all websites with a name containing the word "crow" (using default identifier): http://localhost:12391/arbitrary/resources/search?service=WEBSITE&query=crow&default=true&limit=20 List all resources with name or identifier containing the word "crow": http://localhost:12391/arbitrary/resources/search?query=crow&default=false&limit=20 --- .../api/resource/ArbitraryResource.java | 43 ++++++++++++ .../repository/ArbitraryRepository.java | 2 + .../hsqldb/HSQLDBArbitraryRepository.java | 69 +++++++++++++++++++ 3 files changed, 114 insertions(+) diff --git a/src/main/java/org/qortal/api/resource/ArbitraryResource.java b/src/main/java/org/qortal/api/resource/ArbitraryResource.java index 6ce355f9..82618152 100644 --- a/src/main/java/org/qortal/api/resource/ArbitraryResource.java +++ b/src/main/java/org/qortal/api/resource/ArbitraryResource.java @@ -121,6 +121,49 @@ public class ArbitraryResource { } } + @GET + @Path("/resources/search") + @Operation( + summary = "Search arbitrary resources available on chain, optionally filtered by service.\n" + + "If default is set to true, only resources without identifiers will be returned.", + responses = { + @ApiResponse( + content = @Content(mediaType = MediaType.APPLICATION_JSON, schema = @Schema(implementation = ArbitraryResourceInfo.class)) + ) + } + ) + @ApiErrors({ApiError.REPOSITORY_ISSUE}) + public List searchResources( + @QueryParam("service") Service service, + @QueryParam("query") String query, + @Parameter(description = "Default resources (without identifiers) only") @QueryParam("default") Boolean defaultResource, + @Parameter(ref = "limit") @QueryParam("limit") Integer limit, + @Parameter(ref = "offset") @QueryParam("offset") Integer offset, + @Parameter(ref = "reverse") @QueryParam("reverse") Boolean reverse, + @Parameter(description = "Include status") @QueryParam("includestatus") Boolean includeStatus) { + + try (final Repository repository = RepositoryManager.getRepository()) { + + boolean defaultRes = Boolean.TRUE.equals(defaultResource); + + List resources = repository.getArbitraryRepository() + .searchArbitraryResources(service, query, defaultRes, limit, offset, reverse); + + if (resources == null) { + return new ArrayList<>(); + } + + if (includeStatus != null && includeStatus == true) { + resources = this.addStatusToResources(resources); + } + + return resources; + + } catch (DataException e) { + throw ApiExceptionFactory.INSTANCE.createException(request, ApiError.REPOSITORY_ISSUE, e); + } + } + @GET @Path("/resources/names") @Operation( diff --git a/src/main/java/org/qortal/repository/ArbitraryRepository.java b/src/main/java/org/qortal/repository/ArbitraryRepository.java index ba3ba1d8..ea8ae0de 100644 --- a/src/main/java/org/qortal/repository/ArbitraryRepository.java +++ b/src/main/java/org/qortal/repository/ArbitraryRepository.java @@ -26,6 +26,8 @@ public interface ArbitraryRepository { public List getArbitraryResources(Service service, String identifier, String name, boolean defaultResource, Integer limit, Integer offset, Boolean reverse) throws DataException; + public List searchArbitraryResources(Service service, String query, boolean defaultResource, Integer limit, Integer offset, Boolean reverse) throws DataException; + public List getArbitraryResourceCreatorNames(Service service, String identifier, boolean defaultResource, Integer limit, Integer offset, Boolean reverse) throws DataException; diff --git a/src/main/java/org/qortal/repository/hsqldb/HSQLDBArbitraryRepository.java b/src/main/java/org/qortal/repository/hsqldb/HSQLDBArbitraryRepository.java index b0b806b7..662d0075 100644 --- a/src/main/java/org/qortal/repository/hsqldb/HSQLDBArbitraryRepository.java +++ b/src/main/java/org/qortal/repository/hsqldb/HSQLDBArbitraryRepository.java @@ -367,6 +367,75 @@ public class HSQLDBArbitraryRepository implements ArbitraryRepository { } } + @Override + public List searchArbitraryResources(Service service, String query, + boolean defaultResource, Integer limit, Integer offset, Boolean reverse) throws DataException { + StringBuilder sql = new StringBuilder(512); + List bindParams = new ArrayList<>(); + + // For now we are searching anywhere in the fields + // Note that this will bypass any indexes so may not scale well + // Longer term we probably want to copy resources to their own table anyway + String queryWildcard = String.format("%%%s%%", query.toLowerCase()); + + sql.append("SELECT name, service, identifier FROM ArbitraryTransactions WHERE 1=1"); + + if (service != null) { + sql.append(" AND service = "); + sql.append(service.value); + } + + if (defaultResource) { + // Default resource requested - use NULL identifier and search name only + sql.append(" AND LCASE(name) LIKE ? AND identifier IS NULL"); + bindParams.add(queryWildcard); + } + 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(" GROUP BY name, service, identifier ORDER BY name"); + + if (reverse != null && reverse) { + sql.append(" DESC"); + } + + HSQLDBRepository.limitOffsetSql(sql, limit, offset); + + List arbitraryResources = new ArrayList<>(); + + try (ResultSet resultSet = this.repository.checkedExecute(sql.toString(), bindParams.toArray())) { + if (resultSet == null) + return null; + + do { + String nameResult = resultSet.getString(1); + Service serviceResult = Service.valueOf(resultSet.getInt(2)); + String identifierResult = resultSet.getString(3); + + // We should filter out resources without names + if (nameResult == null) { + continue; + } + + ArbitraryResourceInfo arbitraryResourceInfo = new ArbitraryResourceInfo(); + arbitraryResourceInfo.name = nameResult; + arbitraryResourceInfo.service = serviceResult; + arbitraryResourceInfo.identifier = identifierResult; + + arbitraryResources.add(arbitraryResourceInfo); + } while (resultSet.next()); + + return arbitraryResources; + } catch (SQLException e) { + throw new DataException("Unable to fetch arbitrary transactions from repository", e); + } + } + @Override public List getArbitraryResourceCreatorNames(Service service, String identifier, boolean defaultResource, Integer limit, Integer offset, Boolean reverse) throws DataException {