org.broadleafcommerce.core.search.service.solr
Class SolrSearchServiceImpl

java.lang.Object
  extended by org.broadleafcommerce.core.search.service.solr.SolrSearchServiceImpl
All Implemented Interfaces:
SearchService, org.springframework.beans.factory.DisposableBean

public class SolrSearchServiceImpl
extends Object
implements SearchService, org.springframework.beans.factory.DisposableBean

An implementation of SearchService that uses Solr

Author:
Andre Azzolini (apazzolini)

Field Summary
protected static String DEFAULT_NAMESPACE
           
protected static Locale defaultLocale
           
protected  SolrSearchServiceExtensionListener extensionManager
           
protected  FieldDao fieldDao
           
protected static String GLOBAL_FACET_TAG_FIELD
           
protected  LocaleService localeService
           
protected  ProductDao productDao
           
protected  SearchFacetDao searchFacetDao
           
protected  org.apache.solr.client.solrj.SolrServer server
           
 
Constructor Summary
SolrSearchServiceImpl(org.apache.solr.client.solrj.SolrServer solrServer)
           
SolrSearchServiceImpl(String solrServer)
           
 
Method Summary
protected  void attachActiveFacetFilters(org.apache.solr.client.solrj.SolrQuery query, Map<String,SearchFacetDTO> namedFacetMap, ProductSearchCriteria searchCriteria)
          Restricts the query by adding active facet filters.
protected  void attachBasicDocumentFields(Product product, org.apache.solr.common.SolrInputDocument document)
          Adds the ID, category, and explicitCategory fields for the product to the document
protected  void attachFacets(org.apache.solr.client.solrj.SolrQuery query, Map<String,SearchFacetDTO> namedFacetMap)
          Notifies solr about which facets you want it to determine results and counts for
protected  void attachSortClause(org.apache.solr.client.solrj.SolrQuery query, ProductSearchCriteria searchCriteria, String defaultSort)
          Sets up the sorting criteria.
protected  org.apache.solr.common.SolrInputDocument buildDocument(Product product, List<Field> fields, List<Locale> locales)
          Given a product, fields that relate to that product, and a list of locales and pricelists, builds a SolrInputDocument to be added to the Solr index.
protected  List<SearchFacetDTO> buildSearchFacetDTOs(List<SearchFacet> searchFacets)
          Create the wrapper DTO around the SearchFacet
protected  String convertToMappedProperty(String propertyName, String listPropertyName, String mapPropertyName)
          Converts a propertyName to one that is able to reference inside a map.
 void destroy()
           
protected  boolean facetIsAvailable(SearchFacet facet, javax.servlet.http.HttpServletRequest request)
          Checks to see if the requiredFacets condition for a given facet is met.
 ProductSearchResult findExplicitProductsByCategory(Category category, ProductSearchCriteria searchCriteria)
          Performs a search for products in the given category, taking into consideration the ProductSearchCriteria This method will NOT return products that are in a sub-level of a given category.
protected  ProductSearchResult findProducts(String qualifiedSolrQuery, List<SearchFacetDTO> facets, ProductSearchCriteria searchCriteria, String defaultSort)
          Given a qualified solr query string (such as "category:2002"), actually performs a solr search.
 ProductSearchResult findProductsByCategory(Category category, ProductSearchCriteria searchCriteria)
          Performs a search for products in the given category, taking into consideration the ProductSearchCriteria This method will return products that are in any sub-level of a given category.
 ProductSearchResult findProductsByCategoryAndQuery(Category category, String query, ProductSearchCriteria searchCriteria)
          Performs a search for products in the given category for the given query, taking into consideration the ProductSearchCriteria
 ProductSearchResult findProductsByQuery(String query, ProductSearchCriteria searchCriteria)
          Performs a search for products across all categories for the given query, taking into consideration the ProductSearchCriteria
protected  List<Locale> getAllLocales()
           
 List<SearchFacetDTO> getCategoryFacets(Category category)
          Gets all available facets for a given category
protected  String getCategoryFieldName()
           
protected  String getCategorySortFieldName(Category category)
           
protected  String getCurrentNamespace()
          Determines the current namespace we are operating on.
protected  Locale getDefaultLocale()
          Returns the default locale.
protected  String getDefaultLocalePrefix()
           
protected  String getExplicitCategoryFieldName()
           
protected  String getIdFieldName()
           
protected  String getLocalePrefix()
          Determines if there is a locale prefix that needs to be applied to the given field for this particular request.
protected  Map<String,SearchFacetDTO> getNamedFacetMap(List<SearchFacetDTO> facets, ProductSearchCriteria searchCriteria)
           
