package com.tyndalehouse.step.core.service.impl.suggestion; import com.tyndalehouse.step.core.data.EntityIndexReader; import com.tyndalehouse.step.core.data.EntityManager; import com.tyndalehouse.step.core.data.common.TermsAndMaxCount; import com.tyndalehouse.step.core.models.search.SubjectSuggestion; import com.tyndalehouse.step.core.service.helpers.SuggestionContext; import com.tyndalehouse.step.core.service.impl.SearchType; import com.tyndalehouse.step.core.service.jsword.JSwordPassageService; import com.tyndalehouse.step.core.service.jsword.JSwordSearchService; import com.tyndalehouse.step.core.utils.LuceneUtils; import org.crosswire.jsword.index.lucene.LuceneIndex; import org.tartarus.snowball.ext.PorterStemmer; import javax.inject.Inject; import java.util.Collection; import java.util.HashSet; import java.util.Map; import java.util.TreeMap; /** * @author chrisburrell */ public class SubjectSuggestionServiceImpl extends AbstractIgnoreMergedListSuggestionServiceImpl<SubjectSuggestion> { private final EntityIndexReader naves; private final JSwordSearchService jSwordSearchService; @Inject public SubjectSuggestionServiceImpl(final EntityManager entityManager, JSwordSearchService jSwordSearchService) { this.jSwordSearchService = jSwordSearchService; naves = entityManager.getReader("nave"); } @Override public SubjectSuggestion[] getExactTerms(final SuggestionContext context, final int max, final boolean popularSort) { final Map<String, SubjectSuggestion> suggestions = new TreeMap<String, SubjectSuggestion>(); final PorterStemmer stemmer = new PorterStemmer(); //add the full term final String input = context.getInput(); addSubjectTerms(suggestions, stemmer, LuceneUtils.getAllTermsPrefixedWith(true, false, this.jSwordSearchService.getIndexSearcher(JSwordPassageService.REFERENCE_BOOK), LuceneIndex.FIELD_HEADING, input, max).getTerms(), SearchType.SUBJECT_SIMPLE); addSubjectTerms(suggestions, stemmer, this.naves.findSetOfTerms(true, input, max, "root"), SearchType.SUBJECT_EXTENDED); addSubjectTerms(suggestions, stemmer, this.naves.findSetOfTerms(true, input, max, "fullTerm"), SearchType.SUBJECT_FULL); return suggestions.values().toArray(new SubjectSuggestion[suggestions.size()]); } @Override public SubjectSuggestion[] collectNonExactMatches(final TermsAndMaxCount<SubjectSuggestion> collector, final SuggestionContext context, final SubjectSuggestion[] alreadyRetrieved, final int leftToCollect) { final Map<String, SubjectSuggestion> suggestions = new TreeMap<String, SubjectSuggestion>(); final PorterStemmer stemmer = new PorterStemmer(); addExistingMappings(suggestions, stemmer, alreadyRetrieved); final String input = context.getInput(); final TermsAndMaxCount termsFromHeadings = LuceneUtils.getAllTermsPrefixedWith(false, false, this.jSwordSearchService.getIndexSearcher(JSwordPassageService.REFERENCE_BOOK), LuceneIndex.FIELD_HEADING, input, leftToCollect); final TermsAndMaxCount termsFromSimpleNave = this.naves.findSetOfTermsWithCounts(false, true, input, leftToCollect, "root"); final TermsAndMaxCount termsFromFullNave = this.naves.findSetOfTermsWithCounts(false, true, input, leftToCollect, "fullTerm"); addSubjectTerms(suggestions, stemmer, termsFromHeadings.getTerms(), SearchType.SUBJECT_SIMPLE); addSubjectTerms(suggestions, stemmer, termsFromSimpleNave.getTerms(), SearchType.SUBJECT_EXTENDED); addSubjectTerms(suggestions, stemmer, termsFromFullNave.getTerms(), SearchType.SUBJECT_FULL); // termsFromHeadings.setTotalCount(termsFromHeadings.getTotalCount() - addSubjectTerms(suggestions, stemmer, termsFromHeadings.getTerms(), SearchType.SUBJECT_SIMPLE)); // termsFromSimpleNave.setTotalCount(termsFromSimpleNave.getTotalCount() - addSubjectTerms(suggestions, stemmer, termsFromSimpleNave.getTerms(), SearchType.SUBJECT_EXTENDED)); // termsFromFullNave.setTotalCount(termsFromFullNave.getTotalCount() - addSubjectTerms(suggestions, stemmer, termsFromFullNave.getTerms(), SearchType.SUBJECT_FULL)); TermsAndMaxCount countsAndResults = new TermsAndMaxCount(); countsAndResults.setTerms(new HashSet<SubjectSuggestion>(suggestions.values())); collector.setTotalCount(suggestions.size()); return suggestions.values().toArray(new SubjectSuggestion[countsAndResults.getTerms().size()]); } /** * Adds the existing mappings back in * @param suggestions a list of suggestions * @param stemmer the stemmer itself * @param alreadyRetrieved the existing entries */ private void addExistingMappings(final Map<String, SubjectSuggestion> suggestions, final PorterStemmer stemmer, final SubjectSuggestion[] alreadyRetrieved) { if(alreadyRetrieved == null) { return; } for(SubjectSuggestion s : alreadyRetrieved) { stemmer.setCurrent(s.getValue()); stemmer.stem(); String stem = stemmer.getCurrent(); suggestions.put(stem, s); } } /** * @param suggestions the suggestions * @param stemmer the stemmer * @param naveTerms the nave terms * @param searchType the search type * @return the actual number that was added, rather than marked as also available in a different search */ private int addSubjectTerms(final Map<String, SubjectSuggestion> suggestions, final PorterStemmer stemmer, final Collection<String> naveTerms, final SearchType searchType) { int added = 0; for (String s : naveTerms) { stemmer.setCurrent(s); stemmer.stem(); String stem = stemmer.getCurrent(); SubjectSuggestion suggestion = suggestions.get(stem); if (suggestion == null) { added++; suggestion = new SubjectSuggestion(); suggestion.setValue(s); suggestion.addSearchType(searchType); suggestions.put(stem, suggestion); } else if (!suggestion.getSearchTypes().contains(searchType)) { suggestion.addSearchType(searchType); //if the suggestion's value is longer, then replace with the short value if(suggestion.getValue().length() > s.length()) { suggestion.setValue(s); } } } return added; } }