#mysql #spring-boot #hibernate #jpa #hql
Вопрос:
Существует таблица продуктов, в которой есть множество ссылок на категории.
Моя цель-выбрать топ-5 наиболее подходящих товаров для данных категорий.
Сейчас я реализовал такой запрос, но он не очень эффективен, потому что сначала он находит совпадение по количеству категорий и по первому совпадению с названием категории.
@Query("SELECT p FROM Product p JOIN p.categories c WHERE c.name IN (:categories) ORDER BY SIZE(p.categories) DESC")
List<Product> findByCategories_NameInOrderByCount(@Param("categories") Collection<String> categories);
Как я могу улучшить свой запрос, чтобы он первым возвращал товары с наибольшим количеством категорий, соответствующих названию?
Ответ №1:
Ваш запрос неверен, вы должны использовать что-то подобное этому:
@Query("SELECT p FROM Product p WHERE (SELECT COUNT(*) FROM p.categories c WHERE c.name IN (:categories)) > 0 ORDER BY (SELECT COUNT(*) FROM p.categories c WHERE c.name IN (:categories)) DESC")
List<Product> findByCategories_NameInOrderByCount(@Param("categories")
Collection<String> categories);
В любом случае, это, вероятно, также не будет работать очень хорошо, потому что базе данных придется объединить и отсортировать три таблицы. Чтобы избежать этого, обычно используется инвертированный или многозначный индекс, как его называет MySQL (https://dev.mysql.com/doc/refman/8.0/en/create-index.html#create-index-multi-valued). Удалите таблицы product_has_category и category и вместо этого введите столбец JSON «сведения» в таблицу продуктов.
Затем вы можете создать индекс:
CREAT INDEX categories ON product( (CAST(JSON_EXTRACT(categories, '$[*]') AS VARCHAR(80) ARRAY)) )
И запросите его вот так:
@Query("SELECT p FROM Product p WHERE FUNCTION('JSON_OVERLAPS', FUNCTION('JSON_EXTRACT', p.categories, '$[*]'), :categories) = 1 ORDER BY FUNCTION('COUNT_MATCHES', p.categories, :categories) DESC")
List<Product> findByCategories_NameInOrderByCount(@Param("categories")
String categories);
Но вам нужно добавить функцию в свою базу данных:
CREATE FUNCTION COUNT_MATCHES (categories JSON, searchCategories JSON)
RETURNS INT DETERMINISTIC
RETURN (
SELECT COUNT(*)
FROM JSON_TABLE(categories, '$[*]' COLUMNS(
name VARCHAR(80) PATH '
Комментарии:
1. Большое вам спасибо, я долго страдал.
ERROR ON ERROR)
) c
JOIN JSON_TABLE(searchCategories, '$[*]' COLUMNS(
name VARCHAR(80) PATH '
Комментарии:
1. Большое вам спасибо, я долго страдал.
ERROR ON ERROR )
) s ON c.name = s.name
);
Комментарии:
1. Большое вам спасибо, я долго страдал.