protected  String getNamespaceFieldName()
           
protected  List<Product> getProducts(org.apache.solr.client.solrj.response.QueryResponse response)
          Given a list of product IDs from solr, this method will look up the IDs via the productDao and build out actual Product instances.
protected  String getPropertyNameForFieldFacet(Field field)
          Returns the property name for the given field and its configured facet field type.
protected  String getPropertyNameForFieldFacet(Field field, String prefix)
          Returns the property name for the given field, its configured facet field type, and the given prefix
protected  String getPropertyNameForFieldSearchable(Field field, FieldType searchableFieldType)
          Returns the property name for the given field and field type.
protected  String getPropertyNameForFieldSearchable(Field field, FieldType searchableFieldType, String prefix)
          Returns the property name for the given field, field type, and prefix
protected  Map<String,Object> getPropertyValues(Product product, Field field, FieldType fieldType, List<Locale> locales)
          Returns a map of prefix to value for the requested attributes.
protected  String getSearchableFieldName()
           
protected  String getSearchableFieldName(String prefix)
           
 List<SearchFacetDTO> getSearchFacets()
          Gets all available facets for search results page
protected  String getSolrFieldKey(Field field, ProductSearchCriteria searchCriteria)
          This method will be used to map a field abbreviation to the appropriate solr index field to use.
protected  Map<String,String> getSolrFieldKeyMap(ProductSearchCriteria searchCriteria)
           
protected  String getSolrFieldString(String indexField, SearchFacetRange range)
          Returns a field string.
protected  String getSolrFieldTag(String tagField, String tag)
          Returns a solr field tag.
protected  String getSolrTaggedFieldString(String indexField, String tagField, String tag, SearchFacetRange range)
          Returns a fully composed solr field string.
protected  List<Product> readAllActiveProducts()
          This method to read all active products will be slow if you have a large catalog.
 void rebuildIndex()
          Rebuilds the current index.
protected  String sanitizeQuery(String query)
          Perform any necessary query sanitation here.
protected  void setFacetResults(Map<String,SearchFacetDTO> namedFacetMap, org.apache.solr.client.solrj.response.QueryResponse response)
          Builds out the DTOs for facet results from the search.
 void setPagingAttributes(ProductSearchResult result, org.apache.solr.client.solrj.response.QueryResponse response, ProductSearchCriteria searchCriteria)
          Sets the total results, the current page, and the page size on the ProductSearchResult.
protected  void sortFacetResults(Map<String,SearchFacetDTO> namedFacetMap)
          Invoked to sort the facet results.
 
Methods inherited from class java.lang.Object
clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
 

Field Detail

GLOBAL_FACET_TAG_FIELD

protected static final String GLOBAL_FACET_TAG_FIELD
See Also:
Constant Field Values

DEFAULT_NAMESPACE

protected static final String DEFAULT_NAMESPACE
See Also:
Constant Field Values

defaultLocale

protected static Locale defaultLocale

productDao

protected ProductDao productDao

fieldDao

protected FieldDao fieldDao

searchFacetDao

protected SearchFacetDao searchFacetDao

localeService

protected LocaleService localeService

extensionManager

protected SolrSearchServiceExtensionListener extensionManager

server

protected org.apache.solr.client.solrj.SolrServer server
Constructor Detail

SolrSearchServiceImpl

public SolrSearchServiceImpl(String solrServer)
                      throws IOException,
                             ParserConfigurationException,
                             SAXException
Throws:
IOException
ParserConfigurationException
SAXException

SolrSearchServiceImpl

public SolrSearchServiceImpl(org.apache.solr.client.solrj.SolrServer solrServer)
Method Detail

destroy

public void destroy()
             throws Exception
Specified by:
destroy in interface org.springframework.beans.factory.DisposableBean
Throws:
Exception

rebuildIndex

@Transactional(value="blTransactionManager")
public void rebuildIndex()
                  throws ServiceException,
                         IOException
Description copied from interface: SearchService
Rebuilds the current index. Note that some search service implementations may not necessarily support rebuilding an index (such as the DatabaseProductSearchService)

Specified by:
rebuildIndex in interface SearchService
Throws:
ServiceException
IOException

readAllActiveProducts

protected List<Product> readAllActiveProducts()
This method to read all active products will be slow if you have a large catalog. In this case, you will want to read the products in a different manner. For example, if you know the fields that will be indexed, you can configure a DAO object to only load those fields. You could also use a JDBC based DAO for even faster access. This default implementation is only suitable for small catalogs.

Returns:
the list of all active products to be used by the index building task

