/** * Copyright (c) 2000-present Liferay, Inc. All rights reserved. * * 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. */ package com.liferay.portal.search.elasticsearch.internal; import com.liferay.portal.kernel.log.Log; import com.liferay.portal.kernel.log.LogFactoryUtil; import com.liferay.portal.kernel.search.DocumentImpl; import com.liferay.portal.kernel.search.Field; import com.liferay.portal.kernel.search.SearchContext; import com.liferay.portal.kernel.search.suggest.AggregateSuggester; import com.liferay.portal.kernel.search.suggest.BaseQuerySuggester; import com.liferay.portal.kernel.search.suggest.PhraseSuggester; import com.liferay.portal.kernel.search.suggest.QuerySuggester; import com.liferay.portal.kernel.search.suggest.Suggester; import com.liferay.portal.kernel.search.suggest.SuggesterResult; import com.liferay.portal.kernel.search.suggest.SuggesterResults; import com.liferay.portal.kernel.search.suggest.SuggesterTranslator; import com.liferay.portal.kernel.search.suggest.TermSuggester; import com.liferay.portal.search.elasticsearch.connection.ElasticsearchConnectionManager; import com.liferay.portal.search.elasticsearch.index.IndexNameBuilder; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import org.apache.commons.lang.time.StopWatch; import org.elasticsearch.action.suggest.SuggestRequestBuilder; import org.elasticsearch.action.suggest.SuggestResponse; import org.elasticsearch.client.Client; import org.elasticsearch.common.text.Text; import org.elasticsearch.search.suggest.Suggest; import org.elasticsearch.search.suggest.SuggestBuilder; import org.elasticsearch.search.suggest.term.TermSuggestion; import org.osgi.service.component.annotations.Component; import org.osgi.service.component.annotations.Reference; /** * @author Michael C. Han */ @Component( immediate = true, property = {"search.engine.impl=Elasticsearch"}, service = QuerySuggester.class ) public class ElasticsearchQuerySuggester extends BaseQuerySuggester { @Override public Map<String, List<String>> spellCheckKeywords( SearchContext searchContext, int max) { String field = DocumentImpl.getLocalizedName( searchContext.getLocale(), Field.SPELL_CHECK_WORD); TermSuggester termSuggester = new TermSuggester( "spellCheckRequest", field, searchContext.getKeywords()); termSuggester.setSize(max); SuggesterResults suggesterResults = suggest( searchContext, termSuggester); Map<String, List<String>> suggestionsMap = new HashMap<>(); for (SuggesterResult suggesterResult : suggesterResults.getSuggesterResults()) { for (SuggesterResult.Entry suggesterResultEntry : suggesterResult.getEntries()) { String text = suggesterResultEntry.getText(); List<String> suggestionsList = suggestionsMap.get(text); if (suggestionsList == null) { suggestionsList = new ArrayList<>(); suggestionsMap.put(text, suggestionsList); } for (SuggesterResult.Entry.Option suggesterResultEntryOption : suggesterResultEntry.getOptions()) { suggestionsList.add(suggesterResultEntryOption.getText()); } } } return suggestionsMap; } @Override public SuggesterResults suggest( SearchContext searchContext, Suggester suggester) { StopWatch stopWatch = new StopWatch(); stopWatch.start(); Client client = elasticsearchConnectionManager.getClient(); SuggestBuilder suggestBuilder = suggesterTranslator.translate( suggester, searchContext); SuggestRequestBuilder suggestRequestBuilder = client.prepareSuggest( indexNameBuilder.getIndexName(searchContext.getCompanyId())); for (SuggestBuilder.SuggestionBuilder<?> suggestionBuilder : suggestBuilder.getSuggestion()) { suggestRequestBuilder.addSuggestion(suggestionBuilder); } if (suggester instanceof AggregateSuggester) { AggregateSuggester aggregateSuggester = (AggregateSuggester)suggester; suggestRequestBuilder.setSuggestText(aggregateSuggester.getValue()); } SuggestResponse suggestResponse = suggestRequestBuilder.get(); Suggest suggest = suggestResponse.getSuggest(); SuggesterResults suggesterResults = new SuggesterResults(); for (Suggest.Suggestion <? extends Suggest.Suggestion.Entry <? extends Suggest.Suggestion.Entry.Option>> suggestion : suggest) { SuggesterResult suggesterResult = translate(suggestion); suggesterResults.addSuggesterResult(suggesterResult); } if (_log.isInfoEnabled()) { stopWatch.stop(); _log.info( "Spell checked keywords in " + stopWatch.getTime() + "ms"); } return suggesterResults; } @Override public String[] suggestKeywordQueries( SearchContext searchContext, int max) { List<String> keywordQueries = new ArrayList<>(); String field = DocumentImpl.getLocalizedName( searchContext.getLocale(), Field.KEYWORD_SEARCH); PhraseSuggester phraseSuggester = new PhraseSuggester( "keywordQueryRequest", field, searchContext.getKeywords()); phraseSuggester.setSize(max); SuggesterResults suggesterResults = suggest( searchContext, phraseSuggester); SuggesterResult suggesterResult = suggesterResults.getSuggesterResult( phraseSuggester.getName()); List<SuggesterResult.Entry> suggesterResultEntries = suggesterResult.getEntries(); for (SuggesterResult.Entry suggesterResultEntry : suggesterResultEntries) { for (SuggesterResult.Entry.Option suggesterResultEntryOption : suggesterResultEntry.getOptions()) { String optionText = String.valueOf( suggesterResultEntryOption.getText()); keywordQueries.add(optionText); } } return keywordQueries.toArray(new String[keywordQueries.size()]); } protected SuggesterResult translate( Suggest.Suggestion <? extends Suggest.Suggestion.Entry <? extends Suggest.Suggestion.Entry.Option>> suggestion) { SuggesterResult suggesterResult = new SuggesterResult( suggestion.getName()); for (Suggest.Suggestion.Entry<? extends Suggest.Suggestion.Entry.Option> suggestionEntry : suggestion) { SuggesterResult.Entry suggesterResultEntry = translate( suggestionEntry); suggesterResult.addEntry(suggesterResultEntry); } return suggesterResult; } protected SuggesterResult.Entry.Option translate( Suggest.Suggestion.Entry.Option suggestionEntryOption) { SuggesterResult.Entry.Option suggesterResultEntryOption = new SuggesterResult.Entry.Option( suggestionEntryOption.getText().string(), suggestionEntryOption.getScore()); if (suggestionEntryOption.getHighlighted() != null) { suggesterResultEntryOption.setHighlightedText( suggestionEntryOption.getHighlighted().string()); } if (suggestionEntryOption instanceof TermSuggestion.Entry.Option) { TermSuggestion.Entry.Option termSuggestionEntryOption = (TermSuggestion.Entry.Option)suggestionEntryOption; suggesterResultEntryOption.setFrequency( termSuggestionEntryOption.getFreq()); } return suggesterResultEntryOption; } protected SuggesterResult.Entry translate( Suggest.Suggestion.Entry<? extends Suggest.Suggestion.Entry.Option> suggestionEntry) { Text text = suggestionEntry.getText(); SuggesterResult.Entry suggesterResultEntry = new SuggesterResult.Entry( text.string()); List<? extends Suggest.Suggestion.Entry.Option> suggestionEntryOptions = suggestionEntry.getOptions(); for (Suggest.Suggestion.Entry.Option suggestionEntryOption : suggestionEntryOptions) { SuggesterResult.Entry.Option suggesterResultEntryOption = translate( suggestionEntryOption); suggesterResultEntry.addOption(suggesterResultEntryOption); } return suggesterResultEntry; } @Reference(unbind = "-") protected ElasticsearchConnectionManager elasticsearchConnectionManager; @Reference(unbind = "-") protected IndexNameBuilder indexNameBuilder; @Reference(target = "(search.engine.impl=Elasticsearch)") protected SuggesterTranslator<SuggestBuilder> suggesterTranslator; private static final Log _log = LogFactoryUtil.getLog( ElasticsearchQuerySuggester.class); }