org.shredzone.cilla.service.search.strategy.LuceneSearchStrategy.java Source code

Java tutorial

Introduction

Here is the source code for org.shredzone.cilla.service.search.strategy.LuceneSearchStrategy.java

Source

/*
 * cilla - Blog Management System
 *
 * Copyright (C) 2012 Richard "Shred" Krber
 *   http://cilla.shredzone.org
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published
 * by the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program 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 General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
package org.shredzone.cilla.service.search.strategy;

import java.io.StringReader;
import java.util.List;
import java.util.stream.Collectors;

import javax.annotation.Resource;

import org.apache.lucene.analysis.SimpleAnalyzer;
import org.apache.lucene.analysis.TokenStream;
import org.apache.lucene.queryParser.ParseException;
import org.apache.lucene.search.Query;
import org.apache.lucene.search.highlight.Formatter;
import org.apache.lucene.search.highlight.Fragmenter;
import org.apache.lucene.search.highlight.Highlighter;
import org.apache.lucene.search.highlight.QueryScorer;
import org.apache.lucene.util.Version;
import org.hibernate.Criteria;
import org.shredzone.cilla.core.model.Page;
import org.shredzone.cilla.core.repository.SearchDao;
import org.shredzone.cilla.core.search.PageBridge;
import org.shredzone.cilla.service.search.FilterModel;
import org.shredzone.cilla.service.search.impl.SearchResultImpl;
import org.shredzone.cilla.service.search.renderer.SearchResultRenderer;
import org.shredzone.cilla.ws.exception.CillaServiceException;
import org.springframework.stereotype.Component;

/**
 * A {@link SearchStrategy} that uses Lucene for finding. This strategy is used when a
 * query string is set in the {@link FilterModel}.
 *
 * @author Richard "Shred" Krber
 */
@Component
public class LuceneSearchStrategy extends AbstractSearchStrategy {

    private @Resource SearchDao searchDao;
    private @Resource SearchResultRenderer searchResultRenderer;

    @Override
    public void count(SearchResultImpl result) throws CillaServiceException {
        Query query = createQuery(result);
        Criteria crit = createCriteria(result);

        result.setCount(searchDao.count(query, crit));
    }

    @Override
    public void search(SearchResultImpl result) throws CillaServiceException {
        Query query = createQuery(result);
        Criteria crit = createPaginatedCriteria(result);

        List<Page> resultset = searchDao.fetch(query, crit);
        result.setPages(resultset);
        result.setHighlighted(createHighlights(query, resultset));
    }

    /**
     * Creates a {@link Query} for the search result.
     *
     * @param result
     *            {@link SearchResultImpl} to create a {@link Query} for
     * @return {@link Query} that was created
     */
    private Query createQuery(SearchResultImpl result) throws CillaServiceException {
        FilterModel filter = result.getFilter();
        try {
            return searchDao.parseQuery(filter.getQuery(), filter.getLocale());
        } catch (ParseException ex) {
            throw new CillaServiceException("Could not parse query '" + filter.getQuery() + "'", ex);
        }
    }

    /**
     * Creates a list of highlights for a search result.
     *
     * @param pq
     *            {@link Query} that was used
     * @param result
     *            List of {@link Page} results
     * @return matching list of text extracts with highlights
     */
    private List<String> createHighlights(Query pq, List<Page> result) {
        QueryScorer scorer = new QueryScorer(pq, "text");
        Fragmenter fragmenter = searchResultRenderer.createFragmenter(scorer);
        Formatter formatter = searchResultRenderer.createFormatter();

        Highlighter hilighter = new Highlighter(formatter, scorer);
        hilighter.setTextFragmenter(fragmenter);

        PageBridge bridge = new PageBridge();

        return result.stream().parallel().map(bridge::objectToString).map(plain -> highlight(plain, hilighter))
                .collect(Collectors.toList());
    }

    /**
     * Highlight the contents by search result.
     *
     * @param content
     *            Plain text content to highlight
     * @param hilighter
     *            {@link Highlighter} to use
     * @return Highlighted content
     */
    private String highlight(String content, Highlighter hilighter) {
        try (SimpleAnalyzer analyzer = new SimpleAnalyzer(Version.LUCENE_36)) {
            TokenStream tokenStream = analyzer.tokenStream("text", new StringReader(content));

            StringBuilder sb = new StringBuilder();
            sb.append(searchResultRenderer.getHeader());
            sb.append(hilighter.getBestFragments(tokenStream, content, searchResultRenderer.getMaxResults(),
                    searchResultRenderer.getSeparator()));
            sb.append(searchResultRenderer.getFooter());
            return sb.toString();
        } catch (Exception ex) {
            return content;
        }
    }

}