Merge branch 'fix/search-bar-wiring' into 'main'
Refined search code and wired up the client side search with server Closes #68 See merge request cse1105/2025-2026/teams/csep-team-76!66
This commit is contained in:
commit
75e9fc1e62
3 changed files with 94 additions and 55 deletions
|
|
@ -56,9 +56,28 @@ public class ServerUtils {
|
|||
return list; // JSON string-> List<Recipe> (Jackson)
|
||||
}
|
||||
|
||||
/**
|
||||
* The method used by the search bar to get filtered recipes.
|
||||
* @param filter - filter string
|
||||
* @param locales - locales of the user
|
||||
* @return filtered recipe list
|
||||
*/
|
||||
public List<Recipe> getRecipesFiltered(String filter, List<String> locales) throws IOException, InterruptedException {
|
||||
// TODO: implement filtering on server side
|
||||
return this.getRecipes(locales);
|
||||
//TODO add limit integration
|
||||
String uri = SERVER + "/recipes?search=" + filter + "&locales=" + String.join(",", locales);
|
||||
HttpRequest request = HttpRequest.newBuilder()
|
||||
.uri(URI.create(uri))
|
||||
.GET()
|
||||
.build();
|
||||
HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
|
||||
|
||||
if(response.statusCode() != statusOK){
|
||||
throw new IOException("Failed to get filtered recipes. Server responds with " + response.body());
|
||||
}
|
||||
|
||||
List<Recipe> list = objectMapper.readValue(response.body(), new TypeReference<List<Recipe>>() {});
|
||||
logger.info("Received filtered recipes from server: " + list);
|
||||
return list;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -23,9 +23,11 @@ import org.springframework.web.bind.annotation.RestController;
|
|||
import server.database.RecipeRepository;
|
||||
import server.service.RecipeService;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.logging.Logger;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/api")
|
||||
|
|
@ -60,26 +62,6 @@ public class RecipeController {
|
|||
.orElseGet(() -> ResponseEntity.notFound().build());
|
||||
}
|
||||
|
||||
/**
|
||||
* Mapping for <code>GET /recipes(?limit=)(&locales=)</code>
|
||||
* <p>
|
||||
* If the limit parameter is unspecified, return all recipes in the repository.
|
||||
* @param limit Integer limit of items you want to get
|
||||
* @return The list of recipes
|
||||
*/
|
||||
@GetMapping("/recipes")
|
||||
public ResponseEntity<List<Recipe>> getRecipes(
|
||||
@RequestParam Optional<List<String>> locales,
|
||||
@RequestParam Optional<Integer> limit
|
||||
) {
|
||||
logger.info("GET /recipes called.");
|
||||
|
||||
List<Recipe> recipes = locales
|
||||
.map(loc -> findWithLocales(loc, limit))
|
||||
.orElseGet(() -> findAll(limit));
|
||||
|
||||
return ResponseEntity.ok(recipes);
|
||||
}
|
||||
|
||||
private List<Recipe> findWithLocales(List<String> locales, Optional<Integer> limit) {
|
||||
return limit
|
||||
|
|
@ -155,36 +137,68 @@ public class RecipeController {
|
|||
|
||||
/**
|
||||
* Performs a search based on a case-insensitive partial match on
|
||||
* Recipe name and limits the result to a set amount of results.
|
||||
* Recipe name, ingredients and preparations steps
|
||||
* and limits the result to a set amount of results.
|
||||
* @param search - name of the recipe to be searched for.
|
||||
* @param limit - limit of the results queried for.
|
||||
* @param lang - stores the info of the language of the user to provide server support/
|
||||
* @param locales - stores the info of the language of the user to provide server support/
|
||||
* @return - returns a ResponseEntity with a List of Recipes and an HTTP 200 ok status.
|
||||
*/
|
||||
@GetMapping("/recipes")
|
||||
public ResponseEntity<List<Recipe>> getRecipes(
|
||||
@RequestParam Optional<String> search,
|
||||
@RequestParam Optional<Integer> limit,
|
||||
@RequestParam Optional<String> lang){
|
||||
@RequestParam Optional<List<String>> locales){
|
||||
|
||||
List<Recipe> recipes = recipeRepository.findAll();
|
||||
logger.info("GET /recipes called ith search: " + search.orElse("none"));
|
||||
|
||||
List<Recipe> finalRecipes = recipes;
|
||||
recipes = search
|
||||
.filter(s -> !s.trim().isEmpty()) // filters recipes if the string is not empty by doing a lowercase search
|
||||
.map(s -> {
|
||||
String lowercaseSearch = s.toLowerCase();
|
||||
return finalRecipes.stream()
|
||||
.filter(recipe ->
|
||||
recipe.getName().toLowerCase().contains(lowercaseSearch)
|
||||
)
|
||||
.toList();
|
||||
})
|
||||
.orElse(recipes);
|
||||
recipes = limit // filters based on limit if provided
|
||||
.filter(l -> l < finalRecipes.size())
|
||||
.map(l -> finalRecipes.stream().limit(l).toList())
|
||||
.orElse(recipes);
|
||||
return ResponseEntity.ok(recipes);
|
||||
//Start the search with all the recipes (locale filtered)
|
||||
List<Recipe> recipeList = locales.map(loc->findWithLocales(loc, Optional.empty()))
|
||||
.orElseGet(()->recipeService.findAll());
|
||||
|
||||
//check if limit is zero (and also below 0 for anomalous cases) and end the search early
|
||||
if(limit.isPresent() && limit.get() <= 0){
|
||||
recipeList.clear();
|
||||
return ResponseEntity.ok(recipeList);
|
||||
}
|
||||
|
||||
//Apply Search filter
|
||||
|
||||
if(search.isPresent() && !search.get().trim().isEmpty()) {
|
||||
recipeList = recipeList.stream()
|
||||
.filter(recipe ->
|
||||
matchesSearchTerms(recipe, Arrays.stream(search.get().split(" ")).toList()))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
//Apply limit
|
||||
if(limit.isPresent() && limit.get()< recipeList.size()){
|
||||
recipeList = recipeList.stream()
|
||||
.limit(limit.get())
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
return ResponseEntity.ok(recipeList);
|
||||
|
||||
}
|
||||
|
||||
public boolean matchesSearchTerms(Recipe recipe, List<String> searchTerms){
|
||||
|
||||
List<String> lowerCased = searchTerms.stream()
|
||||
.map(String::toLowerCase)
|
||||
.map(String::trim)
|
||||
.toList();
|
||||
|
||||
for (String s : lowerCased) {
|
||||
boolean foundInRecipeName = recipe.getName().trim().toLowerCase().contains(s); //searches recipe names
|
||||
boolean foundInIngredients = recipe.getIngredients().stream().anyMatch(ing -> ing.getIngredient()
|
||||
.getName().toLowerCase().contains(s)); // searches in ingredients
|
||||
boolean foundInPrepSteps = recipe.getPreparationSteps().stream()
|
||||
.anyMatch(step -> step.toLowerCase().contains(s)); //searches preparation steps
|
||||
if(!foundInRecipeName && !foundInIngredients && !foundInPrepSteps){
|
||||
return false; // returns false if not met all criteria. this meets the backlog criteria
|
||||
}
|
||||
}
|
||||
return true; // all terms found
|
||||
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -115,19 +115,21 @@ public class RecipeControllerTest {
|
|||
// The number of recipes returned is the same as the entire input list
|
||||
assertEquals(recipes.size(),
|
||||
controller.getRecipes(
|
||||
Optional.empty(),
|
||||
Optional.empty(),
|
||||
Optional.empty()).getBody().size());
|
||||
Optional.of(List.of("en", "nl"))).getBody().size());;
|
||||
}
|
||||
|
||||
@Test
|
||||
@Tag("test-from-init-data")
|
||||
public void getSomeRecipes() {
|
||||
final int LIMIT = 5;
|
||||
// The number of recipes returned is the same as the entire input list
|
||||
// The number of recipes returned is the same as the limit
|
||||
assertEquals(LIMIT,
|
||||
controller.getRecipes(
|
||||
Optional.empty(),
|
||||
Optional.of(LIMIT)).getBody().size());
|
||||
Optional.empty(),
|
||||
Optional.of(LIMIT),
|
||||
Optional.of(List.of("en"))).getBody().size());
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -135,8 +137,10 @@ public class RecipeControllerTest {
|
|||
public void getManyRecipesWithLocale() {
|
||||
// The number of recipes returned is the same as the entire input list
|
||||
assertEquals(recipes.size(),
|
||||
controller.getRecipes(Optional.of(List.of("en", "nl")),
|
||||
Optional.empty()).getBody().size());
|
||||
controller.getRecipes(
|
||||
Optional.empty(),
|
||||
Optional.empty(),
|
||||
Optional.of(List.of("en", "nl"))).getBody().size());
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -144,18 +148,20 @@ public class RecipeControllerTest {
|
|||
public void getNoRecipesWithLocale() {
|
||||
// should have NO Dutch recipes (thank god)
|
||||
assertEquals(0,
|
||||
controller.getRecipes(Optional.of(List.of("nl")),
|
||||
Optional.empty()).getBody().size());
|
||||
controller.getRecipes(
|
||||
Optional.empty(),
|
||||
Optional.empty(),
|
||||
Optional.of(List.of("nl"))).getBody().size());
|
||||
}
|
||||
|
||||
@Test
|
||||
@Tag("test-from-init-data")
|
||||
public void getSomeRecipesWithLocale() {
|
||||
final int LIMIT = 5;
|
||||
// The number of recipes returned is the same as the entire input list
|
||||
assertEquals(LIMIT,
|
||||
controller.getRecipes(Optional.of(List.of("en", "nl")),
|
||||
Optional.of(LIMIT)).getBody().size());
|
||||
controller.getRecipes(
|
||||
Optional.empty(),
|
||||
Optional.of(LIMIT),
|
||||
Optional.of(List.of("en", "nl"))).getBody().size());
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue