From 780e8ac7da7f3a5fcac776465c83852992861114 Mon Sep 17 00:00:00 2001 From: smagles Date: Sat, 2 Aug 2025 15:47:36 +0200 Subject: [PATCH] Migrate price from Double to Long --- .../database/entity/Advertisement.java | 2 +- .../entity/AdvertisementDocument.java | 2 +- ...stinctTopCategorySpecificationFactory.java | 19 ---- ...vertisementSearchSpecificationFactory.java | 28 ------ .../routing/controller/FilterController.java | 28 ------ .../routing/dto/AdvertisementDto.java | 5 +- .../dto/AdvertisementSearchResultDto.java | 9 +- .../everybuy/routing/dto/PriceRangeDto.java | 9 +- .../AdvertisementSearchParametersDto.java | 4 +- .../request/CreateAdvertisementRequest.java | 4 +- .../request/UpdateAdvertisementRequest.java | 4 +- .../AdvertisementWithStatisticResponse.java | 5 +- .../response/CreateAdvertisementResponse.java | 5 +- .../FavouriteAdvertisementResponse.java | 6 +- .../FilteredAdvertisementsResponse.java | 6 +- .../response/UpdateAdvertisementResponse.java | 6 +- .../routing/dto/util/PriceSerializer.java | 16 --- .../filter/FilterAdvertisementService.java | 74 -------------- .../filter/FilterPriceRangeService.java | 45 --------- ...ElasticAdvertisementReindexingService.java | 97 ++++++++++--------- ...lasticSearchPriceAggregationExtractor.java | 20 ++-- ...=> ElasticSearchCategoryQueryBuilder.java} | 2 +- .../ElasticSearchCategoryService.java | 4 +- .../V2025.08.02.002__fix_price_values.sql | 1 + .../V2025.08.02__change_price_type.sql | 2 + 25 files changed, 83 insertions(+), 320 deletions(-) delete mode 100644 src/main/java/ua/everybuy/database/repository/advertisement/spec/factory/AdvertisementDistinctTopCategorySpecificationFactory.java delete mode 100644 src/main/java/ua/everybuy/database/repository/advertisement/spec/factory/AdvertisementSearchSpecificationFactory.java delete mode 100644 src/main/java/ua/everybuy/routing/controller/FilterController.java delete mode 100644 src/main/java/ua/everybuy/routing/dto/util/PriceSerializer.java delete mode 100644 src/main/java/ua/everybuy/service/advertisement/filter/FilterAdvertisementService.java delete mode 100644 src/main/java/ua/everybuy/service/advertisement/filter/FilterPriceRangeService.java rename src/main/java/ua/everybuy/service/advertisement/search/category/{ElasticSearchCategoryAggregationQueryBuilder.java => ElasticSearchCategoryQueryBuilder.java} (96%) create mode 100644 src/main/resources/db/migration/V2025.08.02.002__fix_price_values.sql create mode 100644 src/main/resources/db/migration/V2025.08.02__change_price_type.sql diff --git a/src/main/java/ua/everybuy/database/entity/Advertisement.java b/src/main/java/ua/everybuy/database/entity/Advertisement.java index 51cc5bb..8e00702 100644 --- a/src/main/java/ua/everybuy/database/entity/Advertisement.java +++ b/src/main/java/ua/everybuy/database/entity/Advertisement.java @@ -27,7 +27,7 @@ public class Advertisement { private String description; @Column(name = "price", nullable = false, length = 55) - private Double price; + private Long price; @Column(name = "creation_date", nullable = false) @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) diff --git a/src/main/java/ua/everybuy/database/entity/AdvertisementDocument.java b/src/main/java/ua/everybuy/database/entity/AdvertisementDocument.java index 46e5722..3a0db0a 100644 --- a/src/main/java/ua/everybuy/database/entity/AdvertisementDocument.java +++ b/src/main/java/ua/everybuy/database/entity/AdvertisementDocument.java @@ -13,7 +13,7 @@ public class AdvertisementDocument { private Long id; private String title; private String description; - private Double price; + private Long price; private Date creationDate; private Date updateDate; private Boolean isEnabled; diff --git a/src/main/java/ua/everybuy/database/repository/advertisement/spec/factory/AdvertisementDistinctTopCategorySpecificationFactory.java b/src/main/java/ua/everybuy/database/repository/advertisement/spec/factory/AdvertisementDistinctTopCategorySpecificationFactory.java deleted file mode 100644 index e1b1ecf..0000000 --- a/src/main/java/ua/everybuy/database/repository/advertisement/spec/factory/AdvertisementDistinctTopCategorySpecificationFactory.java +++ /dev/null @@ -1,19 +0,0 @@ -package ua.everybuy.database.repository.advertisement.spec.factory; - -import org.springframework.data.jpa.domain.Specification; -import org.springframework.stereotype.Component; -import ua.everybuy.database.entity.Advertisement; -import ua.everybuy.database.repository.advertisement.spec.AdvertisementSearchSpecifications; - -@Component -public class AdvertisementDistinctTopCategorySpecificationFactory { - private static final double SIMILARITY_THRESHOLD = 0.3; - - public Specification createSpecification(String keyword) { - return Specification - .where(AdvertisementSearchSpecifications.isEnabled()) - .and(AdvertisementSearchSpecifications.fetchAllRelations()) - .and(AdvertisementSearchSpecifications.hasSimilarTitle(keyword, SIMILARITY_THRESHOLD)) - .and(AdvertisementSearchSpecifications.distinctByCategory()); - } -} diff --git a/src/main/java/ua/everybuy/database/repository/advertisement/spec/factory/AdvertisementSearchSpecificationFactory.java b/src/main/java/ua/everybuy/database/repository/advertisement/spec/factory/AdvertisementSearchSpecificationFactory.java deleted file mode 100644 index 43018dc..0000000 --- a/src/main/java/ua/everybuy/database/repository/advertisement/spec/factory/AdvertisementSearchSpecificationFactory.java +++ /dev/null @@ -1,28 +0,0 @@ -package ua.everybuy.database.repository.advertisement.spec.factory; - -import org.springframework.data.jpa.domain.Specification; -import org.springframework.stereotype.Component; -import ua.everybuy.database.entity.Advertisement; -import ua.everybuy.database.repository.advertisement.spec.AdvertisementSearchSpecifications; -import ua.everybuy.routing.dto.request.AdvertisementSearchParametersDto; - -@Component -public class AdvertisementSearchSpecificationFactory { - private static final double SIMILARITY_THRESHOLD = 0.3; - - public Specification createSpecification(AdvertisementSearchParametersDto params) { - return Specification - .where(AdvertisementSearchSpecifications.isEnabled()) - .and(AdvertisementSearchSpecifications.fetchAllRelations()) - .and(AdvertisementSearchSpecifications.hasMinPrice(params.getMinPrice())) - .and(AdvertisementSearchSpecifications.hasMaxPrice(params.getMaxPrice())) - .and(AdvertisementSearchSpecifications.belongsToCity(params.getCityId())) - .and(AdvertisementSearchSpecifications.belongsToRegion(params.getRegionId())) - .and(AdvertisementSearchSpecifications.belongsToTopSubCategory(params.getTopSubCategoryId())) - .and(AdvertisementSearchSpecifications.belongsToLowSubCategory(params.getLowSubCategoryId())) - .and(AdvertisementSearchSpecifications.belongsToCategory(params.getCategoryId())) - .and(AdvertisementSearchSpecifications.hasProductType(params.getProductType())) - .and(AdvertisementSearchSpecifications.hasSection(params.getSection())) - .and(AdvertisementSearchSpecifications.hasSimilarTitle(params.getKeyword(), SIMILARITY_THRESHOLD)); - } -} diff --git a/src/main/java/ua/everybuy/routing/controller/FilterController.java b/src/main/java/ua/everybuy/routing/controller/FilterController.java deleted file mode 100644 index 141249c..0000000 --- a/src/main/java/ua/everybuy/routing/controller/FilterController.java +++ /dev/null @@ -1,28 +0,0 @@ -package ua.everybuy.routing.controller; - -import jakarta.validation.Valid; -import lombok.RequiredArgsConstructor; -import org.springframework.validation.annotation.Validated; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RequestParam; -import ua.everybuy.service.advertisement.filter.FilterAdvertisementService; -import org.springframework.web.bind.annotation.RestController; -import ua.everybuy.routing.dto.request.AdvertisementSearchParametersDto; -import ua.everybuy.routing.dto.AdvertisementSearchResultDto; - -@RestController -@RequiredArgsConstructor -@RequestMapping("/product") -@Validated -public class FilterController { - private final FilterAdvertisementService filterAdvertisementService; - - @GetMapping("/filter") - public AdvertisementSearchResultDto doFilter( - @Valid AdvertisementSearchParametersDto searchParametersDto, - @RequestParam(defaultValue = "1") int page, - @RequestParam(defaultValue = "20") int size) { - return filterAdvertisementService.getFilteredAdvertisements(searchParametersDto, page, size); - } -} diff --git a/src/main/java/ua/everybuy/routing/dto/AdvertisementDto.java b/src/main/java/ua/everybuy/routing/dto/AdvertisementDto.java index c88c588..5f57073 100644 --- a/src/main/java/ua/everybuy/routing/dto/AdvertisementDto.java +++ b/src/main/java/ua/everybuy/routing/dto/AdvertisementDto.java @@ -1,12 +1,10 @@ package ua.everybuy.routing.dto; -import com.fasterxml.jackson.databind.annotation.JsonSerialize; import lombok.Builder; import lombok.Getter; import lombok.Setter; import ua.everybuy.database.entity.Category; import ua.everybuy.database.entity.City; -import ua.everybuy.routing.dto.util.PriceSerializer; import java.time.LocalDateTime; import java.util.List; import java.util.Set; @@ -18,8 +16,7 @@ public class AdvertisementDto { private Long id; private String title; private String description; - @JsonSerialize(using = PriceSerializer.class) - private Double price; + private Long price; private LocalDateTime creationDate; private Boolean isEnabled; private Boolean isNegotiable; diff --git a/src/main/java/ua/everybuy/routing/dto/AdvertisementSearchResultDto.java b/src/main/java/ua/everybuy/routing/dto/AdvertisementSearchResultDto.java index 6f8722f..3dbf26b 100644 --- a/src/main/java/ua/everybuy/routing/dto/AdvertisementSearchResultDto.java +++ b/src/main/java/ua/everybuy/routing/dto/AdvertisementSearchResultDto.java @@ -1,13 +1,10 @@ package ua.everybuy.routing.dto; import java.util.List; - -import com.fasterxml.jackson.databind.annotation.JsonSerialize; import lombok.Builder; import lombok.Getter; import lombok.Setter; import ua.everybuy.routing.dto.response.FilteredAdvertisementsResponse; -import ua.everybuy.routing.dto.util.PriceSerializer; @Getter @Setter @@ -15,9 +12,7 @@ public class AdvertisementSearchResultDto { private long totalAdvertisements; private int totalPages; - @JsonSerialize(using = PriceSerializer.class) - private Double minPrice; - @JsonSerialize(using = PriceSerializer.class) - private Double maxPrice; + private Long minPrice; + private Long maxPrice; private List advertisements; } diff --git a/src/main/java/ua/everybuy/routing/dto/PriceRangeDto.java b/src/main/java/ua/everybuy/routing/dto/PriceRangeDto.java index 8e4f0c5..1cd6f9c 100644 --- a/src/main/java/ua/everybuy/routing/dto/PriceRangeDto.java +++ b/src/main/java/ua/everybuy/routing/dto/PriceRangeDto.java @@ -1,13 +1,10 @@ package ua.everybuy.routing.dto; -import com.fasterxml.jackson.databind.annotation.JsonSerialize; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Getter; import lombok.NoArgsConstructor; import lombok.Setter; -import org.springframework.context.annotation.Bean; -import ua.everybuy.routing.dto.util.PriceSerializer; @Getter @Setter @@ -15,8 +12,6 @@ @NoArgsConstructor @Builder public class PriceRangeDto { - @JsonSerialize(using = PriceSerializer.class) - private Double minPrice; - @JsonSerialize(using = PriceSerializer.class) - private Double maxPrice; + private Long minPrice; + private Long maxPrice; } diff --git a/src/main/java/ua/everybuy/routing/dto/request/AdvertisementSearchParametersDto.java b/src/main/java/ua/everybuy/routing/dto/request/AdvertisementSearchParametersDto.java index 2230860..6374e68 100644 --- a/src/main/java/ua/everybuy/routing/dto/request/AdvertisementSearchParametersDto.java +++ b/src/main/java/ua/everybuy/routing/dto/request/AdvertisementSearchParametersDto.java @@ -15,9 +15,9 @@ @ToString public class AdvertisementSearchParametersDto { @Min(0) - private Double minPrice; + private Long minPrice; @Min(0) - private Double maxPrice; + private Long maxPrice; private Long regionId; private Long cityId; private Long topSubCategoryId; diff --git a/src/main/java/ua/everybuy/routing/dto/request/CreateAdvertisementRequest.java b/src/main/java/ua/everybuy/routing/dto/request/CreateAdvertisementRequest.java index 50947b0..3a5bac8 100644 --- a/src/main/java/ua/everybuy/routing/dto/request/CreateAdvertisementRequest.java +++ b/src/main/java/ua/everybuy/routing/dto/request/CreateAdvertisementRequest.java @@ -17,8 +17,8 @@ public record CreateAdvertisementRequest( String description, @NotNull(message = "Price is required") - @DecimalMin(value = "0.0", inclusive = true, message = "Price must be a positive number") - Double price, + @Min(value = 0, message = "Price must be a positive number") + Long price, @NotNull(message = "City ID is required") Long cityId, diff --git a/src/main/java/ua/everybuy/routing/dto/request/UpdateAdvertisementRequest.java b/src/main/java/ua/everybuy/routing/dto/request/UpdateAdvertisementRequest.java index 87d049b..9a457ba 100644 --- a/src/main/java/ua/everybuy/routing/dto/request/UpdateAdvertisementRequest.java +++ b/src/main/java/ua/everybuy/routing/dto/request/UpdateAdvertisementRequest.java @@ -17,8 +17,8 @@ public record UpdateAdvertisementRequest( String description, @NotNull(message = "Price is required") - @DecimalMin(value = "0.0", inclusive = true, message = "Price must be a positive number") - Double price, + @Min(value = 0, message = "Price must be a positive number") + Long price, @NotNull(message = "City ID is required") Long cityId, diff --git a/src/main/java/ua/everybuy/routing/dto/response/AdvertisementWithStatisticResponse.java b/src/main/java/ua/everybuy/routing/dto/response/AdvertisementWithStatisticResponse.java index 6af2257..bc5287f 100644 --- a/src/main/java/ua/everybuy/routing/dto/response/AdvertisementWithStatisticResponse.java +++ b/src/main/java/ua/everybuy/routing/dto/response/AdvertisementWithStatisticResponse.java @@ -1,9 +1,7 @@ package ua.everybuy.routing.dto.response; -import com.fasterxml.jackson.databind.annotation.JsonSerialize; import lombok.Getter; import lombok.Setter; -import ua.everybuy.routing.dto.util.PriceSerializer; @Getter @Setter @@ -12,8 +10,7 @@ public class AdvertisementWithStatisticResponse { private String section; private String title; private String productType; - @JsonSerialize(using = PriceSerializer.class) - private Double price; + private Long price; private Long userId; private String mainPhotoUrl; private Integer favouriteCount; diff --git a/src/main/java/ua/everybuy/routing/dto/response/CreateAdvertisementResponse.java b/src/main/java/ua/everybuy/routing/dto/response/CreateAdvertisementResponse.java index f04e780..2108556 100644 --- a/src/main/java/ua/everybuy/routing/dto/response/CreateAdvertisementResponse.java +++ b/src/main/java/ua/everybuy/routing/dto/response/CreateAdvertisementResponse.java @@ -1,9 +1,7 @@ package ua.everybuy.routing.dto.response; -import com.fasterxml.jackson.databind.annotation.JsonSerialize; import lombok.Getter; import lombok.Setter; -import ua.everybuy.routing.dto.util.PriceSerializer; import java.time.LocalDateTime; import java.util.List; import java.util.Set; @@ -14,8 +12,7 @@ public class CreateAdvertisementResponse { private Long id; private String title; private String description; - @JsonSerialize(using = PriceSerializer.class) - private Double price; + private Long price; private LocalDateTime creationDate; private Boolean isEnabled; private Boolean isNegotiable; diff --git a/src/main/java/ua/everybuy/routing/dto/response/FavouriteAdvertisementResponse.java b/src/main/java/ua/everybuy/routing/dto/response/FavouriteAdvertisementResponse.java index 888a960..75fd26b 100644 --- a/src/main/java/ua/everybuy/routing/dto/response/FavouriteAdvertisementResponse.java +++ b/src/main/java/ua/everybuy/routing/dto/response/FavouriteAdvertisementResponse.java @@ -1,14 +1,11 @@ package ua.everybuy.routing.dto.response; -import com.fasterxml.jackson.databind.annotation.JsonSerialize; import lombok.AllArgsConstructor; import lombok.Getter; import lombok.Setter; import ua.everybuy.database.entity.Advertisement; import ua.everybuy.database.entity.Category; import ua.everybuy.database.entity.City; -import ua.everybuy.routing.dto.util.PriceSerializer; - import java.time.LocalDateTime; @Getter @@ -21,8 +18,7 @@ public class FavouriteAdvertisementResponse { private String mainPhotoUrl; private String title; private Advertisement.ProductType productType; - @JsonSerialize(using = PriceSerializer.class) - private Double price; + private Long price; private LocalDateTime updateDate; private Category category; private City city; diff --git a/src/main/java/ua/everybuy/routing/dto/response/FilteredAdvertisementsResponse.java b/src/main/java/ua/everybuy/routing/dto/response/FilteredAdvertisementsResponse.java index 5da3078..9fb536a 100644 --- a/src/main/java/ua/everybuy/routing/dto/response/FilteredAdvertisementsResponse.java +++ b/src/main/java/ua/everybuy/routing/dto/response/FilteredAdvertisementsResponse.java @@ -1,14 +1,11 @@ package ua.everybuy.routing.dto.response; -import com.fasterxml.jackson.databind.annotation.JsonSerialize; import lombok.Getter; import lombok.Setter; import ua.everybuy.database.entity.Advertisement; import ua.everybuy.database.entity.City; import ua.everybuy.routing.dto.CategoryDto; import ua.everybuy.routing.dto.SubCategoryDto; -import ua.everybuy.routing.dto.util.PriceSerializer; - import java.time.LocalDateTime; @Getter @@ -20,8 +17,7 @@ public class FilteredAdvertisementsResponse { private String title; private Advertisement.ProductType productType; private String section; - @JsonSerialize(using = PriceSerializer.class) - private Double price; + private Long price; private String description; private Boolean isNegotiable; private LocalDateTime updateDate; diff --git a/src/main/java/ua/everybuy/routing/dto/response/UpdateAdvertisementResponse.java b/src/main/java/ua/everybuy/routing/dto/response/UpdateAdvertisementResponse.java index 99944cb..951d346 100644 --- a/src/main/java/ua/everybuy/routing/dto/response/UpdateAdvertisementResponse.java +++ b/src/main/java/ua/everybuy/routing/dto/response/UpdateAdvertisementResponse.java @@ -1,10 +1,7 @@ package ua.everybuy.routing.dto.response; -import com.fasterxml.jackson.databind.annotation.JsonSerialize; import lombok.Getter; import lombok.Setter; -import ua.everybuy.routing.dto.util.PriceSerializer; - import java.time.LocalDateTime; import java.util.List; import java.util.Set; @@ -15,8 +12,7 @@ public class UpdateAdvertisementResponse { private Long id; private String title; private String description; - @JsonSerialize(using = PriceSerializer.class) - private Double price; + private Long price; private LocalDateTime creationDate; private LocalDateTime updateDate; private Boolean isEnabled; diff --git a/src/main/java/ua/everybuy/routing/dto/util/PriceSerializer.java b/src/main/java/ua/everybuy/routing/dto/util/PriceSerializer.java deleted file mode 100644 index 97d7efe..0000000 --- a/src/main/java/ua/everybuy/routing/dto/util/PriceSerializer.java +++ /dev/null @@ -1,16 +0,0 @@ -package ua.everybuy.routing.dto.util; - -import com.fasterxml.jackson.core.JsonGenerator; -import com.fasterxml.jackson.databind.JsonSerializer; -import com.fasterxml.jackson.databind.SerializerProvider; -import java.io.IOException; -import java.text.DecimalFormat; - -public class PriceSerializer extends JsonSerializer { - private static final DecimalFormat df = new DecimalFormat("0.00"); - - @Override - public void serialize(Double value, JsonGenerator gen, SerializerProvider serializers) throws IOException { - gen.writeString(df.format(value)); - } -} diff --git a/src/main/java/ua/everybuy/service/advertisement/filter/FilterAdvertisementService.java b/src/main/java/ua/everybuy/service/advertisement/filter/FilterAdvertisementService.java deleted file mode 100644 index 589840a..0000000 --- a/src/main/java/ua/everybuy/service/advertisement/filter/FilterAdvertisementService.java +++ /dev/null @@ -1,74 +0,0 @@ -package ua.everybuy.service.advertisement.filter; - -import lombok.RequiredArgsConstructor; -import org.springframework.data.domain.Page; -import org.springframework.data.domain.PageRequest; -import org.springframework.data.domain.Pageable; -import org.springframework.data.domain.Sort; -import org.springframework.data.jpa.domain.Specification; -import org.springframework.stereotype.Service; -import ua.everybuy.service.advertisement.filter.sort.SortStrategyFactory; -import ua.everybuy.database.entity.Advertisement; -import ua.everybuy.database.repository.advertisement.AdvertisementRepository; -import ua.everybuy.database.repository.advertisement.spec.factory.AdvertisementSearchSpecificationFactory; -import ua.everybuy.routing.dto.PriceRangeDto; -import ua.everybuy.routing.dto.request.AdvertisementSearchParametersDto; -import ua.everybuy.routing.dto.response.FilteredAdvertisementsResponse; -import ua.everybuy.routing.dto.AdvertisementSearchResultDto; -import ua.everybuy.routing.mapper.AdvertisementFilterMapper; -import java.util.List; -import java.util.stream.Collectors; - -import static ua.everybuy.service.advertisement.filter.sort.SortStrategyFactory.DATE_DESCENDING; - -@Service -@RequiredArgsConstructor -public class FilterAdvertisementService { - private final AdvertisementRepository advertisementRepository; - private final AdvertisementFilterMapper advertisementFilterMapper; - private final AdvertisementSearchSpecificationFactory filterAdSpecFactory; - private final SortStrategyFactory sortStrategyFactory; - private final FilterValidator filterValidator; - private final FilterPriceRangeService filterPriceRangeService; - - public AdvertisementSearchResultDto getFilteredAdvertisements(AdvertisementSearchParametersDto searchParametersDto, - int page, int size) { - - Page filteredAds = applyFilters(searchParametersDto, page, size); - - long totalAdvertisements = filteredAds.getTotalElements(); - int totalPages = filteredAds.getTotalPages(); - - PriceRangeDto priceRange = filterPriceRangeService.getPriceRange(searchParametersDto); - List advertisements = mapToResponse(filteredAds); - - return advertisementFilterMapper.mapToAdvertisementPaginationDto(totalAdvertisements, totalPages, - priceRange.getMinPrice(), priceRange.getMaxPrice(), advertisements); - - } - - private Page applyFilters(AdvertisementSearchParametersDto searchParametersDto, - int page, int size) { - - filterValidator.validate(searchParametersDto); - - Specification specs = filterAdSpecFactory.createSpecification(searchParametersDto); - Sort sort = buildSort(searchParametersDto.getSortOrder()); - Pageable pageable = PageRequest.of(page - 1, - size, sort); - return advertisementRepository.findAll(specs, pageable); - } - - private List mapToResponse(Page advertisements) { - return advertisements.stream() - .map(advertisementFilterMapper::mapToFilteredAdvertisementsResponse) - .collect(Collectors.toList()); - } - - private Sort buildSort(String sortOrder) { - Sort priceSort = sortStrategyFactory.getSortStrategy(sortOrder).getSortOrder(); - Sort dateSort = sortStrategyFactory.getSortStrategy(DATE_DESCENDING).getSortOrder(); - return priceSort.and(dateSort); - } - -} diff --git a/src/main/java/ua/everybuy/service/advertisement/filter/FilterPriceRangeService.java b/src/main/java/ua/everybuy/service/advertisement/filter/FilterPriceRangeService.java deleted file mode 100644 index 95b73ea..0000000 --- a/src/main/java/ua/everybuy/service/advertisement/filter/FilterPriceRangeService.java +++ /dev/null @@ -1,45 +0,0 @@ -package ua.everybuy.service.advertisement.filter; - -import lombok.RequiredArgsConstructor; -import org.springframework.data.domain.Page; -import org.springframework.data.domain.PageRequest; -import org.springframework.data.domain.Pageable; -import org.springframework.data.domain.Sort; -import org.springframework.stereotype.Service; -import ua.everybuy.service.advertisement.filter.sort.SortStrategyFactory; -import ua.everybuy.database.entity.Advertisement; -import ua.everybuy.database.repository.advertisement.AdvertisementRepository; -import ua.everybuy.database.repository.advertisement.spec.factory.AdvertisementSearchSpecificationFactory; -import ua.everybuy.routing.dto.PriceRangeDto; -import ua.everybuy.routing.dto.request.AdvertisementSearchParametersDto; - -import static ua.everybuy.service.advertisement.filter.sort.SortStrategyFactory.PRICE_ASCENDING; -import static ua.everybuy.service.advertisement.filter.sort.SortStrategyFactory.PRICE_DESCENDING; - -@Service -@RequiredArgsConstructor -public class FilterPriceRangeService { - - private final AdvertisementRepository advertisementRepository; - private final AdvertisementSearchSpecificationFactory filterAdSpecFactory; - private final SortStrategyFactory sortStrategyFactory; - - public PriceRangeDto getPriceRange(AdvertisementSearchParametersDto searchParametersDto) { - Double minPrice = getPrice(searchParametersDto, PRICE_ASCENDING); - Double maxPrice = getPrice(searchParametersDto, PRICE_DESCENDING); - return new PriceRangeDto(minPrice, maxPrice); - } - - private Double getPrice(AdvertisementSearchParametersDto searchParametersDto, String priceOrder) { - Sort priceSort = sortStrategyFactory.getSortStrategy(priceOrder).getSortOrder(); - Pageable pageRequest = PageRequest.of(0, 1, priceSort); - Page adPage = findAdvertisements(searchParametersDto, pageRequest); - return adPage.hasContent() ? adPage.getContent().get(0).getPrice() : 0.0; - } - - private Page findAdvertisements(AdvertisementSearchParametersDto searchParametersDto, Pageable pageRequest) { - return advertisementRepository.findAll( - filterAdSpecFactory.createSpecification(searchParametersDto), pageRequest); - } -} - diff --git a/src/main/java/ua/everybuy/service/advertisement/search/ElasticAdvertisementReindexingService.java b/src/main/java/ua/everybuy/service/advertisement/search/ElasticAdvertisementReindexingService.java index 17a62d8..2db1f3e 100644 --- a/src/main/java/ua/everybuy/service/advertisement/search/ElasticAdvertisementReindexingService.java +++ b/src/main/java/ua/everybuy/service/advertisement/search/ElasticAdvertisementReindexingService.java @@ -17,6 +17,7 @@ import ua.everybuy.database.entity.AdvertisementDocument; import ua.everybuy.database.repository.advertisement.AdvertisementRepository; import ua.everybuy.routing.mapper.AdvertisementDocumentMapper; + import java.io.IOException; import java.util.List; @@ -81,58 +82,58 @@ private BulkRequest createBulkIndexRequest(List advertisements) private String getIndexMapping() { return """ - { - "settings": { - "analysis": { - "analyzer": { - "ukrainian": { - "type": "custom", - "tokenizer": "standard", - "filter": [ - "lowercase", - "ukrainian_stop", - "ukrainian_stemmer" - ] + { + "settings": { + "analysis": { + "analyzer": { + "ukrainian": { + "type": "custom", + "tokenizer": "standard", + "filter": [ + "lowercase", + "ukrainian_stop", + "ukrainian_stemmer" + ] + } + }, + "filter": { + "ukrainian_stop": { + "type": "stop", + "stopwords": "_ukrainian_" + }, + "ukrainian_stemmer": { + "type": "stemmer", + "language": "ukrainian" + } + } } }, - "filter": { - "ukrainian_stop": { - "type": "stop", - "stopwords": "_ukrainian_" - }, - "ukrainian_stemmer": { - "type": "stemmer", - "language": "ukrainian" + "mappings": { + "properties": { + "id": { "type": "long" }, + "title": { + "type": "text", + "analyzer": "ukrainian", + "fields": { + "exact": { "type": "keyword" } + } + }, + "description": { "type": "text" }, + "price": { "type": "long" }, + "creationDate": { "type": "date" }, + "updateDate": { "type": "date" }, + "isEnabled": { "type": "boolean" }, + "isNegotiable": { "type": "boolean" }, + "userId": { "type": "long" }, + "mainPhotoUrl": { "type": "text" }, + "cityId": { "type": "long" }, + "topSubCategoryId": { "type": "long" }, + "lowSubCategoryId": { "type": "long" }, + "productType": { "type": "keyword" }, + "section": { "type": "keyword" } } } } - }, - "mappings": { - "properties": { - "id": { "type": "long" }, - "title": { - "type": "text", - "analyzer": "ukrainian", - "fields": { - "exact": { "type": "keyword" } - } - }, - "description": { "type": "text" }, - "price": { "type": "double" }, - "creationDate": { "type": "date" }, - "updateDate": { "type": "date" }, - "isEnabled": { "type": "boolean" }, - "isNegotiable": { "type": "boolean" }, - "userId": { "type": "long" }, - "mainPhotoUrl": { "type": "text" }, - "cityId": { "type": "long" }, - "topSubCategoryId": { "type": "long" }, - "lowSubCategoryId": { "type": "long" }, - "productType": { "type": "keyword" }, - "section": { "type": "keyword" } - } - } - } - """; + """; } } diff --git a/src/main/java/ua/everybuy/service/advertisement/search/ElasticSearchPriceAggregationExtractor.java b/src/main/java/ua/everybuy/service/advertisement/search/ElasticSearchPriceAggregationExtractor.java index 2d67084..124e133 100644 --- a/src/main/java/ua/everybuy/service/advertisement/search/ElasticSearchPriceAggregationExtractor.java +++ b/src/main/java/ua/everybuy/service/advertisement/search/ElasticSearchPriceAggregationExtractor.java @@ -9,7 +9,7 @@ @Service public class ElasticSearchPriceAggregationExtractor { public PriceRangeDto extractPriceRange(Aggregations aggregations) { - if (aggregations == null) return new PriceRangeDto(0.0, 0.0); + if (aggregations == null) return new PriceRangeDto(0L, 0L); return PriceRangeDto.builder() .minPrice(extractMinPrice(aggregations)) @@ -17,19 +17,19 @@ public PriceRangeDto extractPriceRange(Aggregations aggregations) { .build(); } - private double extractMinPrice(Aggregations aggregations) { + private long extractMinPrice(Aggregations aggregations) { Min min = aggregations.get("min_price"); - if (min == null) return 0.0; - - double value = min.getValue(); - return (!Double.isInfinite(value) && !Double.isNaN(value)) ? value : 0.0; + if (min == null) return 0L; + if (min.getValue() == Double.POSITIVE_INFINITY) return 0L; + return (long) min.getValue(); } - private double extractMaxPrice(Aggregations aggregations) { + private long extractMaxPrice(Aggregations aggregations) { Max max = aggregations.get("max_price"); - if (max == null) return 0.0; + if (max == null) return 0L; + + if (max.getValue() == Double.NEGATIVE_INFINITY) return 0L; - double value = max.getValue(); - return (!Double.isInfinite(value) && !Double.isNaN(value)) ? value : 0.0; + return (long) max.getValue(); } } diff --git a/src/main/java/ua/everybuy/service/advertisement/search/category/ElasticSearchCategoryAggregationQueryBuilder.java b/src/main/java/ua/everybuy/service/advertisement/search/category/ElasticSearchCategoryQueryBuilder.java similarity index 96% rename from src/main/java/ua/everybuy/service/advertisement/search/category/ElasticSearchCategoryAggregationQueryBuilder.java rename to src/main/java/ua/everybuy/service/advertisement/search/category/ElasticSearchCategoryQueryBuilder.java index 6a342ec..2284d29 100644 --- a/src/main/java/ua/everybuy/service/advertisement/search/category/ElasticSearchCategoryAggregationQueryBuilder.java +++ b/src/main/java/ua/everybuy/service/advertisement/search/category/ElasticSearchCategoryQueryBuilder.java @@ -14,7 +14,7 @@ @Component @RequiredArgsConstructor -public class ElasticSearchCategoryAggregationQueryBuilder implements QueryBuilder { +public class ElasticSearchCategoryQueryBuilder implements QueryBuilder { private static final String SECTION_FIELD = "section"; private static final String CATEGORY_FIELD = "topSubCategoryId"; private static final String INDEX_NAME = "advertisements"; diff --git a/src/main/java/ua/everybuy/service/advertisement/search/category/ElasticSearchCategoryService.java b/src/main/java/ua/everybuy/service/advertisement/search/category/ElasticSearchCategoryService.java index 7ce04f8..89d04d1 100644 --- a/src/main/java/ua/everybuy/service/advertisement/search/category/ElasticSearchCategoryService.java +++ b/src/main/java/ua/everybuy/service/advertisement/search/category/ElasticSearchCategoryService.java @@ -24,7 +24,7 @@ public class ElasticSearchCategoryService implements SearchCategoryService { private static final String SECTION_AGG = "by_section"; private static final List SECTIONS = List.of("SELL", "BUY"); - private final ElasticSearchCategoryAggregationQueryBuilder queryBuilder; + private final ElasticSearchCategoryQueryBuilder queryBuilder; private final RestHighLevelClient esClient; private final TopLevelSubCategoryService topCategoryService; @@ -32,7 +32,7 @@ public class ElasticSearchCategoryService implements SearchCategoryService { * Finds top categories by keyword, grouped by section (SELL/BUY) * * @param keyword Search term - * @return Map where key is section, value is list of categories with counts + * @return Map where key is section, value is list of categories with counts advertisements */ @Override public Map> findTopCategoriesByKeyword(String keyword) { diff --git a/src/main/resources/db/migration/V2025.08.02.002__fix_price_values.sql b/src/main/resources/db/migration/V2025.08.02.002__fix_price_values.sql new file mode 100644 index 0000000..bfeb66c --- /dev/null +++ b/src/main/resources/db/migration/V2025.08.02.002__fix_price_values.sql @@ -0,0 +1 @@ +UPDATE advertisements SET price = ROUND(price / 100); diff --git a/src/main/resources/db/migration/V2025.08.02__change_price_type.sql b/src/main/resources/db/migration/V2025.08.02__change_price_type.sql new file mode 100644 index 0000000..0cd7494 --- /dev/null +++ b/src/main/resources/db/migration/V2025.08.02__change_price_type.sql @@ -0,0 +1,2 @@ +ALTER TABLE advertisements ALTER COLUMN price TYPE BIGINT; +UPDATE advertisements SET price = ROUND(price * 100);