SolrSearchEngine.java
/*
* Copyright (c) 2002-2021, City of Paris
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright notice
* and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice
* and the following disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* 3. Neither the name of 'Mairie de Paris' nor 'Lutece' nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
* License 1.0
*/
package fr.paris.lutece.plugins.search.solr.business;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.regex.Matcher;
import java.util.stream.Collectors;
import javax.servlet.http.HttpServletRequest;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.logging.log4j.util.Strings;
import org.apache.solr.client.solrj.SolrClient;
import org.apache.solr.client.solrj.SolrQuery;
import org.apache.solr.client.solrj.SolrQuery.ORDER;
import org.apache.solr.client.solrj.SolrServerException;
import org.apache.solr.client.solrj.impl.NoOpResponseParser;
import org.apache.solr.client.solrj.request.QueryRequest;
import org.apache.solr.client.solrj.response.FacetField;
import org.apache.solr.client.solrj.response.QueryResponse;
import org.apache.solr.client.solrj.response.SpellCheckResponse;
import org.apache.solr.common.SolrDocumentList;
import org.apache.solr.common.util.NamedList;
import fr.paris.lutece.plugins.search.solr.business.facetintersection.FacetIntersection;
import fr.paris.lutece.plugins.search.solr.business.field.Field;
import fr.paris.lutece.plugins.search.solr.business.field.SolrFieldManager;
import fr.paris.lutece.plugins.search.solr.indexer.SolrIndexerService;
import fr.paris.lutece.plugins.search.solr.indexer.SolrItem;
import fr.paris.lutece.plugins.search.solr.util.SolrConstants;
import fr.paris.lutece.plugins.search.solr.util.SolrUtil;
import fr.paris.lutece.portal.business.page.Page;
import fr.paris.lutece.portal.service.search.SearchEngine;
import fr.paris.lutece.portal.service.search.SearchItem;
import fr.paris.lutece.portal.service.search.SearchResult;
import fr.paris.lutece.portal.service.security.LuteceUser;
import fr.paris.lutece.portal.service.security.SecurityService;
import fr.paris.lutece.portal.service.util.AppLogService;
import fr.paris.lutece.portal.service.util.AppPropertiesService;
/**
*
* SolrSearchEngine
*
*/
public class SolrSearchEngine implements SearchEngine
{
private static final String PROPERTY_SOLR_AUTOCOMPLETE_HANDLER = "solr.autocomplete.handler";
private static final String PROPERTY_SOLR_SPELLCHECK_HANDLER = "solr.spellcheck.handler";
private static final String PROPERTY_SOLR_HIGHLIGHT_PRE = "solr.highlight.pre";
private static final String PROPERTY_SOLR_HIGHLIGHT_POST = "solr.highlight.post";
private static final String PROPERTY_SOLR_HIGHLIGHT_SNIPPETS = "solr.highlight.snippets";
private static final String PROPERTY_SOLR_HIGHLIGHT_FRAGSIZE = "solr.highlight.fragsize";
private static final String PROPERTY_SOLR_HIGHLIGHT_SUMMARY_FRAGSIZE = "solr.highlight.summary.fragsize";
private static final String PROPERTY_SOLR_FACET_DATE_START = "solr.facet.date.start";
private static final String PROPERTY_FIELD_OR = "solr.field.or";
private static final String PROPERTY_FIELD_SWITCH = "solr.field.switch";
private static final String PROPERTY_FIELD_AND = "solr.field.and";
private static final boolean SOLR_FACET_COUNT_EXCLUDE = AppPropertiesService.getPropertyBoolean( "solr.facet.count.exclude", false );
// To allow the phrase to boost even with 2 words in between
// For example "Lutece is a Framework" would match for the search "Lutece Framework"
private static final String PROPERTY_SOLR_SEARCH_PS = "solr.search.ps";
private static final String DEFAULT_SOLR_SEARCH_PS = "3";
// According to Apache Solr Enterprise Search Server - Third Edition
// This should be a good default value
private static final String PROPERTY_SOLR_SEARCH_TIE = "solr.search.tie";
private static final String DEFAULT_SOLR_SEARCH_TIE = "0.1";
// see https://issues.apache.org/jira/browse/SOLR-3085
// Like one of the usecases described in the bug report,
// a stopword is very unlikely to match a tag (there is no tag named "le" or "la"). So it makes sense
// to autorelax mm by default.
private static final String PROPERTY_SOLR_SEARCH_AUTORELAXMM = "solr.search.autorelaxmm";
private static final String DEFAULT_SOLR_SEARCH_AUTORELAXMM = "true";
private static final String PROPERTY_SOLR_SEARCH_BOOSTRECENT = "solr.search.boostrecent";
private static final String DEFAULT_SOLR_SEARCH_BOOSTRECENT = "3.16e-12"; // Pretty soft: After 10 years, the score is divided by 2.
private static final String SOLR_AUTOCOMPLETE_HANDLER = AppPropertiesService.getProperty( PROPERTY_SOLR_AUTOCOMPLETE_HANDLER );
private static final String SOLR_SPELLCHECK_HANDLER = AppPropertiesService.getProperty( PROPERTY_SOLR_SPELLCHECK_HANDLER );
private static final String SOLR_HIGHLIGHT_PRE = AppPropertiesService.getProperty( PROPERTY_SOLR_HIGHLIGHT_PRE );
private static final String SOLR_HIGHLIGHT_POST = AppPropertiesService.getProperty( PROPERTY_SOLR_HIGHLIGHT_POST );
private static final int SOLR_HIGHLIGHT_SNIPPETS = AppPropertiesService.getPropertyInt( PROPERTY_SOLR_HIGHLIGHT_SNIPPETS, 5 );
private static final int SOLR_HIGHLIGHT_FRAGSIZE = AppPropertiesService.getPropertyInt( PROPERTY_SOLR_HIGHLIGHT_FRAGSIZE, 100 );
private static final int SOLR_HIGHLIGHT_SUMMARY_FRAGSIZE = AppPropertiesService.getPropertyInt( PROPERTY_SOLR_HIGHLIGHT_SUMMARY_FRAGSIZE, 0 );
private static final String SOLR_FACET_DATE_START = AppPropertiesService.getProperty( PROPERTY_SOLR_FACET_DATE_START );
private static final String SOLR_SPELLFIELD_OR = AppPropertiesService.getProperty( PROPERTY_FIELD_OR );
private static final String SOLR_SPELLFIELD_SWITCH = AppPropertiesService.getProperty( PROPERTY_FIELD_SWITCH );
private static final String SOLR_SPELLFIELD_AND = AppPropertiesService.getProperty( PROPERTY_FIELD_AND );
public static final String SOLR_FACET_DATE_GAP = AppPropertiesService.getProperty( "solr.facet.date.gap", "+1YEAR" );
public static final String SOLR_FACET_DATE_END = AppPropertiesService.getProperty( "solr.facet.date.end", "NOW" );
public static final int SOLR_FACET_LIMIT = AppPropertiesService.getPropertyInt( "solr.facet.limit", 100 );
private static SolrSearchEngine _instance;
private static final String DEF_TYPE = "edismax";
private static final String[] TAB_COMPLEX_QUERY_CHAR = {"(",")","[","]", " OR ", " AND ", "-", "+", "=","^"};
/**
* Return search results
*
* @param strQuery
* The search query
* @param request
* The HTTP request
* @return Results as a collection of SearchResult
*/
public List<SearchResult> getSearchResults( String strQuery, HttpServletRequest request )
{
List<SearchResult> results = new ArrayList<>( );
SolrClient solrServer = SolrServerService.getInstance( ).getSolrServer( );
if ( ( solrServer != null ) && StringUtils.isNotEmpty( strQuery ) )
{
SolrQuery query = new SolrQuery( );
query.setQuery( strQuery );
String [ ] userRoles;
String [ ] roles;
if ( SecurityService.isAuthenticationEnable( ) )
{
// authentification on, check roles
LuteceUser user = SecurityService.getInstance( ).getRegisteredUser( request );
if ( user != null )
{
userRoles = SecurityService.getInstance( ).getRolesByUser( user );
}
else
{
userRoles = new String [ 0];
}
roles = new String [ userRoles.length + 1];
System.arraycopy( userRoles, 0, roles, 0, userRoles.length );
roles [roles.length - 1] = Page.ROLE_NONE;
String filterRole = buildFilter( SearchItem.FIELD_ROLE, roles );
// portlets roles
query = query.addFilterQuery( filterRole );
}
try
{
QueryResponse response = solrServer.query( query );
SolrDocumentList documentList = response.getResults( );
results = SolrUtil.transformSolrDocumentList( documentList );
}
catch( SolrServerException | IOException e )
{
AppLogService.error( e.getMessage( ), e );
}
}
return results;
}
/**
* Return search results as json
*
* @param solrParam
* The solrParam
* @return Results as json
*/
public String getJsonSearchResults( Map<String, String[] > solrParam )
{
SolrClient solrServer = SolrServerService.getInstance( ).getSolrServer( );
if ( ( solrServer != null ) )
{
SolrQuery query = new SolrQuery();
solrParam.forEach( (key, value) -> query.set(key, value) );
QueryRequest req = new QueryRequest( query );
NoOpResponseParser rawJsonResponseParser = new NoOpResponseParser();
rawJsonResponseParser.setWriterType("json");
req.setResponseParser(rawJsonResponseParser);
try
{
NamedList<Object> resp = solrServer.request(req);
return (String) resp.get("response");
}
catch( SolrServerException | IOException e )
{
AppLogService.error( e.getMessage( ), e );
}
}
return Strings.EMPTY;
}
/**
* Return the result with facets. Does NOT support authentification yet.
*
* @param strQuery
* the query
* @param facetQueries
* The selected facets
* @param sortName
* The facet name to sort by
* @param sortOrder
* "asc" or "desc"
* @param nLimit
* Maximal number of results.
* @return the result with facets
*/
public SolrFacetedResult getFacetedSearchResults( String strQuery, String [ ] facetQueries, String sortName, String sortOrder, int nLimit,
int nCurrentPageIndex, int nItemsPerPage, Boolean bSpellCheck )
{
return getFacetedSearchResults( strQuery, null, facetQueries, sortName, sortOrder, nLimit, nCurrentPageIndex, nItemsPerPage, bSpellCheck );
}
/**
* Return the result with facets. Does NOT support authentification yet.
*
* @param strQuery
* the query
* @param facetQueries
* The selected facets
* @param fl
* the list of fields
* @param sortName
* The facet name to sort by
* @param sortOrder
* "asc" or "desc"
* @param nLimit
* Maximal number of results.
* @return the result with facets
*/
public SolrFacetedResult getFacetedSearchResults( String strQuery, String[] fl, String [ ] facetQueries, String sortName, String sortOrder, int nLimit,
int nCurrentPageIndex, int nItemsPerPage, Boolean bSpellCheck )
{
SolrFacetedResult facetedResult = new SolrFacetedResult( );
SolrClient solrServer = SolrServerService.getInstance( ).getSolrServer( );
List<SolrSearchResult> results = new ArrayList<>( );
Map<Field, List<String>> myValuesList = new HashMap<>( );
if ( solrServer == null )
{
facetedResult.setFacetFields( new ArrayList<>( ) );
facetedResult.setSolrSearchResults( results );
return facetedResult;
}
SolrQuery query = new SolrQuery( strQuery );
query.setHighlight( true );
query.setHighlightSimplePre( SOLR_HIGHLIGHT_PRE );
query.setHighlightSimplePost( SOLR_HIGHLIGHT_POST );
query.setHighlightSnippets( SOLR_HIGHLIGHT_SNIPPETS );
query.setHighlightFragsize( SOLR_HIGHLIGHT_FRAGSIZE );
query.setParam( "f.summary.hl.fragsize", Integer.toString( SOLR_HIGHLIGHT_SUMMARY_FRAGSIZE ) );
query.setFacet( true );
query.setFacetLimit( SOLR_FACET_LIMIT );
query.setFields(fl != null ? fl: new String [] { SolrConstants.CONSTANT_WILDCARD });
for ( Field field : SolrFieldManager.getFacetList( ).values( ) )
{
// Add facet Field
if ( field.getEnableFacet( ) )
{
StringBuilder exclusionTag = new StringBuilder( );
// If exclusion enabled, ignore the fq for the current facet count results.
if ( SOLR_FACET_COUNT_EXCLUDE )
{
exclusionTag.append( "{!ex=t" ).append( field.getName( ) ).append( "}" );
}
if ( isFieldDate( field.getName( ) ) )
{
query.setParam( "facet.range", exclusionTag + field.getName( ) );
query.setParam( "facet.range.start", SOLR_FACET_DATE_START );
query.setParam( "facet.range.gap", SOLR_FACET_DATE_GAP );
query.setParam( "facet.range.end", SOLR_FACET_DATE_END );
query.setParam( "facet.range.mincount", "0" );
}
else
{
query.addFacetField( exclusionTag + field.getSolrName( ) );
query.setParam( "f." + field.getSolrName( ) + ".facet.mincount", String.valueOf( field.getFacetMincount( ) ) );
}
myValuesList.put( field, new ArrayList<String>( ) );
}
}
// Facet intersection
List<String> treeParam = new ArrayList<>( );
for ( FacetIntersection intersect : SolrFieldManager.getIntersectionlist( ) )
{
treeParam.add( intersect.getField1( ).getSolrName( ) + "," + intersect.getField2( ).getSolrName( ) );
}
query.setParam( "facet.tree", (String [ ]) treeParam.toArray( new String [ 0] ) );
query.setParam( "spellcheck", bSpellCheck );
// sort order
if ( StringUtils.isNotEmpty( sortName ) )
{
if ( sortOrder.equals( "asc" ) )
{
query.setSort( sortName, ORDER.asc );
}
else
{
query.setSort( sortName, ORDER.desc );
}
}
else
{
for ( Field field : SolrFieldManager.getSortList( ) )
{
if ( field.getDefaultSort( ) )
{
query.setSort( field.getName( ), ORDER.desc );
}
}
}
// Treat HttpRequest
// FacetQuery
if ( facetQueries != null )
{
for ( String strFacetQuery : facetQueries )
{
if ( isComplexFacetQuery(strFacetQuery) )
{
query.addFilterQuery( strFacetQuery );
}
else
{
String [ ] myValues = strFacetQuery.split( ":", 2 );
if ( myValues != null && myValues.length == 2 )
{
myValuesList = getFieldArrange( myValues, myValuesList );
}
}
}
for ( Entry<Field, List<String>> entry : myValuesList.entrySet( ) )
{
Field tmpFieldValue = entry.getKey( );
List<String> strValues = entry.getValue( );
String strFacetString = "";
if ( CollectionUtils.isNotEmpty( strValues ) )
{
strFacetString = extractQuery( strValues, tmpFieldValue.getOperator( ) );
if ( isFieldDate( tmpFieldValue.getName( ) ) )
{
strFacetString = strFacetString.replaceAll( "\"", "" );
}
StringBuilder facetFilter = new StringBuilder( tmpFieldValue.getName( ) );
// If exclusion enabled, add the tag so that it can be excluded.
if ( SOLR_FACET_COUNT_EXCLUDE )
{
StringBuilder tag = new StringBuilder( "{!tag=t" ).append( tmpFieldValue.getName( ) ).append( "}" );
facetFilter.insert( 0, tag );
}
facetFilter.append( ":" );
facetFilter.append( strFacetString );
query.addFilterQuery( facetFilter.toString( ) );
}
}
}
try
{
// count query
query.setRows( 0 );
if ( !strQuery.equals( "*:*" ) )
{
query.setParam( "defType", DEF_TYPE );
String strWeightValue = generateQueryWeightValue( );
query.setParam( "qf", strWeightValue );
String strPhraseSlop = AppPropertiesService.getProperty( PROPERTY_SOLR_SEARCH_PS, DEFAULT_SOLR_SEARCH_PS );
if ( StringUtils.isNotBlank( strPhraseSlop ) )
{
query.setParam( "pf", strWeightValue );
query.setParam( "pf2", strWeightValue );
query.setParam( "pf3", strWeightValue );
query.setParam( "ps", strPhraseSlop );
query.setParam( "qs", strPhraseSlop ); // Slop when the user explicitly uses quotes
}
String strTie = AppPropertiesService.getProperty( PROPERTY_SOLR_SEARCH_TIE, DEFAULT_SOLR_SEARCH_TIE );
if ( StringUtils.isNotBlank( strTie ) )
{
query.setParam( "tie", strTie );
}
String strAutoRelaxmm = AppPropertiesService.getProperty( PROPERTY_SOLR_SEARCH_AUTORELAXMM, DEFAULT_SOLR_SEARCH_AUTORELAXMM );
if ( StringUtils.isNotBlank( strAutoRelaxmm ) )
{
query.setParam( "mm.autoRelax", strAutoRelaxmm );
}
String strBoostRecent = AppPropertiesService.getProperty( PROPERTY_SOLR_SEARCH_BOOSTRECENT, DEFAULT_SOLR_SEARCH_BOOSTRECENT );
if ( StringUtils.isNotBlank( strBoostRecent ) )
{
query.setParam( "boost", "recip(ms(NOW/HOUR,date)," + strBoostRecent + ",1,1)" );
}
}
QueryResponse response = solrServer.query( query );
int nResults = (int) response.getResults( ).getNumFound( );
facetedResult.setCount( nResults > nLimit ? nLimit : nResults );
query.setStart( ( nCurrentPageIndex - 1 ) * nItemsPerPage );
query.setRows( nItemsPerPage > nLimit ? nLimit : nItemsPerPage );
response = solrServer.query( query );
// HighLight
Map<String, Map<String, List<String>>> highlightsMap = response.getHighlighting( );
SolrHighlights highlights = null;
if ( highlightsMap != null )
{
highlights = new SolrHighlights( highlightsMap );
}
// resultList
List<SolrItem> itemList = response.getBeans( SolrItem.class );
results = SolrUtil.transformSolrItemsToSolrSearchResults( itemList, highlights );
// set the spellcheckresult
facetedResult.setSolrSpellCheckResponse( response.getSpellCheckResponse( ) );
// Range facet (dates)
if ( ( response.getFacetRanges( ) != null ) && !response.getFacetRanges( ).isEmpty( ) )
{
facetedResult.setFacetDateList( response.getFacetRanges( ) );
}
// FacetField
facetedResult.setFacetFields( response.getFacetFields( ) );
// Facet intersection (facet tree)
NamedList<Object> resp = (NamedList<Object>) response.getResponse( ).get( "facet_counts" );
if ( resp != null )
{
NamedList<NamedList<NamedList<Integer>>> trees = (NamedList<NamedList<NamedList<Integer>>>) resp.get( "trees" );
Map<String, List<FacetField>> treesResult = new HashMap<>( );
if ( trees != null )
{
for ( Entry<String, NamedList<NamedList<Integer>>> selectedFacet : trees )
{
// Selected Facet (ex : type,categorie )
List<FacetField> facetFields = new ArrayList<>( selectedFacet.getValue( ).size( ) );
for ( Entry<String, NamedList<Integer>> facetField : selectedFacet.getValue( ) )
{
FacetField ff = new FacetField( facetField.getKey( ) );
for ( Entry<String, Integer> value : facetField.getValue( ) )
{
// Second Level
ff.add( value.getKey( ), value.getValue( ) );
}
facetFields.add( ff );
}
treesResult.put( selectedFacet.getKey( ), facetFields );
}
}
facetedResult.setFacetIntersection( treesResult );
}
}
catch( SolrServerException | IOException e )
{
AppLogService.error( e.getMessage( ), e );
}
facetedResult.setSolrSearchResults( results );
return facetedResult;
}
/**
* @param strValues
* @param strStart
* @param strEnd
* @return
*/
private String extractQuery( List<String> strValues, String strOperator )
{
StringBuilder sbFacetString = new StringBuilder( );
String strStart = strValues.size( ) > 1 ? "(" : "";
String strEnd = strValues.size( ) > 1 ? ")" : "";
List<String> notBlankValues = strValues.stream( ).filter( StringUtils::isNotBlank ).collect( Collectors.toList( ) );
for ( String strTmpSearch : notBlankValues )
{
strTmpSearch = "\"" + strTmpSearch.replaceAll( "\"", Matcher.quoteReplacement( "\\\"" ) ) + "\"";
if ( SOLR_SPELLFIELD_OR.equalsIgnoreCase( strOperator ) || SOLR_SPELLFIELD_AND.equalsIgnoreCase( strOperator ) )
{
if ( StringUtils.isBlank( sbFacetString.toString( ) ) )
{
sbFacetString.append( strTmpSearch.trim( ) );
}
else
{
sbFacetString.append( " " ).append( strOperator ).append( " " ).append( strTmpSearch.trim( ) );
}
}
else
if ( SOLR_SPELLFIELD_SWITCH.equalsIgnoreCase( strOperator ) )
{
sbFacetString = new StringBuilder( strTmpSearch.trim( ) );
}
}
return strStart.concat( sbFacetString.toString( ) ).concat( strEnd );
}
/**
* Get a matrice from all the fields with their values
*
* @param myValues
* @return
*/
private Map<Field, List<String>> getFieldArrange( String [ ] myValues, Map<Field, List<String>> myTab )
{
Map<Field, List<String>> lstReturn = myTab;
Field tmpField = getField( lstReturn, myValues [0] );
if ( tmpField != null )
{
boolean bAddField = true;
List<String> getValuesFromFrield = lstReturn.get( tmpField );
for ( String strField : getValuesFromFrield )
{
if ( strField.equalsIgnoreCase( myValues [1] ) )
{
bAddField = false;
}
}
if ( bAddField )
{
getValuesFromFrield.add( myValues [1] );
lstReturn.put( tmpField, getValuesFromFrield );
}
}
return lstReturn;
}
/**
* @return
*/
private String generateQueryWeightValue( )
{
List<Field> fieldList = SolrFieldManager.getFieldList( );
StringBuilder strQueryWeight = new StringBuilder( );
for ( Field field : fieldList )
{
if ( field.getWeight( ) > 0 )
{
strQueryWeight.append( field.getSolrName( ) ).append( "^" ).append( field.getWeight( ) ).append( " " );
}
}
return strQueryWeight.toString( );
}
/**
* Get the field
*
* @param strName
*/
private Field getField( Map<Field, List<String>> values, String strName )
{
Field fieldReturn = null;
for ( Field tmp : values.keySet( ) )
{
if ( tmp.getName( ).equalsIgnoreCase( strName ) )
{
fieldReturn = tmp;
}
}
return fieldReturn;
}
/**
* Return the result geojseon and uid
*
* @param strQuery
* the query
* @param facetQueries
* The selected facets
* @param nLimit
* Maximal number of results.
* @return the results geojson and uid
*/
public List<SolrSearchResult> getGeolocSearchResults( String strQuery, String [ ] facetQueries, int nLimit )
{
SolrClient solrServer = SolrServerService.getInstance( ).getSolrServer( );
Map<Field, List<String>> myValuesList = new HashMap<>( );
if ( solrServer == null )
{
return new ArrayList<>( );
}
String strFields = "*" + SolrItem.DYNAMIC_GEOJSON_FIELD_SUFFIX + "," + SearchItem.FIELD_UID;
SolrQuery query = new SolrQuery( strQuery );
query.setParam( "fl", strFields );
for ( Field field : SolrFieldManager.getFacetList( ).values( ) )
{
// Add facet Field
if ( field.getEnableFacet( ) )
{
myValuesList.put( field, new ArrayList<String>( ) );
}
}
// Treat HttpRequest
// FacetQuery
if ( facetQueries != null )
{
for ( String strFacetQuery : facetQueries )
{
if ( isComplexFacetQuery(strFacetQuery) )
{
query.addFilterQuery( strFacetQuery );
}
else
{
String [ ] myValues = strFacetQuery.split( ":", 2 );
if ( myValues != null && myValues.length == 2 )
{
myValuesList = getFieldArrange( myValues, myValuesList );
}
}
}
for ( Entry<Field, List<String>> entry : myValuesList.entrySet( ) )
{
Field tmpFieldValue = entry.getKey( );
List<String> strValues = entry.getValue( );
String strFacetString = "";
if ( CollectionUtils.isNotEmpty( strValues ) )
{
strFacetString = extractQuery( strValues, tmpFieldValue.getOperator( ) );
if ( isFieldDate( tmpFieldValue.getName( ) ) )
{
strFacetString = strFacetString.replaceAll( "\"", "" );
}
query.addFilterQuery( tmpFieldValue.getName( ) + ":" + strFacetString );
}
}
}
if ( !strQuery.equals( "*:*" ) )
{
query.setParam( "defType", DEF_TYPE );
query.setParam( "mm.autoRelax", true );
}
query.setStart( 0 );
query.setRows( nLimit );
QueryResponse response;
try
{
response = solrServer.query( query );
}
catch( SolrServerException | IOException e )
{
AppLogService.error( "Solr getGeolocSearchResults error: " + e.getMessage( ), e );
return new ArrayList<>( );
}
// resultList
List<SolrItem> itemList = response.getBeans( SolrItem.class );
return SolrUtil.transformSolrItemsToSolrSearchResults( itemList, null );
}
/**
* Return the suggestion terms
*
* @param term
* the terms of search
* @return The spell checker response
*/
public SpellCheckResponse getSpellChecker( String term )
{
SpellCheckResponse spellCheck = null;
SolrClient solrServer = SolrServerService.getInstance( ).getSolrServer( );
SolrQuery query = new SolrQuery( term );
// Do not return results (optimization)
query.setRows( 0 );
// Activate spellChecker
query.setParam( "spellcheck", "true" );
// The request handler used
query.setRequestHandler( "/" + SOLR_SPELLCHECK_HANDLER );
// The number of suggest returned
query.setParam( "spellcheck.count", "1" ); // TODO
// Returns the frequency of the terms
query.setParam( "spellcheck.extendedResults", "true" ); // TODO
// Return the best suggestion combinaison with many words
query.setParam( "spellcheck.collate", "true" ); // TODO
try
{
QueryResponse response = solrServer.query( query );
spellCheck = response.getSpellCheckResponse( );
}
catch( SolrServerException | IOException e )
{
AppLogService.error( e.getMessage( ), e );
}
return spellCheck;
}
public QueryResponse getJsonpSuggest( String terms, String callback )
{
QueryResponse response = null;
SolrClient solrServer = SolrServerService.getInstance( ).getSolrServer( );
SolrQuery query = new SolrQuery( terms );
query.setParam( "wt", "json" );
query.setParam( "json.wrf", callback );
query.setRows( 10 );
query.setRequestHandler( "/" + SOLR_AUTOCOMPLETE_HANDLER );
try
{
response = solrServer.query( query );
}
catch( SolrServerException | IOException e )
{
AppLogService.error( e.getMessage( ), e );
}
return response;
}
public String getDocumentHighLighting( String strDocumentId, String terms )
{
String strDocumentIdPrefixed = SolrIndexerService.getWebAppName( ) + SolrConstants.CONSTANT_UNDERSCORE + strDocumentId;
String xmlContent = null;
SolrClient solrServer = SolrServerService.getInstance( ).getSolrServer( );
SolrQuery query = new SolrQuery( terms );
query.setHighlight( true );
query.setHighlightSimplePre( SOLR_HIGHLIGHT_PRE );
query.setHighlightSimplePost( SOLR_HIGHLIGHT_POST );
query.setHighlightFragsize( 0 ); // return all the content, not fragments
query.setParam( "hl.fl", SolrItem.FIELD_XML_CONTENT ); // return only the field xml_content HighLighting
query.setFields( SearchItem.FIELD_UID ); // return only the field uid
query.setRows( 1 );
query.addFilterQuery( SearchItem.FIELD_UID + ":" + strDocumentIdPrefixed );
try
{
QueryResponse response = solrServer.query( query );
if ( response.getResults( ).size( ) == 1 )
{
SolrHighlights highlights = new SolrHighlights( response.getHighlighting( ) );
if ( highlights.getHighlights( strDocumentIdPrefixed ).getMap( ).size( ) > 0 )
{
xmlContent = highlights.getHighlights( strDocumentIdPrefixed ).getMap( ).get( SolrItem.FIELD_XML_CONTENT ).get( 0 );
}
}
}
catch( SolrServerException | IOException e )
{
AppLogService.error( e.getMessage( ), e );
}
return xmlContent;
}
/**
* Builds a field filter string. field:(value0 value1 ...valueN)
*
* @param strField
* the field
* @param values
* the values
* @return the filter string
*/
private String buildFilter( String strField, String [ ] values )
{
StringBuilder sb = new StringBuilder( );
sb.append( strField );
sb.append( ":(" );
for ( String strValue : values )
{
sb.append( strValue );
sb.append( " " );
}
String filterString = sb.substring( 0, sb.length( ) - 1 );
filterString += ")";
return filterString;
}
/**
* Returns the instance
*
* @return the instance
*/
public static SolrSearchEngine getInstance( )
{
if ( _instance == null )
{
_instance = new SolrSearchEngine( );
}
return _instance;
}
private boolean isFieldDate( String fieldName )
{
return fieldName.equalsIgnoreCase( "date" ) || fieldName.toLowerCase( ).endsWith( "_date" );
}
/**
* Tests id the current query is complex or not
* @param strFacetQuery
* @return true if query is complex, false otherwise
*/
private boolean isComplexFacetQuery(String strFacetQuery)
{
if ( strFacetQuery != null )
{
for ( String strComplexChar : TAB_COMPLEX_QUERY_CHAR )
{
if ( StringUtils.contains(strFacetQuery, strComplexChar) )
{
return true;
}
}
}
return false;
}
}