Skip to content

Commit

Permalink
Merge pull request #223 from bounswe/201-search
Browse files Browse the repository at this point in the history
Add Search Endpoint
  • Loading branch information
oguzhekim authored Nov 24, 2024
2 parents a1c50e2 + 8964b58 commit efcfff0
Show file tree
Hide file tree
Showing 9 changed files with 226 additions and 2 deletions.
8 changes: 8 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,14 @@ dist
dist-ssr
*.local

# Ignore all docker-compose files by name
docker-compose*.yaml
docker-compose*.yml

# Ignore all Dockerfiles by name
Dockerfile*


# Editor directories and files
.vscode/*
!.vscode/extensions.json
Expand Down
9 changes: 9 additions & 0 deletions backend/demo-group7/.gitignore
Original file line number Diff line number Diff line change
@@ -1,9 +1,17 @@
HELP.md
application.properties
target/
!.mvn/wrapper/maven-wrapper.jar
!**/src/main/**/target/
!**/src/test/**/target/

# Ignore all docker-compose files by name
docker-compose*.yaml
docker-compose*.yml

# Ignore all Dockerfiles by name
Dockerfile*

### STS ###
.apt_generated
.classpath
Expand All @@ -13,6 +21,7 @@ target/
.springBeans
.sts4-cache


### IntelliJ IDEA ###
.idea
*.iws
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
package com.group7.demo.controllers;

import com.group7.demo.services.SearchService;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import java.util.Arrays;
import java.util.List;
import java.util.Map;

