简体   繁体   中英

JPQL query for select on entities having @OneToMany relation with optional search parameters

I have two entities, Recipes and Ingredient :

Recipes looks like:

public class Recipes {

    private Long id;

    private String name;

    private Integer serveCount;

    private RecipesType type;

    @OneToMany(mappedBy = "recipes",
            cascade = CascadeType.ALL)
    private List<Ingredient> ingredients = new ArrayList<>();
}

And Ingredient looks like:

public class Ingredient {
    
        private Long id;

        private String name;

        private Integer count;

        private String unit;

        @ManyToOne(fetch = FetchType.LAZY)
        @JoinColumn(name = "RECIPES_ID", nullable = false)
        private Recipes recipes;
    }

I was asked to implement filtering service with these parameters:

  1. Recipes type(Vegetarian, Dairy, Fast food)
  2. Number of serving
  3. Specific ingredients (either include or exclude)

So I created a FilterRequestDTO based on them and implemented my service as below:

@Override
public ListResponseDTO<RecipesDTO> getFilteredRecipes(FilterRecipesDTO filterRecipesDTO) {
    ListResponseDTO<RecipesDTO> response = new ListResponseDTO<>();
    var temp = repository.findFilteredRecipes(
            Objects.nonNull(filterRecipesDTO.getType()) ? RecipesType.parseRecipesType(filterRecipesDTO.getType()) : null,
            Objects.nonNull(filterRecipesDTO.getServeCount()) ? filterRecipesDTO.getServeCount() : null,
            filterRecipesDTO.getIncludeIngredients().size() > 0 ? filterRecipesDTO.getIncludeIngredients() : null,
            filterRecipesDTO.getExcludeIngredients().size() > 0 ? filterRecipesDTO.getExcludeIngredients() : null,
            PageRequest.of(filterRecipesDTO.getPage(), filterRecipesDTO.getSize()));

    if (temp.isPresent()) {
        response = new ListResponseDTO<>(filterRecipesDTO.getPage(), temp.get().getNumberOfElements());
        for (Recipes recipes : temp.get().getContent())
            response.getData().add(new RecipesDTO().from(recipes));
    }
    return response;
}

And it calls my Repository method layer which looks like:

@Query("select r from Recipes r join Ingredient i on r.id = i.recipes.id " +
        "where (:typ is null or r.type = :typ) " +
        "and (:cnt  is null or r.serveCount = :cnt) " +
        "and (:inc is null or i.name in :inc) " +
        "and (:exc is null or i.name not in :exc)")
Optional<Page<Recipes>> findFilteredRecipes(@Param("typ") RecipesType type,
                                            @Param("cnt") Integer serveCount,
                                            @Param("inc") List<String> includeIngredients,
                                            @Param("exc") List<String> excludeIngredients,
                                            Pageable page);

But when I want to filter result with given includeIngredients and excludeIngredients lists, it does not seem to work.

Do you have any idea how solve this issue?

Any help would be appreciated

If you need to return receipt if it contains at least one ingredient requested, the query would be:

select r from Recipes r
where (:typ is null or r.type = :typ)
and (:cnt  is null or r.serveCount = :cnt)
and ((:inc) is null or exists (select 1 from Ingredient i where i.recipes=r and i.name in :inc)) 
and ((:exc) is null or not exists (select 1 from Ingredient i where i.recipes=r and i.name in :exc))

if the requirement is to return receipt with all ingredients requested, the query would be:

select r from Recipes r
where (:typ is null or r.type = :typ)
and (:cnt  is null or r.serveCount = :cnt)
and ((:inc) is null or :inccnt = (select count(1) from Ingredient i where i.recipes=r and i.name in :inc)) 
and ((:exc) is null or not exists (select 1 from Ingredient i i.recipes=r and where i.name in :exc))

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM