de.powerstaff.business.service.impl.ProfileSearchServiceImpl.java Source code

Java tutorial

Introduction

Here is the source code for de.powerstaff.business.service.impl.ProfileSearchServiceImpl.java

Source

/**
 * Mogwai PowerStaff. Copyright (C) 2002 The Mogwai Project.
 *
 * 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 de.powerstaff.business.service.impl;

import de.mogwai.common.usercontext.UserContextHolder;
import de.powerstaff.business.dao.ProfileSearchDAO;
import de.powerstaff.business.dto.DataPage;
import de.powerstaff.business.dto.ProfileSearchEntry;
import de.powerstaff.business.dto.ProfileSearchInfoDetail;
import de.powerstaff.business.entity.*;
import de.powerstaff.business.lucene.analysis.ProfileAnalyzerFactory;
import de.powerstaff.business.service.*;
import org.apache.commons.lang.StringUtils;
import org.apache.lucene.analysis.Analyzer;
import org.apache.lucene.analysis.CachingTokenFilter;
import org.apache.lucene.document.Document;
import org.apache.lucene.index.IndexReader;
import org.apache.lucene.index.Term;
import org.apache.lucene.queryParser.ParseException;
import org.apache.lucene.search.BooleanClause.Occur;
import org.apache.lucene.search.*;
import org.apache.lucene.search.highlight.Highlighter;
import org.apache.lucene.search.highlight.InvalidTokenOffsetsException;
import org.apache.lucene.search.highlight.QueryScorer;
import org.apache.lucene.search.highlight.SpanGradientFormatter;
import org.apache.lucene.search.similar.MoreLikeThis;
import org.hibernate.SessionFactory;
import org.hibernate.search.FullTextQuery;
import org.hibernate.search.FullTextSession;
import org.hibernate.search.Search;
import org.hibernate.search.SearchFactory;
import org.hibernate.search.store.DirectoryProvider;
import org.slf4j.LoggerFactory;

import java.io.File;
import java.io.IOException;
import java.io.StringReader;
import java.sql.Date;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;

/**
 * @author Mirko Sertic
 */
public class ProfileSearchServiceImpl implements ProfileSearchService {

    private static final org.slf4j.Logger LOGGER = LoggerFactory.getLogger(ProfileSearchServiceImpl.class);

    static {
        BooleanQuery.setMaxClauseCount(8192);
    }

    private PowerstaffSystemParameterService systemParameterService;

    private ProfileSearchDAO profileSearchDAO;

    private SessionFactory sessionFactory;

    private FSCache fileSystemCache;

    public void setFileSystemCache(FSCache fileSystemCache) {
        this.fileSystemCache = fileSystemCache;
    }

    public void setSystemParameterService(PowerstaffSystemParameterService systemParameterService) {
        this.systemParameterService = systemParameterService;
    }

    public void setProfileSearchDAO(ProfileSearchDAO profileSearchDAO) {
        this.profileSearchDAO = profileSearchDAO;
    }

    public void setSessionFactory(SessionFactory sessionFactory) {
        this.sessionFactory = sessionFactory;
    }

    private Query getRealQuery(SavedProfileSearch aRequest, Analyzer aAnalyzer) throws IOException, ParseException {

        BooleanQuery theQuery = new BooleanQuery();

        GoogleStyleQueryParser theParser = new GoogleStyleQueryParser(null);

        theQuery.add(theParser.parseQuery(aRequest.getProfileContent(), aAnalyzer, ProfileIndexerService.CONTENT),
                Occur.MUST);

        if (!StringUtils.isEmpty(aRequest.getPlz())) {
            theQuery.add(
                    new WildcardQuery(new Term(ProfileIndexerService.PLZ, aRequest.getPlz().replace("%", "*"))),
                    Occur.MUST);
        }

        return theQuery;
    }

    protected String getHighlightedSearchResult(Analyzer aAnalyzer, Highlighter aHighlighter, String aContent,
            Query aQuery) throws IOException, InvalidTokenOffsetsException {

        CachingTokenFilter tokenStream = new CachingTokenFilter(
                aAnalyzer.tokenStream(ProfileIndexerService.CONTENT, new StringReader(aContent)));

        return aHighlighter.getBestFragments(tokenStream, aContent, 5, " ... ");
    }