getAllLocales

protected List<Locale> getAllLocales()
Returns:
a list of all possible locale prefixes to consider

buildDocument

protected org.apache.solr.common.SolrInputDocument buildDocument(Product product,
                                                                 List<Field> fields,
                                                                 List<Locale> locales)
Given a product, fields that relate to that product, and a list of locales and pricelists, builds a SolrInputDocument to be added to the Solr index.

Parameters:
product -
fields -
locales -
Returns:
the document

attachBasicDocumentFields

protected void attachBasicDocumentFields(Product product,
                                         org.apache.solr.common.SolrInputDocument document)
Adds the ID, category, and explicitCategory fields for the product to the document

Parameters:
product -
document -

getPropertyValues

protected Map<String,Object> getPropertyValues(Product product,
                                               Field field,
                                               FieldType fieldType,
                                               List<Locale> locales)
                                        throws IllegalAccessException,
                                               InvocationTargetException,
                                               NoSuchMethodException
Returns a map of prefix to value for the requested attributes. For example, if the requested field corresponds to a Sku's description and the locales list has the en_US locale and the es_ES locale, the resulting map could be { "en_US" : "A description", "es_ES" : "Una descripcion" }

Parameters:
product -
field -
isPriceField -
prefix -
Returns:
the value of the property
Throws:
IllegalAccessException
InvocationTargetException
NoSuchMethodException

findExplicitProductsByCategory

public ProductSearchResult findExplicitProductsByCategory(Category category,
                                                          ProductSearchCriteria searchCriteria)
                                                   throws ServiceException
Description copied from interface: SearchService
Performs a search for products in the given category, taking into consideration the ProductSearchCriteria This method will NOT return products that are in a sub-level of a given category. For example, if you had a "Routers" category and a "Enterprise Routers" sub-category, asking for products in "Routers", would NOT return products that are in the "Enterprise Routers" category.

Specified by:
findExplicitProductsByCategory in interface SearchService
Returns:
Throws:
ServiceException
See Also:
SearchService.findProductsByCategory(Category, ProductSearchCriteria)

findProductsByCategory

public ProductSearchResult findProductsByCategory(Category category,
                                                  ProductSearchCriteria searchCriteria)
                                           throws ServiceException
Description copied from interface: SearchService
Performs a search for products in the given category, taking into consideration the ProductSearchCriteria This method will return products that are in any sub-level of a given category. For example, if you had a "Routers" category and a "Enterprise Routers" sub-category, asking for products in "Routers", would return products that are in the "Enterprise Routers" category.

Specified by:
findProductsByCategory in interface SearchService
Returns:
the result of the search
Throws:
ServiceException
See Also:
SearchService.findExplicitProductsByCategory(Category, ProductSearchCriteria)

findProductsByQuery

public ProductSearchResult findProductsByQuery(String query,
                                               ProductSearchCriteria searchCriteria)
                                        throws ServiceException
Description copied from interface: SearchService
Performs a search for products across all categories for the given query, taking into consideration the ProductSearchCriteria

Specified by:
findProductsByQuery in interface SearchService
Returns:
the result of the search
Throws:
ServiceException

findProductsByCategoryAndQuery

public ProductSearchResult findProductsByCategoryAndQuery(Category category,
                                                          String query,
                                                          ProductSearchCriteria searchCriteria)
                                                   throws ServiceException
Description copied from interface: SearchService
Performs a search for products in the given category for the given query, taking into consideration the ProductSearchCriteria

Specified by:
findProductsByCategoryAndQuery in interface SearchService
Throws:
ServiceException

getSearchFacets

public List<SearchFacetDTO> getSearchFacets()
Description copied from interface: SearchService
Gets all available facets for search results page

Specified by:
getSearchFacets in interface SearchService
Returns:
the available facets

getCategoryFacets

public List<SearchFacetDTO> getCategoryFacets(Category category)
Description copied from interface: SearchService
Gets all available facets for a given category

Specified by:
getCategoryFacets in interface SearchService
Returns:
the available facets

findProducts

protected ProductSearchResult findProducts(String qualifiedSolrQuery,
                                           List<SearchFacetDTO> facets,
                                           ProductSearchCriteria searchCriteria,
                                           String defaultSort)
                                    throws ServiceException
Given a qualified solr query string (such as "category:2002"), actually performs a solr search. It will take into considering the search criteria to build out facets / pagination / sorting.

Parameters:
qualifiedSolrQuery -
facets -
searchCriteria -
Returns:
the ProductSearchResult of the search
Throws:
ServiceException

attachSortClause