@RestController
@RequestMapping("/api")
public class SearchController {

private final SearchService searchService;

public SearchController(SearchService searchService) {
this.searchService = searchService;
}


// @GetMapping("/search")
// public ResponseEntity<Map<String, Object>> search(@RequestParam("q") String query) {
// // Split the query into keywords by whitespace and filter out any empty strings
// String[] keywords = query.split("\\s+");
// List<String> keywordList = Arrays.stream(keywords)
// .map(String::trim)
// .filter(k -> !k.isEmpty())
// .toList();
//
// // Pass the keyword list to the service
// Map<String, Object> results = searchService.search(keywordList);
//
// return ResponseEntity.ok(results);
// }
@GetMapping("/search")
public ResponseEntity<Map<String, Object>> search(@RequestParam("q") String query) {
// String[] keywords = query.split("\\s+");
// List<String> keywordList = Arrays.stream(keywords)
// .map(String::trim)
// .filter(k -> !k.isEmpty())
// .toList();

Map<String, Object> results = searchService.search(query);

return ResponseEntity.ok(results);
}


}
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ public class Post {
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;

@Lob
// @Lob
@Column(columnDefinition = "TEXT")
private String content;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,8 @@ public class TrainingProgram {

private String title;

@Lob
// @Lob
@Column(columnDefinition = "TEXT")
private String description;

@ManyToOne
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,23 @@

import com.group7.demo.models.Exercise;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;

import java.util.List;
import java.util.Optional;

public interface ExerciseRepository extends JpaRepository<Exercise, Long> {
Optional<Exercise> findByName(String name);

@Query("SELECT DISTINCT e FROM Exercise e " +
"LEFT JOIN e.instructions i " +
"WHERE " +
"LOWER(e.name) LIKE LOWER(CONCAT('%', :query, '%')) OR " +
"LOWER(e.bodyPart) LIKE LOWER(CONCAT('%', :query, '%')) OR " +
"LOWER(e.targetMuscle) LIKE LOWER(CONCAT('%', :query, '%')) OR " +
"LOWER(e.equipment) LIKE LOWER(CONCAT('%', :query, '%')) OR " +
"LOWER(i) LIKE LOWER(CONCAT('%', :query, '%'))")
List<Exercise> search(@Param("query") String query);

}
Original file line number Diff line number Diff line change
Expand Up @@ -15,5 +15,14 @@ public interface PostRepository extends JpaRepository<Post, Long> {
@Query("SELECT DISTINCT p FROM Post p JOIN p.tags t WHERE t.name IN :tagNames")
List<Post> findPostsByTags(@Param("tagNames") Set<String> tagNames);

@Query("SELECT p FROM Post p " +
"LEFT JOIN p.tags t " +
"LEFT JOIN p.trainingProgram tp " +
"WHERE " +
"LOWER(p.content) LIKE LOWER(CONCAT('%', :query, '%')) OR " +
"LOWER(t.name) LIKE LOWER(CONCAT('%', :query, '%')) OR " +
"LOWER(tp.title) LIKE LOWER(CONCAT('%', :query, '%')) OR " +
"LOWER(p.user.username) LIKE LOWER(CONCAT('%', :query, '%'))")
List<Post> search(@Param("query") String query);

}
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,17 @@
import com.group7.demo.models.TrainingProgram;
import com.group7.demo.models.User;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;

import java.util.List;

public interface TrainingProgramRepository extends JpaRepository<TrainingProgram, Long> {

List<TrainingProgram> findByTrainer(User user);

@Query("SELECT t FROM TrainingProgram t WHERE " +
"LOWER(t.title) LIKE LOWER(CONCAT('%', :query, '%')) OR " +
"LOWER(t.description) LIKE LOWER(CONCAT('%', :query, '%'))")
List<TrainingProgram> search(@Param("query") String query);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
package com.group7.demo.services;

import com.group7.demo.dtos.PostResponse;
import com.group7.demo.dtos.TrainingProgramResponse;
import com.group7.demo.dtos.mapper.Mapper;
import com.group7.demo.models.Exercise;
import com.group7.demo.models.Post;
import com.group7.demo.models.TrainingProgram;
import com.group7.demo.repository.ExerciseRepository;
import com.group7.demo.repository.PostRepository;
import com.group7.demo.repository.TrainingProgramRepository;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.stream.Collectors;

@Service
public class SearchService {
private final ExerciseRepository exerciseRepository;
private final PostRepository postRepository;
private final TrainingProgramRepository trainingProgramRepository;

private final Mapper mapper;

public SearchService(ExerciseRepository exerciseRepository,
PostRepository postRepository,
TrainingProgramRepository trainingProgramRepository, Mapper mapper) {
this.exerciseRepository = exerciseRepository;
this.postRepository = postRepository;
this.trainingProgramRepository = trainingProgramRepository;
this.mapper = mapper;
}

// public Map<String, Object> search(String query) {
// Map<String, Object> results = new HashMap<>();
//
// // Use the mapper for converting entities to response objects
// List<PostResponse> postResponses = postRepository.search(query).stream()
// .map(mapper::mapToPostResponse)
// .collect(Collectors.toList());
//
// // Map training programs (assumes you have a corresponding method in the Mapper class)
// List<TrainingProgramResponse> trainingProgramResponses = trainingProgramRepository.search(query).stream()
// .map(mapper::mapToTrainingProgramResponse)
// .collect(Collectors.toList());
//
// // Add exercises directly if they don't need custom mapping
// List<Exercise> exercises = exerciseRepository.search(query);
//
// // Add to the results map
// results.put("posts", postResponses);
// results.put("trainingPrograms", trainingProgramResponses);
// results.put("exercises", exercises);
//
// return results;
// }

// public Map<String, Object> search(List<String> keywords) {
// // Search for posts, exercises, and training programs by looping over keywords
// List<PostResponse> posts = keywords.stream()
// .flatMap(keyword -> postRepository.search(keyword).stream())
// .distinct() // Remove duplicates
// .map(mapper::mapToPostResponse)
// .toList();
//
// List<Exercise> exercises = keywords.stream()
// .flatMap(keyword -> exerciseRepository.search(keyword).stream())
// .distinct()
// .toList();
//
// List<TrainingProgramResponse> trainingPrograms = keywords.stream()
// .flatMap(keyword -> trainingProgramRepository.search(keyword).stream())
// .distinct()
// .map(mapper::mapToTrainingProgramResponse)
// .toList();
//
// // Aggregate results into a map
// Map<String, Object> results = new HashMap<>();
// results.put("posts", posts);
// results.put("exercises", exercises);
// results.put("trainingPrograms", trainingPrograms);
//
// return results;
// }
public Map<String, Object> search(String query) {
// Split the query into individual keywords
String[] keywords = query.trim().split("\\s+");

// Initialize results with the first keyword
Set<Post> posts = new HashSet<>(postRepository.search(keywords[0]));
Set<Exercise> exercises = new HashSet<>(exerciseRepository.search(keywords[0]));
Set<TrainingProgram> trainingPrograms = new HashSet<>(trainingProgramRepository.search(keywords[0]));

// Intersect results for each subsequent keyword
for (int i = 1; i < keywords.length; i++) {
posts.retainAll(postRepository.search(keywords[i]));
exercises.retainAll(exerciseRepository.search(keywords[i]));
trainingPrograms.retainAll(trainingProgramRepository.search(keywords[i]));
}

// Map entities to DTOs
List<PostResponse> postResponses = posts.stream()
.map(mapper::mapToPostResponse)
.collect(Collectors.toList());

List<Exercise> exerciseResponses = exercises.stream()
.collect(Collectors.toList());

List<TrainingProgramResponse> trainingProgramResponses = trainingPrograms.stream()
.map(mapper::mapToTrainingProgramResponse)
.collect(Collectors.toList());

// Combine results into a single response
Map<String, Object> response = Map.of(
"posts", postResponses,
"exercises", exerciseResponses,
"trainingPrograms", trainingProgramResponses
);

return response;
}


}

0 comments on commit efcfff0

Please sign in to comment.