    public SavedProfileSearch getSearchRequestForUser(String aUserId) {
        return profileSearchDAO.getSavedSearchForUser(aUserId);
    }

    public SavedProfileSearch getSearchRequest(long aSearchRequestId) {
        return profileSearchDAO.getSavedSearchById(aSearchRequestId);
    }

    @Override
    public void saveSearchRequest(SavedProfileSearch searchRequest, boolean cleanup)
            throws OptimisticLockException {

        User theUser = (User) UserContextHolder.getUserContext().getAuthenticatable();

        if (cleanup) {
            searchRequest.getProfilesToIgnore().clear();
        }

        // If the search was for a project, it will also be saved as the last search for a user
        if (searchRequest.getProject() != null) {

            boolean isNew = false;
            SavedProfileSearch theSearchForUser = profileSearchDAO.getSavedSearchForUser(theUser.getUsername());
            if (theSearchForUser == null) {
                theSearchForUser = new SavedProfileSearch();
                theSearchForUser.setUser(theUser);
                isNew = true;
            } else {
                if (cleanup) {
                    searchRequest.getProfilesToIgnore().clear();
                }
            }

            theSearchForUser.setPlz(searchRequest.getPlz());
            theSearchForUser.setProfileContent(searchRequest.getProfileContent());
            theSearchForUser.setSortierung(searchRequest.getSortierung());
            theSearchForUser.setStundensatzVon(searchRequest.getStundensatzVon());
            theSearchForUser.setStundensatzBis(searchRequest.getStundensatzBis());
            theSearchForUser.getProfilesToIgnore().addAll(searchRequest.getProfilesToIgnore());

            if (isNew) {
                // Auf keinen Fall die bereits existierende Id vom SavedSearchRequest pro projekt bernehmen!
                theSearchForUser.setId(null);
            }

            // Keine Zuordnung zu einem Projekt!
            theSearchForUser.setProject(null);

            profileSearchDAO.save(theSearchForUser);
        }

        profileSearchDAO.save(searchRequest);
    }

    @Override
    public DataPage<ProfileSearchEntry> findProfileDataPage(SavedProfileSearch aRequest, int startRow, int pageSize)
            throws Exception {

        if (aRequest.getId() == null) {
            // Kann passieren, wenn die Suche das erste mal aufgerufen wird
            return new DataPage<ProfileSearchEntry>(0, 0, new ArrayList<ProfileSearchEntry>());
        }

        Analyzer theAnalyzer = ProfileAnalyzerFactory.createAnalyzer();

        FullTextSession theSession = Search.getFullTextSession(sessionFactory.getCurrentSession());

        Query theQuery = getRealQuery(aRequest, theAnalyzer);

        LOGGER.info("Search query is " + theQuery + " from " + startRow + " with pagesize " + pageSize);

        Highlighter theHighlighter = new Highlighter(new SpanGradientFormatter(1, "#000000", "#0000FF", null, null),
                new QueryScorer(theQuery));

        BooleanQuery theRealQuery = new BooleanQuery();
        theRealQuery.add(theQuery, Occur.MUST);

        if (aRequest != null) {
            for (String theId : aRequest.getProfilesToIgnore()) {
                theRealQuery.add(new TermQuery(new Term(ProfileIndexerService.UNIQUE_ID, theId)), Occur.MUST_NOT);
            }
        }

        LOGGER.info("Query with ignore is " + theRealQuery);

        Sort theSort = null;
        if (!StringUtils.isEmpty(aRequest.getSortierung())) {
            int theSortType = SortField.STRING;
            boolean theReverse = false;

            String theSortField = aRequest.getSortierungField();

            if (ProfileIndexerService.STUNDENSATZ.equals(theSortField)) {
                theSortType = SortField.LONG;
            }
            if (ProfileIndexerService.VERFUEGBARKEIT.equals(theSortField)) {
                theReverse = true;
            }
            if (ProfileIndexerService.LETZTERKONTAKT.equals(theSortField)) {
                theReverse = true;
            }

            if (aRequest.isSortierungReverse()) {
                theReverse = !theReverse;
            }

            theSort = new Sort(new SortField(theSortField, theSortType, theReverse));
        }

        List<Filter> theFilterList = new ArrayList<Filter>();
        TermsFilter theContactForbidden = new TermsFilter();
        theContactForbidden.addTerm(new Term(ProfileIndexerService.KONTAKTSPERRE, "false"));
        theFilterList.add(theContactForbidden);

        if (aRequest.getStundensatzVon() != null || aRequest.getStundensatzBis() != null) {
            if (aRequest.getStundensatzVon() != null) {
                theFilterList.add(NumericRangeFilter.newLongRange(ProfileIndexerService.STUNDENSATZ,
                        aRequest.getStundensatzVon(), Long.MAX_VALUE, true, true));
            }
            if (aRequest.getStundensatzBis() != null) {
                theFilterList.add(NumericRangeFilter.newLongRange(ProfileIndexerService.STUNDENSATZ, 0l,
                        aRequest.getStundensatzBis(), true, true));
            }
        }

        Filter theFilter = new ChainedFilter(theFilterList.toArray(new Filter[theFilterList.size()]),
                ChainedFilter.AND);

        int theEnd = startRow + pageSize;

        FullTextQuery theHibernateQuery = theSession.createFullTextQuery(theRealQuery, Freelancer.class);
        if (theFilter != null) {
            theHibernateQuery.setFilter(theFilter);
        }
        if (theSort != null) {
            theHibernateQuery.setSort(theSort);
        }
        theHibernateQuery.setFirstResult(startRow);
        theHibernateQuery.setMaxResults(theEnd - startRow);
        theHibernateQuery.setProjection(FullTextQuery.THIS, FullTextQuery.DOCUMENT);

        List<ProfileSearchEntry> theResult = new ArrayList<ProfileSearchEntry>();

        for (Object theSingleEntity : theHibernateQuery.list()) {
            Object[] theRow = (Object[]) theSingleEntity;
            Freelancer theFreelancer = (Freelancer) theRow[0];
            Document theDocument = (Document) theRow[1];
            ProfileSearchEntry theEntry = createResultEntry(theAnalyzer, theQuery, theHighlighter, theFreelancer,
                    theDocument);

            theResult.add(theEntry);
        }

        return new DataPage<ProfileSearchEntry>(theHibernateQuery.getResultSize(), startRow, theResult);
    }

    private ProfileSearchEntry createResultEntry(Analyzer aAnalyzer, Query aQuery, Highlighter aHighlighter,
            Freelancer aFreelancer, Document aDocument) throws IOException, InvalidTokenOffsetsException {
        ProfileSearchEntry theEntry = new ProfileSearchEntry();
        theEntry.setCode(aFreelancer.getCode());
        theEntry.setDocumentId("" + aFreelancer.getId());

        ProfileSearchInfoDetail theDetail = new ProfileSearchInfoDetail();
        theDetail.setId(aFreelancer.getId());
        theDetail.setName1(aFreelancer.getName1());
        theDetail.setName2(aFreelancer.getName2());
        theDetail.setAvailability(aFreelancer.getAvailabilityAsDate());
        theDetail.setPlz(aFreelancer.getPlz());
        theDetail.setStundensatz(aFreelancer.getSallaryLong());
        theDetail.setContactforbidden(aFreelancer.isContactforbidden());
        theDetail.setContacts(new ArrayList<FreelancerContact>(aFreelancer.getContacts()));
        theDetail.setLastContact(aFreelancer.getLastContactDate());
        for (FreelancerToTag theTag : aFreelancer.getTags()) {
            theDetail.getTags().add(theTag.getTag());
        }

        String theContent = aDocument.get(ProfileIndexerService.ORIG_CONTENT);
        theEntry.setHighlightResult(getHighlightedSearchResult(aAnalyzer, aHighlighter, theContent, aQuery));

        theEntry.setFreelancer(theDetail);

        return theEntry;
    }

    @Override
    public void removeSavedSearchEntry(SavedProfileSearch searchRequest, String aDocumentId)
            throws OptimisticLockException {

        SavedProfileSearch theSearch = profileSearchDAO.getSavedSearchById(searchRequest.getId());

        theSearch.getProfilesToIgnore().add(aDocumentId);

        profileSearchDAO.save(theSearch);
    }

    @Override
    public int getPageSize() {
        return systemParameterService.getMaxSearchResult();
    }

    @Override
    public synchronized List<FreelancerProfile> loadProfilesFor(Freelancer aFreelancer) {
        List<FreelancerProfile> theProfiles = new ArrayList<FreelancerProfile>();

        if (aFreelancer != null && aFreelancer.getCode() != null) {

            String theCode = aFreelancer.getCode().trim().toLowerCase();
            Set<File> theFilesForCode = fileSystemCache.getFilesForCode(theCode);

            if (theFilesForCode != null) {

                String theClientBaseDir = systemParameterService.getIndexerNetworkDir();
                if (!theClientBaseDir.endsWith("\\")) {
                    theClientBaseDir += "\\";
                }
                String theServerBaseDir = systemParameterService.getIndexerSourcePath();
                if (!theServerBaseDir.endsWith("\\")) {
                    theServerBaseDir += "\\";
                }

                SimpleDateFormat theFormat = new SimpleDateFormat("dd.MM.yyyy");

                for (File theFile : theFilesForCode) {
                    FreelancerProfile theProfile = new FreelancerProfile();
                    theProfile.setName(theFile.getName());
                    theProfile.setInfotext("Aktualisiert : " + theFormat.format(new Date(theFile.lastModified())));
                    theProfile.setFileOnserver(theFile);

                    String theStrippedPath = theFile.toString().substring(theServerBaseDir.length());
                    theProfile.setFileName(theClientBaseDir + theStrippedPath);

                    theProfiles.add(theProfile);
                }
            }

        }
        return theProfiles;
    }

    @Override
    public List<ProfileSearchEntry> getSimilarFreelancer(Freelancer aFreelancer) {
        List<ProfileSearchEntry> theResult = new ArrayList<ProfileSearchEntry>();
        if (aFreelancer != null && aFreelancer.getId() != null) {

            FullTextSession theSession = Search.getFullTextSession(sessionFactory.getCurrentSession());
            SearchFactory theSearchFactory = theSession.getSearchFactory();

            Analyzer theAnalyzer = ProfileAnalyzerFactory.createAnalyzer();

            DirectoryProvider theFreeelancerProvider = theSearchFactory.getDirectoryProviders(Freelancer.class)[0];

            IndexReader theIndexReader = null;

            try {
                theIndexReader = theSearchFactory.getReaderProvider().openReader(theFreeelancerProvider);

                MoreLikeThis theMoreLikeThis = new MoreLikeThis(theIndexReader);

                // Zuerst den Freiberufler raussuchen
                Query theQuery = new TermQuery(
                        new Term(ProfileIndexerService.UNIQUE_ID, aFreelancer.getId().toString()));
                FullTextQuery theHibernateQuery = theSession.createFullTextQuery(theQuery, Freelancer.class);
                theHibernateQuery.setProjection(FullTextQuery.THIS, FullTextQuery.DOCUMENT);

                for (Object theSingleEntity : theHibernateQuery.list()) {
                    Object[] theRow = (Object[]) theSingleEntity;
                    Freelancer theFreelancer = (Freelancer) theRow[0];
                    Document theDocument = (Document) theRow[1];

                    theMoreLikeThis.setMinDocFreq(1);
                    theMoreLikeThis.setMinTermFreq(1);
                    theMoreLikeThis.setAnalyzer(theAnalyzer);
                    theMoreLikeThis.setFieldNames(new String[] { ProfileIndexerService.CONTENT });
                    Query theMltQuery = theMoreLikeThis
                            .like(new StringReader(theDocument.get(ProfileIndexerService.ORIG_CONTENT)));

                    FullTextQuery theMoreLikeThisQuery = theSession.createFullTextQuery(theMltQuery,
                            Freelancer.class);
                    theMoreLikeThisQuery.setProjection(FullTextQuery.THIS, FullTextQuery.DOCUMENT,
                            FullTextQuery.SCORE);
                    theMoreLikeThisQuery.setMaxResults(50);

                    Highlighter theHighlighter = new Highlighter(
                            new SpanGradientFormatter(1, "#000000", "#0000FF", null, null),
                            new QueryScorer(theMltQuery));

                    for (Object theSingleMltEntry : theMoreLikeThisQuery.list()) {
                        Object[] theMltRow = (Object[]) theSingleMltEntry;
                        Freelancer theMltFreelancer = (Freelancer) theMltRow[0];
                        Document theMltDocument = (Document) theMltRow[1];
                        Float theMltScore = (Float) theMltRow[2];

                        if (theMltFreelancer != theFreelancer) {
                            // Einen gefunden
                            ProfileSearchEntry theEntry = createResultEntry(theAnalyzer, theMltQuery,
                                    theHighlighter, theMltFreelancer, theMltDocument);
                            theResult.add(theEntry);
                        }
                    }

                }
            } catch (Exception e) {
                throw new RuntimeException(e);
            } finally {
                if (theIndexReader != null) {
                    theSearchFactory.getReaderProvider().closeReader(theIndexReader);
                }
            }

        }
        return theResult;
    }