protected void attachSortClause(org.apache.solr.client.solrj.SolrQuery query,
                                ProductSearchCriteria searchCriteria,
                                String defaultSort)
Sets up the sorting criteria. This will support sorting by multiple fields at a time

Parameters:
query -
searchCriteria -

attachActiveFacetFilters

protected void attachActiveFacetFilters(org.apache.solr.client.solrj.SolrQuery query,
                                        Map<String,SearchFacetDTO> namedFacetMap,
                                        ProductSearchCriteria searchCriteria)
Restricts the query by adding active facet filters.

Parameters:
query -
namedFacetMap -
searchCriteria -

attachFacets

protected void attachFacets(org.apache.solr.client.solrj.SolrQuery query,
                            Map<String,SearchFacetDTO> namedFacetMap)
Notifies solr about which facets you want it to determine results and counts for

Parameters:
query -
namedFacetMap -

setFacetResults

protected void setFacetResults(Map<String,SearchFacetDTO> namedFacetMap,
                               org.apache.solr.client.solrj.response.QueryResponse response)
Builds out the DTOs for facet results from the search. This will then be used by the view layer to display which values are avaialble given the current constraints as well as the count of the values.

Parameters:
namedFacetMap -
response -

sortFacetResults

protected void sortFacetResults(Map<String,SearchFacetDTO> namedFacetMap)
Invoked to sort the facet results. This method will use the natural sorting of the value attribute of the facet (or, if value is null, the minValue of the facet result). Override this method to customize facet sorting for your given needs.

Parameters:
namedFacetMap -

setPagingAttributes

public void setPagingAttributes(ProductSearchResult result,
                                org.apache.solr.client.solrj.response.QueryResponse response,
                                ProductSearchCriteria searchCriteria)
Sets the total results, the current page, and the page size on the ProductSearchResult. Total results comes from solr, while page and page size are duplicates of the searchCriteria conditions for ease of use.

Parameters:
result -
response -
searchCriteria -

getProducts

protected List<Product> getProducts(org.apache.solr.client.solrj.response.QueryResponse response)
Given a list of product IDs from solr, this method will look up the IDs via the productDao and build out actual Product instances. It will return a Products that is sorted by the order of the IDs in the passed in list.

Parameters:
response -
Returns:
the actual Product instances as a result of the search

getSolrTaggedFieldString

protected String getSolrTaggedFieldString(String indexField,
                                          String tagField,
                                          String tag,
                                          SearchFacetRange range)
Returns a fully composed solr field string. Given indexField = a, tag = ex, and a non-null range, would produce the following String: {!ex=a}a:[minVal TO maxVal]


getSolrFieldTag

protected String getSolrFieldTag(String tagField,
                                 String tag)
Returns a solr field tag. Given indexField = a, tag = ex, would produce the following String: {!ex=a}


getSolrFieldString

protected String getSolrFieldString(String indexField,
                                    SearchFacetRange range)
Returns a field string. Given indexField = a and a non-null range, would produce the following String: a:[minVal TO maxVal]


buildSearchFacetDTOs

protected List<SearchFacetDTO> buildSearchFacetDTOs(List<SearchFacet> searchFacets)
Create the wrapper DTO around the SearchFacet

Parameters:
searchFacets -
Returns:
the wrapper DTO

facetIsAvailable

protected boolean facetIsAvailable(SearchFacet facet,
                                   javax.servlet.http.HttpServletRequest request)
Checks to see if the requiredFacets condition for a given facet is met.

Parameters:
facet -
request -
Returns:
whether or not the facet parameter is available

convertToMappedProperty

protected String convertToMappedProperty(String propertyName,
                                         String listPropertyName,
                                         String mapPropertyName)
