package app.mealsmadeeasy.api.recipe; import app.mealsmadeeasy.api.user.User; import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Slice; import org.springframework.data.jpa.repository.EntityGraph; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.Query; import java.util.List; import java.util.Optional; public interface RecipeRepository extends JpaRepository { Slice findAllByIsPublicIsTrue(Pageable pageable); List findAllByViewersContaining(User viewer); List findAllByOwner(User owner); @Query("SELECT r from Recipe r WHERE r.owner.username = ?1 AND r.slug = ?2") Optional findByOwnerUsernameAndSlug(String ownerUsername, String slug); @Query("SELECT r FROM Recipe r WHERE size(r.stars) >= ?1 AND (r.isPublic OR ?2 MEMBER OF r.viewers)") List findAllViewableByStarsGreaterThanEqual(long stars, User viewer); @Query("SELECT r FROM Recipe r WHERE r.id = ?1") @EntityGraph(attributePaths = { "viewers" }) Optional findByIdWithViewers(long id); @Query("SELECT r FROM Recipe r WHERE r.id = ?1") @EntityGraph(attributePaths = { "stars" }) Optional findByIdWithStars(long id); @Query("SELECT size(r.stars) FROM Recipe r WHERE r.id = ?1") int getStarCount(long recipeId); @Query("SELECT size(r.viewers) FROM Recipe r WHERE r.id = ?1") int getViewerCount(long recipeId); @Query("SELECT r FROM Recipe r WHERE r.isPublic OR r.owner = ?1 OR ?1 MEMBER OF r.viewers") Slice findAllViewableBy(User viewer, Pageable pageable); List findAllByEmbeddingIsNull(); @Query( nativeQuery = true, value = """ WITH distances AS (SELECT recipe_id, embedding <=> cast(?1 AS vector) AS distance FROM recipe_embedding) SELECT r.* FROM distances d INNER JOIN recipe r ON r.id = d.recipe_id WHERE d.distance < ?2 AND ( r.is_public = TRUE OR r.owner_id = ?3 OR exists(SELECT 1 FROM recipe_viewer v WHERE v.recipe_id = r.id AND v.viewer_id = ?3) ) ORDER BY d.distance; """ ) List searchByEmbeddingAndViewableBy(float[] queryEmbedding, float similarity, Integer viewerId); @Query( nativeQuery = true, value = """ WITH distances AS (SELECT recipe_id, embedding <=> cast(?1 AS vector) AS distance FROM recipe_embedding) SELECT r.* FROM distances d INNER JOIN recipe r ON r.id = d.recipe_id WHERE d.distance < ?2 AND r.is_public = TRUE ORDER BY d.distance; """ ) List searchByEmbeddingAndIsPublic(float[] queryEmbedding, float similarity); }