diff --git a/src/main/java/ru/kayashov/bar/service/CocktailService.java b/src/main/java/ru/kayashov/bar/service/CocktailService.java index a1a8def..4dc0425 100644 --- a/src/main/java/ru/kayashov/bar/service/CocktailService.java +++ b/src/main/java/ru/kayashov/bar/service/CocktailService.java @@ -2,8 +2,6 @@ package ru.kayashov.bar.service; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; -import org.hibernate.Session; -import org.hibernate.query.Query; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -32,20 +30,18 @@ import ru.kayashov.bar.repository.IngredientRepository; import ru.kayashov.bar.repository.ReceiptRepository; import javax.persistence.EntityManager; -import javax.persistence.criteria.CriteriaBuilder; -import javax.persistence.criteria.CriteriaQuery; -import javax.persistence.criteria.Join; -import javax.persistence.criteria.JoinType; -import javax.persistence.criteria.Order; -import javax.persistence.criteria.Predicate; -import javax.persistence.criteria.Root; import java.io.File; import java.io.IOException; +import java.time.Duration; +import java.time.Instant; import java.time.LocalDateTime; import java.util.ArrayList; import java.util.Arrays; +import java.util.Comparator; import java.util.List; import java.util.Objects; +import java.util.Set; +import java.util.stream.Collectors; @Slf4j @Service @@ -87,65 +83,6 @@ public class CocktailService { return mapper.cocktailsToDtoList(criteria(dto), false, true); } - private List criteria(CocktailFilterRequestDto dto) { - Session session = entityManager.unwrap(Session.class); - CriteriaBuilder cb = session.getCriteriaBuilder(); - CriteriaQuery criteriaQuery = cb.createQuery(CocktailEntity.class); - Root root = criteriaQuery.from(CocktailEntity.class); - List predicates = new ArrayList<>(); - - criteriaQuery.distinct(true); - if (!dto.getAll()) { - Predicate pr = cb.isTrue(root.get("allowed")); - predicates.add(pr); - } - - if (!dto.getSearch().isEmpty()) { - String[] search = dto.getSearch().split(" "); - List in = new ArrayList<>(); - Join receiptJoin = root.join("receipt", JoinType.LEFT); - for (String s : search) { - in.add(cb.like(cb.lower(root.get("name")), "%" + s.toLowerCase() + "%")); - in.add(cb.like(cb.lower(receiptJoin.get("ingredient").get("name")), "%" + s.toLowerCase() + "%")); - } - predicates.add(cb.or(in.toArray(new Predicate[0]))); - } - - if (dto.getOnlyFavourite()) { - predicates.add(cb.isTrue(root.get("isFavorite"))); - } - - if (dto.getGlass() != null && !dto.getGlass().isEmpty()) { - predicates.add(root.get("glass").in(dto.getGlass().stream().map(Glass::findValue).toList())); - } - - if (dto.getCategory() != null && !dto.getCategory().isEmpty()) { - predicates.add(root.get("category").in(dto.getCategory().stream().map(Category::findValue).toList())); - } - - if (dto.getAlcohol() != null && !dto.getAlcohol().isEmpty()) { - predicates.add(root.get("alcoholic").in(dto.getAlcohol().stream().map(Alcoholic::findValue).toList())); - } - - //todo: доделать другие виды сортировки - Order order; - switch (dto.getSort()) { - case NAME_ASC -> order = cb.asc(root.get("name")); - case NAME_DESC -> order = cb.desc(root.get("name")); - default -> order = cb.asc(root.get("name")); - } - - criteriaQuery.where(predicates.toArray(new Predicate[0])) - .orderBy(order); - Query query = session.createQuery(criteriaQuery); - query.setFirstResult(dto.getPage() * dto.getSize()); - query.setMaxResults(dto.getSize()); - List cocktailEntities = query.getResultList(); - - log.info("Найдено {} коктейлей", cocktailEntities.size()); - return cocktailEntities; - } - public CocktailForListResponseDto findById(Long id) { CocktailEntity cocktail = repository.findById(id).orElseThrow(); return mapper.cocktailToFullDto(cocktail); @@ -244,6 +181,60 @@ public class CocktailService { log.info("Коктейль {} - {} был скрыт для бара {} - {}", cocktail.getId(), cocktail.getName(), bar.getId(), bar.getName()); } + public void drink(Long id) { + CocktailEntity cocktail = repository.findById(id).orElseThrow(RuntimeException::new); + int count = cocktail.getCountDrink() + 1; + cocktail.setCountDrink(count); + repository.save(cocktail); + + eventService.createEvent(Event.builder() + .type(EventType.DRINK) + .date(LocalDateTime.now()) + .oldState(cocktail.getName()) + .newState(String.valueOf(count)) + .build()); + } + + //todo: написать функцию в БД, чтобы ускорить работу и сохранить читаемый код + private List criteria(CocktailFilterRequestDto dto) { + Instant now = Instant.now(); + List cocktails = repository.findAll() + .stream() + //todo: доделать другие виды сортировки + .sorted(Comparator.comparing(CocktailEntity::getName)) + .filter(c -> c.getAllowed() || dto.getAll()) + .filter(c -> c.getIsFavorite() || !dto.getOnlyFavourite()) + .filter(c -> isEmpty(dto.getGlass()) || dto.getGlass().contains(c.getGlass().getName())) + .filter(c -> isEmpty(dto.getCategory()) || dto.getCategory().contains(c.getCategory().getName())) + .filter(c -> isEmpty(dto.getAlcohol()) || dto.getAlcohol().contains(c.getAlcoholic().getValue())) + .filter(c -> dto.getSearch().isEmpty() || textSearchFilter(c, dto.getSearch())) + .skip((long) dto.getPage() * dto.getSize()) + .limit(dto.getSize()) + .toList(); + log.info("Найдено {} коктейлей за {} ms", cocktails.size(), Duration.between(now, Instant.now()).toMillis()); + return cocktails; + } + + private boolean textSearchFilter(CocktailEntity cocktail, String founding) { + String[] search = founding.split(" "); + + if (Arrays.stream(search).anyMatch(s -> cocktail.getName().toLowerCase().contains(s.toLowerCase()))) { + return true; + } + + Set ingredientsNames = cocktail.getReceipt().stream() + .map(ReceiptEntity::getIngredient) + .map(IngredientEntity::getName) + .map(String::toLowerCase) + .collect(Collectors.toSet()); + + return Arrays.stream(search).anyMatch(s -> ingredientsNames.contains(s.toLowerCase())); + } + + private boolean isEmpty(List value) { + return value == null || value.isEmpty(); + } + //todo: попробовать отыграть эту связку каскадами private void editCocktailReceipts(List old, List actual, CocktailEntity cocktail) { for (ReceiptResponseDto receipt : actual) { @@ -309,18 +300,65 @@ public class CocktailService { return sb.toString(); } - public void drink(Long id) { - CocktailEntity cocktail = repository.findById(id).orElseThrow(RuntimeException::new); - int count = cocktail.getCountDrink() + 1; - cocktail.setCountDrink(count); - repository.save(cocktail); - eventService.createEvent(Event.builder() - .type(EventType.DRINK) - .date(LocalDateTime.now()) - .oldState(cocktail.getName()) - .newState(String.valueOf(count)) - .build()); + //todo: оставить до написания функции в БД +// private List criteria(CocktailFilterRequestDto dto) { +// Instant now = Instant.now(); +// Session session = entityManager.unwrap(Session.class); +// CriteriaBuilder cb = session.getCriteriaBuilder(); +// CriteriaQuery criteriaQuery = cb.createQuery(CocktailEntity.class); +// Root root = criteriaQuery.from(CocktailEntity.class); +// List predicates = new ArrayList<>(); +// +// criteriaQuery.distinct(true); +// if (!dto.getAll()) { +// Predicate pr = cb.isTrue(root.get("allowed")); +// predicates.add(pr); +// } +// +// if (!dto.getSearch().isEmpty()) { +// String[] search = dto.getSearch().split(" "); +// List in = new ArrayList<>(); +// Join receiptJoin = root.join("receipt", JoinType.LEFT); +// for (String s : search) { +// in.add(cb.like(cb.lower(root.get("name")), "%" + s.toLowerCase() + "%")); +// in.add(cb.like(cb.lower(receiptJoin.get("ingredient").get("name")), "%" + s.toLowerCase() + "%")); +// } +// predicates.add(cb.or(in.toArray(new Predicate[0]))); +// } +// +// if (dto.getOnlyFavourite()) { +// predicates.add(cb.isTrue(root.get("isFavorite"))); +// } +// +// if (dto.getGlass() != null && !dto.getGlass().isEmpty()) { +// predicates.add(root.get("glass").in(dto.getGlass().stream().map(Glass::findValue).toList())); +// } +// +// if (dto.getCategory() != null && !dto.getCategory().isEmpty()) { +// predicates.add(root.get("category").in(dto.getCategory().stream().map(Category::findValue).toList())); +// } +// +// if (dto.getAlcohol() != null && !dto.getAlcohol().isEmpty()) { +// predicates.add(root.get("alcoholic").in(dto.getAlcohol().stream().map(Alcoholic::findValue).toList())); +// } - } +// //todo: доделать другие виды сортировки +// Order order; +// switch (dto.getSort()) { +// case NAME_ASC -> order = cb.asc(root.get("name")); +// case NAME_DESC -> order = cb.desc(root.get("name")); +// default -> order = cb.asc(root.get("name")); +// } +// +// criteriaQuery.where(predicates.toArray(new Predicate[0])) +// .orderBy(order); +// Query query = session.createQuery(criteriaQuery); +// query.setFirstResult(dto.getPage() * dto.getSize()); +// query.setMaxResults(dto.getSize()); +// List cocktailEntities = query.getResultList(); +// +// log.info("Найдено {} коктейлей за {} ms", cocktailEntities.size(), Duration.between(now, Instant.now()).toMillis()); +// return cocktailEntities; +// } }