Converts a propertyName to one that is able to reference inside a map. For example, consider the property in Product that references a List, "productAttributes". Also consider the utility method in Product called "mappedProductAttributes", which returns a map of the ProductAttributes keyed by the name property in the ProductAttribute. Given the parameters "productAttributes.heatRange", "productAttributes", "mappedProductAttributes" (which would represent a property called "productAttributes.heatRange" that references a specific ProductAttribute inside of a product whose "name" property is equal to "heatRange", this method will convert this property to mappedProductAttributes(heatRange).value, which is then usable by the standard beanutils PropertyUtils class to get the value.

Parameters:
propertyName -
listPropertyName -
mapPropertyName -
Returns:
the converted property name

getSolrFieldKey

protected String getSolrFieldKey(Field field,
                                 ProductSearchCriteria searchCriteria)
This method will be used to map a field abbreviation to the appropriate solr index field to use. Typically, this default implementation that maps to the facet field type will be sufficient. However, there may be cases where you would want to use a different solr index depending on other currently active facets. In that case, you would associate that mapping here. For example, for the "price" abbreviation, we would generally want to use "defaultSku.retailPrice_td". However, if a secondary facet on item condition is selected (such as "refurbished", we may want to index "price" to "refurbishedSku.retailPrice_td". That mapping occurs here.

Parameters:
fields -
searchCriteria - the searchCriteria in case it is needed to determine the field key
Returns:
the solr field index key to use

getSolrFieldKeyMap

protected Map<String,String> getSolrFieldKeyMap(ProductSearchCriteria searchCriteria)
Parameters:
searchCriteria -
Returns:
a map of abbreviated key to fully qualified solr index field key for all product fields

getNamedFacetMap

protected Map<String,SearchFacetDTO> getNamedFacetMap(List<SearchFacetDTO> facets,
                                                      ProductSearchCriteria searchCriteria)
Parameters:
facets -
searchCriteria -
Returns:
a map of fully qualified solr index field key to the searchFacetDTO object

sanitizeQuery

protected String sanitizeQuery(String query)
Perform any necessary query sanitation here. For example, we disallow open and close parentheses, colons, and we also ensure that quotes are actual quotes (") and not the URL encoding (") so that Solr is able to properly handle the user's intent.

Parameters:
query -
Returns:
the sanitized query

getCurrentNamespace

protected String getCurrentNamespace()
Determines the current namespace we are operating on. For example, if you have multiple sites set up, you may want to filter that here.

Returns:
the global namespace

getLocalePrefix

protected String getLocalePrefix()
Determines if there is a locale prefix that needs to be applied to the given field for this particular request. By default, a locale prefix is not applicable for category, explicitCategory, or fields that have type Price. Also, it is not applicable for non-translatable fields

Returns:
the global prefix if there is one, "" if there isn't

getDefaultLocale

protected Locale getDefaultLocale()
Returns the default locale. Will cache the result for subsequent use. Note: There is no currently configured cache invalidation strategy for the the default locale. Override this method to provide for one if you need it.

Returns:
the default locale

getDefaultLocalePrefix

protected String getDefaultLocalePrefix()
Returns:
the default locale's prefix

getPropertyNameForFieldSearchable

protected String getPropertyNameForFieldSearchable(Field field,
                                                   FieldType searchableFieldType,
                                                   String prefix)
Returns the property name for the given field, field type, and prefix

Parameters:
field -
searchableFieldType -
prefix -
Returns:
the property name for the field and fieldtype

getPropertyNameForFieldFacet

protected String getPropertyNameForFieldFacet(Field field,
                                              String prefix)
Returns the property name for the given field, its configured facet field type, and the given prefix

Parameters:
field -
prefix -
Returns:
the property name for the facet type of this field

getPropertyNameForFieldSearchable

protected String getPropertyNameForFieldSearchable(Field field,
                                                   FieldType searchableFieldType)
Returns the property name for the given field and field type. This will apply the global prefix to the field, and it will also apply either the locale prefix or the pricelist prefix, depending on whether or not the field type was set to FieldType.PRICE

Parameters:
field -
searchableFieldType -
Returns:
the property name for the field and fieldtype

getPropertyNameForFieldFacet

protected String getPropertyNameForFieldFacet(Field field)
Returns the property name for the given field and its configured facet field type. This will apply the global prefix to the field, and it will also apply either the locale prefix or the pricelist prefix, depending on whether or not the field type was set to FieldType.PRICE

Parameters:
field -
Returns:
the property name for the facet type of this field

getNamespaceFieldName

protected String getNamespaceFieldName()
Returns:
the name of the field that keeps track what namespace this document belongs to

getIdFieldName

protected String getIdFieldName()
Returns:
the id field name, with the global prefix as appropriate

getCategoryFieldName

protected String getCategoryFieldName()
Returns:
the category field name, with the global prefix as appropriate

getExplicitCategoryFieldName

protected String getExplicitCategoryFieldName()
Returns:
the explicit category field name, with the global prefix as appropriate

getSearchableFieldName

protected String getSearchableFieldName(String prefix)
Parameters:
prefix -
Returns:
the searchable field name, with the global and specific prefix as appropriate

getSearchableFieldName

protected String getSearchableFieldName()
Returns:
the searchable field name, with the global and locale prefixes as appropriate

getCategorySortFieldName

protected String getCategorySortFieldName(Category category)
Parameters:
category -
Returns:
the default sort field name for this category


Copyright © 2013. All Rights Reserved.