diff --git a/src/main/java/com/onixbyte/deltaforceguide/controller/ModificationController.java b/src/main/java/com/onixbyte/deltaforceguide/controller/ModificationController.java index d047263..816adda 100644 --- a/src/main/java/com/onixbyte/deltaforceguide/controller/ModificationController.java +++ b/src/main/java/com/onixbyte/deltaforceguide/controller/ModificationController.java @@ -15,6 +15,8 @@ import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; +import java.util.List; + @Validated @RestController @RequestMapping("/modifications") @@ -32,9 +34,10 @@ public class ModificationController { @RequestParam(defaultValue = "20") @Min(1) @Max(100) int size, @RequestParam(required = false) @Positive Long firearmId, @RequestParam(defaultValue = "id") String sortBy, - @RequestParam(defaultValue = "DESC") Sort.Direction direction + @RequestParam(defaultValue = "DESC") Sort.Direction direction, + @RequestParam(required = false) List tags ) { - return modificationService.pageQuery(firearmId, PageRequest.of(page, size, Sort.by(direction, sortBy))); + return modificationService.pageQuery(firearmId, tags, PageRequest.of(page, size, Sort.by(direction, sortBy))); } @GetMapping("/{id}") @@ -42,4 +45,3 @@ public class ModificationController { return modificationService.queryById(id); } } - diff --git a/src/main/java/com/onixbyte/deltaforceguide/controller/TagController.java b/src/main/java/com/onixbyte/deltaforceguide/controller/TagController.java new file mode 100644 index 0000000..41230aa --- /dev/null +++ b/src/main/java/com/onixbyte/deltaforceguide/controller/TagController.java @@ -0,0 +1,25 @@ +package com.onixbyte.deltaforceguide.controller; + +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; +import com.onixbyte.deltaforceguide.service.ModificationService; + +import java.util.List; + +@RestController +@RequestMapping("/tags") +public class TagController { + + private final ModificationService modificationService; + + public TagController(ModificationService modificationService) { + this.modificationService = modificationService; + } + + @GetMapping + public List getTags(@RequestParam(required = false) Long firearmId) { + return modificationService.findAllTags(firearmId); + } +} diff --git a/src/main/java/com/onixbyte/deltaforceguide/repository/ModificationRepository.java b/src/main/java/com/onixbyte/deltaforceguide/repository/ModificationRepository.java index f81f73e..5fd88d4 100644 --- a/src/main/java/com/onixbyte/deltaforceguide/repository/ModificationRepository.java +++ b/src/main/java/com/onixbyte/deltaforceguide/repository/ModificationRepository.java @@ -5,8 +5,11 @@ import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; import org.springframework.data.jpa.repository.EntityGraph; import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.query.Param; import org.springframework.stereotype.Repository; +import java.util.List; import java.util.Optional; @Repository @@ -21,6 +24,20 @@ public interface ModificationRepository extends JpaRepository findById(Long id); + + @Query(value = """ + SELECT * FROM modification m + WHERE (:firearmId IS NULL OR m.firearm_id = :firearmId) + AND (CAST(:tagsJson AS text) IS NULL OR cast(m.tags as jsonb) @> cast(CAST(:tagsJson AS text) as jsonb)) + """, + countQuery = """ + SELECT count(*) FROM modification m + WHERE (:firearmId IS NULL OR m.firearm_id = :firearmId) + AND (CAST(:tagsJson AS text) IS NULL OR cast(m.tags as jsonb) @> cast(CAST(:tagsJson AS text) as jsonb)) + """, + nativeQuery = true) + Page pageQueryByFirearmAndTags(@Param("firearmId") Long firearmId, @Param("tagsJson") String tagsJson, Pageable pageable); + + @Query(value = "SELECT DISTINCT jsonb_array_elements_text(cast(tags as jsonb)) FROM modification WHERE (:firearmId IS NULL OR firearm_id = :firearmId)", nativeQuery = true) + List findAllTags(@Param("firearmId") Long firearmId); } - - diff --git a/src/main/java/com/onixbyte/deltaforceguide/service/ModificationService.java b/src/main/java/com/onixbyte/deltaforceguide/service/ModificationService.java index b1d9cc1..4da45d3 100644 --- a/src/main/java/com/onixbyte/deltaforceguide/service/ModificationService.java +++ b/src/main/java/com/onixbyte/deltaforceguide/service/ModificationService.java @@ -4,6 +4,8 @@ import com.onixbyte.deltaforceguide.domain.dto.ModificationResponse; import com.onixbyte.deltaforceguide.domain.dto.PageResponse; import com.onixbyte.deltaforceguide.domain.entity.Modification; import com.onixbyte.deltaforceguide.repository.ModificationRepository; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; import org.springframework.http.HttpStatus; @@ -11,20 +13,36 @@ import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.springframework.web.server.ResponseStatusException; +import java.util.List; + @Service public class ModificationService { private final ModificationRepository modificationRepository; + private final ObjectMapper objectMapper; - public ModificationService(ModificationRepository modificationRepository) { + public ModificationService(ModificationRepository modificationRepository, ObjectMapper objectMapper) { this.modificationRepository = modificationRepository; + this.objectMapper = objectMapper; } @Transactional(readOnly = true) - public PageResponse pageQuery(Long firearmId, Pageable pageable) { - Page page = firearmId == null - ? modificationRepository.findAllBy(pageable) - : modificationRepository.findAllByFirearm_Id(firearmId, pageable); + public PageResponse pageQuery(Long firearmId, List tags, Pageable pageable) { + String tagsJson = null; + if (tags != null && !tags.isEmpty()) { + try { + tagsJson = objectMapper.writeValueAsString(tags); + } catch (JsonProcessingException e) { + throw new RuntimeException("Failed to serialize tags", e); + } + } + + Page page; + if (tagsJson != null || firearmId != null) { + page = modificationRepository.pageQueryByFirearmAndTags(firearmId, tagsJson, pageable); + } else { + page = modificationRepository.findAllBy(pageable); + } return PageResponse.from(page.map(ModificationResponse::from)); } @@ -35,5 +53,9 @@ public class ModificationService { .map(ModificationResponse::from) .orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND, "Modification not found: " + id)); } -} + @Transactional(readOnly = true) + public List findAllTags(Long firearmId) { + return modificationRepository.findAllTags(firearmId); + } +}