    @Override
    public List<ProfileSearchEntry> getSimilarFreelancer(Project aProject) {
        List<ProfileSearchEntry> theResult = new ArrayList<ProfileSearchEntry>();
        if (aProject != null && aProject.getId() != null) {

            FullTextSession theSession = Search.getFullTextSession(sessionFactory.getCurrentSession());
            SearchFactory theSearchFactory = theSession.getSearchFactory();

            Analyzer theAnalyzer = ProfileAnalyzerFactory.createAnalyzer();

            DirectoryProvider theFreeelancerProvider = theSearchFactory.getDirectoryProviders(Freelancer.class)[0];

            IndexReader theIndexReader = null;

            try {
                theIndexReader = theSearchFactory.getReaderProvider().openReader(theFreeelancerProvider);

                MoreLikeThis theMoreLikeThis = new MoreLikeThis(theIndexReader);

                theMoreLikeThis.setMinDocFreq(1);
                theMoreLikeThis.setMinTermFreq(1);
                theMoreLikeThis.setAnalyzer(theAnalyzer);
                theMoreLikeThis.setFieldNames(new String[] { ProfileIndexerService.CONTENT });
                Query theMltQuery = theMoreLikeThis.like(new StringReader(aProject.getDescriptionShort() + " "
                        + aProject.getDescriptionLong() + " " + aProject.getSkills()));

                FullTextQuery theMoreLikeThisQuery = theSession.createFullTextQuery(theMltQuery, Freelancer.class);
                theMoreLikeThisQuery.setProjection(FullTextQuery.THIS, FullTextQuery.DOCUMENT, FullTextQuery.SCORE);
                theMoreLikeThisQuery.setMaxResults(50);

                Highlighter theHighlighter = new Highlighter(
                        new SpanGradientFormatter(1, "#000000", "#0000FF", null, null),
                        new QueryScorer(theMltQuery));

                for (Object theSingleMltEntry : theMoreLikeThisQuery.list()) {
                    Object[] theMltRow = (Object[]) theSingleMltEntry;
                    Freelancer theMltFreelancer = (Freelancer) theMltRow[0];
                    Document theMltDocument = (Document) theMltRow[1];
                    Float theMltScore = (Float) theMltRow[2];

                    ProfileSearchEntry theEntry = createResultEntry(theAnalyzer, theMltQuery, theHighlighter,
                            theMltFreelancer, theMltDocument);
                    theResult.add(theEntry);
                }

            } catch (Exception e) {
                throw new RuntimeException(e);
            } finally {
                if (theIndexReader != null) {
                    theSearchFactory.getReaderProvider().closeReader(theIndexReader);
                }
            }

        }
        return theResult;
    }
}