/**
* Copyright (C) 2011 JTalks.org Team
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
package org.jtalks.jcommune.model.dao.search.hibernate;
import java.util.Collections;
import java.util.List;
import org.apache.commons.lang.StringUtils;
import org.hibernate.Criteria;
import org.hibernate.SessionFactory;
import org.hibernate.criterion.Restrictions;
import org.hibernate.search.FullTextQuery;
import org.hibernate.search.FullTextSession;
import org.hibernate.search.query.dsl.QueryBuilder;
import org.jtalks.jcommune.model.dao.search.TopicSearchDao;
import org.jtalks.jcommune.model.dto.PageRequest;
import org.jtalks.jcommune.model.entity.Post;
import org.jtalks.jcommune.model.entity.Topic;
import org.jtalks.jcommune.model.search.SearchRequestFilter;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
/**
* Hibernate Search DAO implementation for operations with a {@link Topic}.
*
* @author Anuar Nurmakanov
*/
public class TopicHibernateSearchDao extends AbstractHibernateSearchDao
implements TopicSearchDao {
/**
* List of filters.
*/
private List<SearchRequestFilter> filters = Collections.emptyList();
/**
* @param sessionFactory the Hibernate SessionFactory
* @param filters the list of filters to correct the dirty search requests
*/
public TopicHibernateSearchDao(SessionFactory sessionFactory, List<SearchRequestFilter> filters) {
super(sessionFactory);
this.filters = filters;
}
/**
* Injects filters for search requests. It needed for testing.
*
* @param filters the list of filters to correct the dirty search requests
*/
void setFilters(List<SearchRequestFilter> filters) {
this.filters = filters;
}
/**
* {@inheritDoc}
*/
@Override
public Page<Topic> searchByTitleAndContent(String searchText,
PageRequest pageRequest,
List<Long> allowedBranchesIds) {
Page<Topic> searchResults = doSearch(searchText, pageRequest, allowedBranchesIds);
if (isSearchedAboveLastPage(searchResults)) {
pageRequest.adjustPageNumber(Long.valueOf(searchResults.getTotalElements()).intValue());
searchResults = doSearch(searchText, pageRequest, allowedBranchesIds);
}
return searchResults;
}
/**
* Perform actual search
*
* @param searchText the search text
* @param pageRequest contains information for pagination: page number, page
* size
* @param allowedBranchesIds list of allowed branches
* @return object that contains search results for one page(note, that one
* page may contain all search results) and information for pagination
*/
@SuppressWarnings("unchecked")
private Page<Topic> doSearch(String searchText, PageRequest pageRequest, List<Long> allowedBranchesIds) {
List<Topic> topics = Collections.emptyList();
int resultSize = 0;
//TODO The latest versions of the library filtering is not needed.
String filteredSearchText = applyFilters(searchText, filters).trim();
if (!StringUtils.isEmpty(filteredSearchText)) {
FullTextQuery query = createSearchQuery(getFullTextSession(), filteredSearchText, pageRequest);
Criteria criteria = getFullTextSession().createCriteria(Topic.class).add(
Restrictions.in("branch.id", allowedBranchesIds)
);
query.setCriteriaQuery(criteria);
topics = query.list();
resultSize = query.getResultSize();
}
return new PageImpl<Topic>(topics, pageRequest, resultSize);
}
/**
* Checks if this search was by made with too big page number specified
*
* @param searchResults search results
* @return true if page number is too big
*/
private boolean isSearchedAboveLastPage(Page<Topic> searchResults) {
return !searchResults.hasContent() && searchResults.getNumber() > searchResults.getTotalPages();
}
/**
* Builds a search query.
*
* @param fullTextSession the Hibernate Search session
* @param searchText the search text
* @param pageRequest contains information for pagination: page number, page size
* @return the search query
*/
private FullTextQuery createSearchQuery(
FullTextSession fullTextSession,
String searchText,
PageRequest pageRequest) {
QueryBuilder queryBuilder = fullTextSession.
getSearchFactory().
buildQueryBuilder().
forEntity(Topic.class).
get();
org.apache.lucene.search.Query luceneQuery = queryBuilder.
keyword().
onField(Topic.TOPIC_TITLE_FIELD_DEF).
andField(Topic.TOPIC_TITLE_FIELD_RU).
andField(Topic.TOPIC_POSTS_PREFIX + Post.POST_CONTENT_FIELD_DEF).
andField(Topic.TOPIC_POSTS_PREFIX + Post.POST_CONTENT_FIELD_RU).
matching(searchText).
createQuery();
FullTextQuery query = fullTextSession.createFullTextQuery(luceneQuery);
query.setFirstResult(pageRequest.getOffset());
query.setMaxResults(pageRequest.getPageSize());
return query;
}
/**
* This method filters the text.
*
* @param searchText the search text
* @param filters the list of filters
* @return the filtered search text
*/
private String applyFilters(String searchText, List<SearchRequestFilter> filters) {
for (SearchRequestFilter filter : filters) {
searchText = filter.filter(searchText);
}
return searchText;
}
/**
* {@inheritDoc}
*/
@Override
public void rebuildIndex() {
getFullTextSession().createIndexer(Topic.class).start();
}
}