Add search engine.

This commit is contained in:
2018-05-20 23:19:03 +02:00
parent cdefec9124
commit 5b02caf83b
6 changed files with 177 additions and 6 deletions

View File

@@ -0,0 +1,35 @@
package org.codiki.core.entities.business;
import org.codiki.core.entities.persistence.Post;
public class SearchEntity {
private Post post;
private int score;
public SearchEntity(Post post) {
super();
this.post = post;
}
public Post getPost() {
return post;
}
public void setPost(Post post) {
this.post = post;
}
public int getScore() {
return score;
}
public void increaseScore(int pScoreToAdd) {
score += pScoreToAdd;
}
public void decreaseScore(int pScoreToRemove) {
score -= pScoreToRemove;
}
}

View File

@@ -10,7 +10,7 @@ import org.springframework.data.repository.query.Param;
import org.springframework.stereotype.Repository;
@Repository
public interface PostRepository extends CrudRepository<Post, Long> {
public interface PostRepository extends CrudRepository<Post, Long>, PostSearchRepository {
@Query("SELECT p FROM Post p WHERE p.key = :postKey")
Optional<Post> getByKey(@Param("postKey") final String pPostKey);

View File

@@ -0,0 +1,9 @@
package org.codiki.core.repositories;
import java.util.List;
import org.codiki.core.entities.persistence.Post;
public interface PostSearchRepository {
List<Post> search(final String[] pCriterias);
}

View File

@@ -0,0 +1,51 @@
package org.codiki.core.repositories;
import java.util.LinkedList;
import java.util.List;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.PersistenceException;
import javax.persistence.Query;
import org.codiki.core.entities.persistence.Post;
import org.codiki.core.utils.StringUtils;
public class PostSearchRepositoryImpl implements PostSearchRepository {
@PersistenceContext
private EntityManager em;
@SuppressWarnings("unchecked")
@Override
public List<Post> search(final String[] pCriterias) {
List<Post> result = new LinkedList<>();
final String queryStr = buildSearchQuery(pCriterias);
try {
final Query query = em.createQuery(queryStr);
for(int i = 1 ; i <= pCriterias.length ; i++) {
query.setParameter(StringUtils.concat("c", i), StringUtils.concat('%', pCriterias[i - 1].toLowerCase(), '%'));
}
result = query.getResultList();
} catch(final PersistenceException pEx) {
// TODO
}
return result;
}
protected String buildSearchQuery(final String[] pCriterias) {
final StringBuilder result = new StringBuilder("SELECT p FROM Post p JOIN FETCH p.category JOIN FETCH p.author ");
for(int i = 1 ; i <= pCriterias.length ; i++) {
result.append(StringUtils.concat((i == 1) ? "WHERE" : "OR", " LOWER(p.title) LIKE :c", i,
" OR LOWER(p.description) LIKE :c", i,
" OR LOWER(p.text) LIKE :c", i,
" OR LOWER(p.category.name) LIKE :c", i,
" OR LOWER(p.author.name) LIKE :c", i,
" OR LOWER(p.author.email) LIKE :c", i, " "));
}
return result.toString();
}
}

View File

@@ -54,7 +54,9 @@ public class PostController {
public PostDTO getByKey(@PathVariable("postKey") final String pPostKey,
final HttpServletResponse response) {
final PostDTO result = getByKeyAndSource(pPostKey, response);
result.setText(parserService.parse(result.getText()));
if(result != null) {
result.setText(parserService.parse(result.getText()));
}
return result;
}
@@ -79,10 +81,9 @@ public class PostController {
.map(PostDTO::new).collect(Collectors.toList());
}
@PostMapping("/search")
public List<Post> search() {
// TODO
return null;
@GetMapping("/search/{searchCriteria}")
public List<PostDTO> search(@PathVariable("searchCriteria") final String pSearchCriteria) {
return postService.search(pSearchCriteria);
}
@GetMapping("/byCategory/{categoryId}")

View File

@@ -1,12 +1,18 @@
package org.codiki.posts;
import java.io.IOException;
import java.util.Collections;
import java.util.Date;
import java.util.LinkedList;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.lang3.StringUtils;
import org.codiki.core.entities.business.SearchEntity;
import org.codiki.core.entities.dto.PostDTO;
import org.codiki.core.entities.persistence.Category;
import org.codiki.core.entities.persistence.Post;
@@ -19,6 +25,17 @@ import org.springframework.stereotype.Service;
@Service
public class PostService {
private static final int SCORE_TITLE = 50;
private static final int SCORE_DESCRIPTION = 5;
private static final int SCORE_TEXT = 1;
private static final int SCORE_CATEGORY = 30;
private static final int SCORE_AUTHOR_NAME = 40;
private static final int SCORE_AUTHOR_EMAIL = 40;
@Autowired
private PostRepository postRepository;
@@ -96,4 +113,62 @@ public class PostService {
pResponse.sendError(HttpServletResponse.SC_NOT_FOUND);
}
}
public List<PostDTO> search(String pSearchCriteria) {
final String[] criteriasArray = pSearchCriteria.split(" ");
final List<SearchEntity> listSearchEntities = new LinkedList<>();
postRepository.search(criteriasArray).stream().map(SearchEntity::new).forEach(e -> {
calculateScore(e, criteriasArray);
listSearchEntities.add(e);
});
Collections.sort(listSearchEntities, (e1, e2) -> e1.getScore() - e2.getScore());
return listSearchEntities.stream().map(SearchEntity::getPost).map(PostDTO::new).collect(Collectors.toList());
}
void calculateScore(final SearchEntity searchPost, final String[] pCriteriasArray) {
for(final String criteria : pCriteriasArray) {
calculateScoreForTitle(searchPost, criteria);
calculateScoreForDescription(searchPost, criteria);
calculateScoreForText(searchPost, criteria);
calculateScoreForCategory(searchPost, criteria);
calculateScoreForAuthorName(searchPost, criteria);
calculateScoreForAuthorEmail(searchPost, criteria);
}
}
private void calculateScoreForTitle(final SearchEntity pSearchPost, final String pCriteria) {
if(pSearchPost.getPost().getTitle().contains(pCriteria)) {
pSearchPost.increaseScore(SCORE_TITLE);
}
}
private void calculateScoreForDescription(final SearchEntity pSearchPost, final String pCriteria) {
final int criteriaOccurence = StringUtils.countMatches(pSearchPost.getPost().getDescription(), pCriteria);
pSearchPost.increaseScore(criteriaOccurence * SCORE_DESCRIPTION);
}
private void calculateScoreForText(final SearchEntity pSearchPost, final String pCriteria) {
final int criteriaOccurence = StringUtils.countMatches(pSearchPost.getPost().getText(), pCriteria);
pSearchPost.increaseScore(criteriaOccurence * SCORE_TEXT);
}
private void calculateScoreForCategory(final SearchEntity pSearchPost, final String pCriteria) {
if(pSearchPost.getPost().getCategory().getName().contains(pCriteria)) {
pSearchPost.increaseScore(SCORE_CATEGORY);
}
}
private void calculateScoreForAuthorName(final SearchEntity pSearchPost, final String pCriteria) {
if(pSearchPost.getPost().getAuthor().getName().contains(pCriteria)) {
pSearchPost.increaseScore(SCORE_AUTHOR_NAME);
}
}
private void calculateScoreForAuthorEmail(final SearchEntity pSearchPost, final String pCriteria) {
if(pSearchPost.getPost().getAuthor().getEmail().contains(pCriteria)) {
pSearchPost.increaseScore(SCORE_AUTHOR_EMAIL);
}
}
}