Careeroza
Real World Example
Myntra-style Filter Sidebar
Fashion apps like Myntra and Ajio have a sidebar with category, brand, price range, discount and "in stock" filters, any combination of which can be active. Behind it: a Specification-based search returning a DTO page, plus a projection query for the filter counts ("Brand: Puma (124)").
@Service
@RequiredArgsConstructor
@Transactional(readOnly = true)
public class CatalogSearchService {
private final ProductRepository products;
private final EntityManager em;
public Page<ProductCardDto> search(SearchRequest r, Pageable pageable) {
Specification<Product> spec = Specification.allOf(Stream.of(
r.category() == null ? null : ProductSpecs.inCategory(r.category()),
r.brands().isEmpty() ? null : ProductSpecs.brandIn(r.brands()),
r.min() == null ? null : ProductSpecs.priceAtLeast(r.min()),
r.max() == null ? null : ProductSpecs.priceAtMost(r.max()),
r.inStock() ? ProductSpecs.inStock() : null)
.filter(Objects::nonNull).toList());
return products.findAll(spec, pageable).map(ProductCardDto::from);
}
public List<FacetCount> brandFacets(String category) {
return em.createQuery("""
select new com.shopease.catalog.dto.FacetCount(p.brand, count(p))
from Product p
where p.category.name = :category and p.stock > 0
group by p.brand
order by count(p) desc
""", FacetCount.class)
.setParameter("category", category)
.getResultList();
}
}
public record FacetCount(String value, long count) {}The Response
Example
curl "localhost:8080/api/catalog/search?category=Shoes&brands=Puma,Nike&max=4000&inStock=true&size=2&sort=price,desc"Example output
HTTP/1.1 200 OK
Content-Type: application/json
{
"content": [
{"id": 311, "name": "Puma Softride Running Shoes", "price": 3999.00},
{"id": 288, "name": "Nike Revolution 6", "price": 3695.00}
],
"page": {"size": 2, "number": 0, "totalElements": 37, "totalPages": 19},
"facets": {"brand": [{"value": "Puma", "count": 124}, {"value": "Nike", "count": 98}]}
}Which Query Tool for Which Job
| Need | Tool |
|---|---|
| Simple lookup by one or two fields | derived query method |
| Fixed query with joins/aggregates | @Query JPQL |
| Read-only list for an API | DTO / interface projection |
| Optional, combinable filters | Specifications (or Criteria API) |
| Database-specific SQL, reports | native query |
| Bulk change | @Modifying update/delete |