Saturday 5 July 2014

Customising CatalogNavigationView to add native solr data

This post has been written to illustrate a customization scenario to briefly touch upon all the components involved in the Websphere commerce search. The code snippets are just for example purpose and need to be modified with proper exception handling etc. The idea here is to show an end-to-end flow of the IBM search framework and help new starters understand how the search can be customized.
Also the customization in the xmls need to be made as per IBM best practises. i.e - the bod xmls need to be moved to the "-ext" folders and extended where applicable.

Scenario:
My website category browse page  has a price slider . The right end of the price slider needs to be the max price for the current search query. This maxprice  is the maximum price of the product which is included in the total count, and not just the current page.

eg: for browse on the men category, which has 1000 products, even when we click on page one we should get the max price of all the 1000 products and not just the current 20 products displayed on page 1.
The best way to get this is from SOLR.

2 ways of doing it...
1) On the jsp, do an addtional wcf:getdata call on catalogNavigationView, for the same search expression with PageSize set to 1 and sorted by Price descending. This will give you the max price product for the current search criteria.
Although this will work, this may not be the right way.

2) Customize the search framework to return this data.
we will take this up as this touches a lot of commerce search framework points and suits well for me to explain the various customizations that i did.

So here we go.........

On the CategoryNavigationSetup.jsp, we have the below  wcf:tag

<wcf:getData type="com.ibm.commerce.catalog.facade.datatypes.CatalogNavigationViewType" var="catalogNavigationView"
        expressionBuilder="getCatalogNavigationViewByCategory" scope="request" varShowVerb="showCatalogNavigationView"
        maxItems="${pageSize}" recordSetStartNumber="${beginIndex}"  scope="request">
        <wcf:param name="searchProfile" value="IBM_ComposeProductListByCategoryId" />
        <wcf:param name="categoryId" value="${WCParam.categoryId}" />
        <wcf:param name="orderBy" value="${WCParam.orderBy}" />
        <wcf:param name="searchType" value="${searchType}" />
        <c:forEach var="facetValue" items="${paramValues.facet}">
            <wcf:param name="facet" value="${facetValue}" />
        </c:forEach>
        <wcf:param name="minPrice" value="${WCParam.minPrice}" />
        <wcf:param name="maxPrice" value="${WCParam.maxPrice}" />
               <wcf:contextData name="storeId" data="${WCParam.storeId}" />
        <wcf:contextData name="catalogId" data="${WCParam.catalogId}" />
        <c:forEach var="facetLimit" items="${paramValues.facetLimit}">
            <wcf:param name="facetLimit" value="${facetLimit}" />
        </c:forEach>
    </wcf:getData>

We need to modify the IBM_ComposeProductListByCategoryId search profile to retrieve more information (maxPrice)
To do this we will be changing the below file
WC/xml/config/com.ibm.commerce.catalog-fep/wc-search.xml   (Actiual implementation would be to move this file to -ext folder and extend the search profile)

The issue we have is, the maxprice data is not returned as part of the solr response. We will first need to identify a way of getting the maxprice data included on the native solr response, which we then can pass through the commerce framework to the front end.

A bit of reading pointed me to the SOLR stats component.
https://cwiki.apache.org/confluence/display/solr/The+Stats+Component
In a nutshell, you can ask SOLR to provide stats on a particular field. Here is how this will look.

A normal solr query will get back the results as below





The same query with our stats param looks like this















 As you can see we can now get back the max price for the query.

So to get this in commerce, below are the high level steps.

1) Add the stat parameter to the category browse query
2) Parse the solr response to pass the additional info
3) Add the info on the catalognavigationview  userdata field, to be used on the front end.


lets get going with step 1
In the /WC/xml/config/com.ibm.commerce.catalog-fep/wc-search.xml  file find the search profile "IBM_ComposeProductListByCategoryId" and add a custom query preprocessor (Ideally done in -ext folder). (http://www-01.ibm.com/support/knowledgecenter/SSZLC2_7.0.0/com.ibm.commerce.developer.doc/tasks/tsdsearchcustqpre.htm)
So this is how the OOB modified  profile will look


 <_config:profile indexName="CatalogEntry" name="IBM_ComposeProductListByCategoryId">
        <_config:query>
            <_config:param name="maxRows" value="500"/>
            <_config:param name="maxTimeAllowed" value="15000"/>
            <_config:provider classname="com.ibm.commerce.catalog.facade.server.services.search.expression.solr.SolrSearchByCategoryExpressionProvider"/>
            <_config:provider classname="com.ibm.commerce.catalog.facade.server.services.search.expression.solr.SolrSearchByManufacturerExpressionProvider"/>
            <_config:provider classname="com.ibm.commerce.catalog.facade.server.services.search.expression.solr.SolrSearchByPriceExpressionProvider"/>
            <_config:provider classname="com.ibm.commerce.catalog.facade.server.services.search.expression.solr.SolrSearchByFacetExpressionProvider"/>
            <_config:provider classname="com.ibm.commerce.catalog.facade.server.services.search.expression.solr.SolrSearchByStorePathExpressionProvider"/>
            <_config:provider classname="com.ibm.commerce.catalog.facade.server.services.search.expression.solr.SolrSearchByPublishedEntryOnlyExpressionProvider"/>
            <_config:provider classname="com.ibm.commerce.catalog.facade.server.services.search.expression.solr.SolrSearchByCustomExpressionProvider"/>
            <_config:provider classname="com.ibm.commerce.catalog.facade.server.services.search.expression.solr.SolrSearchFacetConditionExpressionProvider"/>
            <_config:provider classname="com.ibm.commerce.catalog.facade.server.services.search.expression.solr.SolrSearchTypeExpressionProvider"/>
            <_config:provider classname="com.ibm.commerce.catalog.facade.server.services.search.expression.solr.SolrSearchSequencingExpressionProvider"/>
            <_config:provider classname="com.ibm.commerce.catalog.facade.server.services.search.expression.solr.SolrSearchProductEntitlementExpressionProvider"/>
            <_config:field name="name"/>
             <_config:preprocessor classname="com.tmpl.commerce.search.CustomPriceQueryPreProcessor"/>
      </_config:query>
        <_config:sort>
            <_config:field name="1" value="mfName_ntk_cs asc"/>
            <_config:field name="2" value="name_ntk asc"/>
            <_config:field name="3" value="price_* asc"/>
            <_config:field name="4" value="price_* desc"/>
        </_config:sort>
        <_config:result>
            <_config:filter classname="com.ibm.commerce.catalog.facade.server.services.search.metadata.SearchCatalogEntryViewPriceResultFilter"/>
            <!--
            <_config:filter classname="com.ibm.commerce.catalog.facade.server.services.search.metadata.SearchCatalogEntryViewSingleSKUResultFilter"/>
            -->
            <_config:filter classname="com.ibm.commerce.catalog.facade.server.services.search.metadata.SearchCatalogNavigationViewPreviewResultFilter"/>
            <_config:filter classname="com.ibm.commerce.catalog.facade.server.services.search.metadata.SearchCatalogNavigationViewDynamicKitResultFilter"/>
            <_config:filter classname="com.ibm.commerce.catalog.facade.server.services.search.metadata.SearchCatalogEntryViewDescriptionResultFilter"/>
            <_config:filter classname="com.ibm.commerce.catalog.facade.server.services.search.metadata.solr.SolrFacetEntryViewImageAndSequenceResultFilter"/>
            <_config:filter classname="com.tmpl.commerce.search.CustomPriceResultFilter"/>  
        </_config:result>
        <_config:facets>
            <_config:param name="sort" value="count"/>
            <_config:param name="minCount" value="1"/>
            <_config:param name="limit" value="200"/>
            <_config:converter classname="com.ibm.commerce.catalog.facade.server.services.search.metadata.solr.SolrSearchCategoryFacetMetaDataConverter" field="parentCatgroup_id_search"/>
        </_config:facets>
    </_config:profile>



And below is the implementaion of the pre-processor


package com.tmpl.commerce.search;
import org.apache.solr.client.solrj.SolrQuery;
import com.ibm.commerce.foundation.server.services.search.query.solr.AbstractSolrSearchQueryPreprocessor;
import com.ibm.commerce.foundation.server.services.dataaccess.SelectionCriteria;
import com.ibm.commerce.foundation.server.services.search.query.SearchQueryPreprocessor;
public class CustomPriceQueryPreProcessor extends AbstractSolrSearchQueryPreprocessor implements
SearchQueryPreprocessor {
             public CustomPriceQueryPreProcessor(String str) {
             super();
         }

         public void invoke(SelectionCriteria selectionCriteria,
                 Object... queryRequestObjects) throws RuntimeException {
             super.invoke(selectionCriteria, queryRequestObjects);
           
                 // pre-processor adds the stats  expression on the solr query.
            
             SolrQuery iQuery = (SolrQuery) queryRequestObjects[0];
                 iSolrQuery.setParam("stats", "true");
                 iSolrQuery.setParam("stats.field", "price_GBP");   // the currency may have to be picked up from the search criteria or passed in.
                 System.out.println("custom preprocess" + iSolrQuery);
         }
     }



Restart the server.
Here is a before/after of the query getting fired for the same category search request

BEFORE
facet.field=parentCatgroup_id_search&facet.field=ads_f10009_ntk_cs&facet.field=ads_f10007_ntk_cs&facet.field=ads_f10017_ntk_cs&facet.field=ads_f10005_ntk_cs&facet.field=ads_f10015_ntk_cs&facet.field=ads_f10013_ntk_cs&facet.field=ads_f10001_ntk_cs&facet.field=ads_f10011_ntk_cs&facet.field=mfName_ntk_cs&facet.field=ads_f10008_ntk_cs&facet.field=ads_f10006_ntk_cs&facet.field=ads_f10501_ntk_cs&facet.field=ads_f10016_ntk_cs&facet.field=ads_f10014_ntk_cs&facet.field=ads_f10002_ntk_cs&facet.field=ads_f10012_ntk_cs&f.ads_f10501_ntk_cs.facet.sort=count&f.ads_f10017_ntk_cs.facet.limit=-1&f.ads_f10002_ntk_cs.facet.limit=-1&f.ads_f10016_ntk_cs.facet.sort=count&f.ads_f10016_ntk_cs.facet.limit=-1&f.ads_f10001_ntk_cs.facet.limit=-1&f.ads_f10006_ntk_cs.facet.sort=count&f.ads_f10011_ntk_cs.facet.sort=count&f.mfName_ntk_cs.facet.sort=count&facet=true&f.ads_f10015_ntk_cs.facet.limit=-1&f.ads_f10009_ntk_cs.facet.limit=-1&f.parentCatgroup_id_search.facet.sort=count&f.ads_f10001_ntk_cs.facet.sort=count&f.ads_f10008_ntk_cs.facet.sort=count&f.ads_f10013_ntk_cs.facet.sort=count&f.ads_f10014_ntk_cs.facet.limit=-1&f.ads_f10008_ntk_cs.facet.limit=-1&start=0&f.ads_f10501_ntk_cs.facet.limit=-1&fq=catalog_id:"10051"&fq=parentCatgroup_id_facet:"10051_45508"&fq=storeent_id:("10152"+"10051")&fq=published:1&fq=-(catenttype_id_ntk_cs:ItemBean+AND+parentCatentry_id:[*+TO+*])&fq=(+%2B*:*+%2B*:*)&f.ads_f10015_ntk_cs.facet.sort=count&f.ads_f10013_ntk_cs.facet.limit=-1&f.ads_f10007_ntk_cs.facet.limit=-1&f.parentCatgroup_id_search.facet.limit=21&f.mfName_ntk_cs.facet.mincount=1&debugQuery=false&f.ads_f10001_ntk_cs.facet.mincount=1&f.ads_f10012_ntk_cs.facet.limit=-1&f.ads_f10006_ntk_cs.facet.limit=-1&f.ads_f10005_ntk_cs.facet.sort=count&f.ads_f10002_ntk_cs.facet.mincount=1&f.ads_f10006_ntk_cs.facet.mincount=1&f.ads_f10008_ntk_cs.facet.mincount=1&f.ads_f10017_ntk_cs.facet.sort=count&f.ads_f10005_ntk_cs.facet.mincount=1&f.ads_f10007_ntk_cs.facet.mincount=1&f.ads_f10009_ntk_cs.facet.mincount=1&f.ads_f10005_ntk_cs.facet.limit=-1&f.ads_f10011_ntk_cs.facet.limit=-1&f.ads_f10012_ntk_cs.facet.sort=count&q=*:*&f.ads_f10007_ntk_cs.facet.sort=count&f.ads_f10011_ntk_cs.facet.mincount=1&f.ads_f10013_ntk_cs.facet.mincount=1&f.ads_f10012_ntk_cs.facet.mincount=1&f.ads_f10015_ntk_cs.facet.mincount=1&f.ads_f10002_ntk_cs.facet.sort=count&f.ads_f10014_ntk_cs.facet.mincount=1&version=2&f.ads_f10016_ntk_cs.facet.mincount=1&rows=24&timeAllowed=15000&f.ads_f10017_ntk_cs.facet.mincount=1&f.mfName_ntk_cs.facet.limit=21&f.parentCatgroup_id_search.facet.mincount=1&f.ads_f10014_ntk_cs.facet.sort=count&sort=name_ntk+asc&wt=javabin&f.ads_f10009_ntk_cs.facet.sort=count&f.ads_f10501_ntk_cs.facet.mincount=1
 


AFTER
facet.field=parentCatgroup_id_search&facet.field=ads_f10009_ntk_cs&facet.field=ads_f10007_ntk_cs&facet.field=ads_f10017_ntk_cs&facet.field=ads_f10005_ntk_cs&facet.field=ads_f10015_ntk_cs&facet.field=ads_f10013_ntk_cs&facet.field=ads_f10001_ntk_cs&facet.field=ads_f10011_ntk_cs&facet.field=mfName_ntk_cs&facet.field=ads_f10008_ntk_cs&facet.field=ads_f10006_ntk_cs&facet.field=ads_f10501_ntk_cs&facet.field=ads_f10016_ntk_cs&facet.field=ads_f10014_ntk_cs&facet.field=ads_f10002_ntk_cs&facet.field=ads_f10012_ntk_cs&f.ads_f10501_ntk_cs.facet.sort=count&f.ads_f10017_ntk_cs.facet.limit=-1&f.ads_f10002_ntk_cs.facet.limit=-1&f.ads_f10016_ntk_cs.facet.sort=count&f.ads_f10016_ntk_cs.facet.limit=-1&f.ads_f10001_ntk_cs.facet.limit=-1&f.ads_f10006_ntk_cs.facet.sort=count&f.ads_f10011_ntk_cs.facet.sort=count&f.mfName_ntk_cs.facet.sort=count&facet=true&f.ads_f10015_ntk_cs.facet.limit=-1&f.ads_f10009_ntk_cs.facet.limit=-1&f.parentCatgroup_id_search.facet.sort=count&f.ads_f10001_ntk_cs.facet.sort=count&f.ads_f10008_ntk_cs.facet.sort=count&f.ads_f10013_ntk_cs.facet.sort=count&f.ads_f10014_ntk_cs.facet.limit=-1&f.ads_f10008_ntk_cs.facet.limit=-1&start=0&f.ads_f10501_ntk_cs.facet.limit=-1&fq=catalog_id:"10051"&fq=parentCatgroup_id_facet:"10051_45508"&fq=storeent_id:("10152"+"10051")&fq=published:1&fq=-(catenttype_id_ntk_cs:ItemBean+AND+parentCatentry_id:[*+TO+*])&fq=(+%2B*:*+%2B*:*)&f.ads_f10015_ntk_cs.facet.sort=count&f.ads_f10013_ntk_cs.facet.limit=-1&f.ads_f10007_ntk_cs.facet.limit=-1&f.parentCatgroup_id_search.facet.limit=21&f.mfName_ntk_cs.facet.mincount=1&debugQuery=false&f.ads_f10001_ntk_cs.facet.mincount=1&f.ads_f10012_ntk_cs.facet.limit=-1&f.ads_f10006_ntk_cs.facet.limit=-1&f.ads_f10005_ntk_cs.facet.sort=count&f.ads_f10002_ntk_cs.facet.mincount=1&f.ads_f10006_ntk_cs.facet.mincount=1&f.ads_f10008_ntk_cs.facet.mincount=1&f.ads_f10017_ntk_cs.facet.sort=count&f.ads_f10005_ntk_cs.facet.mincount=1&f.ads_f10007_ntk_cs.facet.mincount=1&f.ads_f10009_ntk_cs.facet.mincount=1&f.ads_f10005_ntk_cs.facet.limit=-1&f.ads_f10011_ntk_cs.facet.limit=-1&f.ads_f10012_ntk_cs.facet.sort=count&q=*:*&f.ads_f10007_ntk_cs.facet.sort=count&stats=true&f.ads_f10011_ntk_cs.facet.mincount=1&f.ads_f10013_ntk_cs.facet.mincount=1&f.ads_f10012_ntk_cs.facet.mincount=1&f.ads_f10015_ntk_cs.facet.mincount=1&f.ads_f10002_ntk_cs.facet.sort=count&f.ads_f10014_ntk_cs.facet.mincount=1&version=2&f.ads_f10016_ntk_cs.facet.mincount=1&rows=24&timeAllowed=15000&stats.field=price_GBP&f.ads_f10017_ntk_cs.facet.mincount=1&f.mfName_ntk_cs.facet.limit=21&f.parentCatgroup_id_search.facet.mincount=1&f.ads_f10014_ntk_cs.facet.sort=count&sort=name_ntk+asc&wt=javabin&f.ads_f10009_ntk_cs.facet.sort=count&f.ads_f10501_ntk_cs.facet.mincount=1


So now the solr response received by commerce will start including stats for all the category queries.


Moving on to step 2
Although the response has data in it, it will be ignored unless we do something with it.
To do this we write a solrquerypostprocessor (http://www-01.ibm.com/support/knowledgecenter/SSZLC2_7.0.0/com.ibm.commerce.developer.doc/tasks/tsdsearchcustqpost.htm) and register it against the profile as below
PostProcessor has access to the response received from SOLR as well as the commerce solr response object SolrCatalogNavigationViewImpl. So in here we read the additional data from the SOLR response and set it on the commerce response in the extendedDataMap.

So here is the OOB modified search profile

 <_config:profile indexName="CatalogEntry" name="IBM_ComposeProductListByCategoryId">
        <_config:query>
            <_config:param name="maxRows" value="500"/>
            <_config:param name="maxTimeAllowed" value="15000"/>
            <_config:provider classname="com.ibm.commerce.catalog.facade.server.services.search.expression.solr.SolrSearchByCategoryExpressionProvider"/>
            <_config:provider classname="com.ibm.commerce.catalog.facade.server.services.search.expression.solr.SolrSearchByManufacturerExpressionProvider"/>
            <_config:provider classname="com.ibm.commerce.catalog.facade.server.services.search.expression.solr.SolrSearchByPriceExpressionProvider"/>
            <_config:provider classname="com.ibm.commerce.catalog.facade.server.services.search.expression.solr.SolrSearchByFacetExpressionProvider"/>
            <_config:provider classname="com.ibm.commerce.catalog.facade.server.services.search.expression.solr.SolrSearchByStorePathExpressionProvider"/>
            <_config:provider classname="com.ibm.commerce.catalog.facade.server.services.search.expression.solr.SolrSearchByPublishedEntryOnlyExpressionProvider"/>
            <_config:provider classname="com.ibm.commerce.catalog.facade.server.services.search.expression.solr.SolrSearchByCustomExpressionProvider"/>
            <_config:provider classname="com.ibm.commerce.catalog.facade.server.services.search.expression.solr.SolrSearchFacetConditionExpressionProvider"/>
            <_config:provider classname="com.ibm.commerce.catalog.facade.server.services.search.expression.solr.SolrSearchTypeExpressionProvider"/>
            <_config:provider classname="com.ibm.commerce.catalog.facade.server.services.search.expression.solr.SolrSearchSequencingExpressionProvider"/>
            <_config:provider classname="com.ibm.commerce.catalog.facade.server.services.search.expression.solr.SolrSearchProductEntitlementExpressionProvider"/>
            <_config:field name="name"/>
             <_config:preprocessor classname="com.tmpl.commerce.search.CustomPriceQueryPreProcessor"/>
                 <_config:postprocessor classname="com.tmpl.commerce.search.CustomSolrSearchStatsQueryPostprocessor" />

        </_config:query>
        <_config:sort>
            <_config:field name="1" value="mfName_ntk_cs asc"/>
            <_config:field name="2" value="name_ntk asc"/>
            <_config:field name="3" value="price_* asc"/>
            <_config:field name="4" value="price_* desc"/>
        </_config:sort>
        <_config:result>
            <_config:filter classname="com.ibm.commerce.catalog.facade.server.services.search.metadata.SearchCatalogEntryViewPriceResultFilter"/>
            <!--
            <_config:filter classname="com.ibm.commerce.catalog.facade.server.services.search.metadata.SearchCatalogEntryViewSingleSKUResultFilter"/>
            -->
            <_config:filter classname="com.ibm.commerce.catalog.facade.server.services.search.metadata.SearchCatalogNavigationViewPreviewResultFilter"/>
            <_config:filter classname="com.ibm.commerce.catalog.facade.server.services.search.metadata.SearchCatalogNavigationViewDynamicKitResultFilter"/>
            <_config:filter classname="com.ibm.commerce.catalog.facade.server.services.search.metadata.SearchCatalogEntryViewDescriptionResultFilter"/>
            <_config:filter classname="com.ibm.commerce.catalog.facade.server.services.search.metadata.solr.SolrFacetEntryViewImageAndSequenceResultFilter"/>
            <_config:filter classname="com.tmpl.commerce.search.CustomPriceResultFilter"/>  
        </_config:result>
        <_config:facets>
            <_config:param name="sort" value="count"/>
            <_config:param name="minCount" value="1"/>
            <_config:param name="limit" value="200"/>
            <_config:converter classname="com.ibm.commerce.catalog.facade.server.services.search.metadata.solr.SolrSearchCategoryFacetMetaDataConverter" field="parentCatgroup_id_search"/>
        </_config:facets>
    </_config:profile>


And here is the postprocessor code.

package com.tmpl.commerce.search;
import java.util.HashMap;
import java.util.Map;

import org.apache.solr.client.solrj.response.FieldStatsInfo;
import org.apache.solr.client.solrj.response.QueryResponse;

import com.ibm.commerce.catalog.facade.datatypes.CatalogNavigationViewType;
import com.ibm.commerce.catalog.facade.server.services.search.metadata.solr.SolrCatalogNavigationViewImpl;
import com.ibm.commerce.foundation.server.services.dataaccess.SelectionCriteria;
import com.ibm.commerce.foundation.server.services.search.query.SearchQueryPostprocessor;
import com.ibm.commerce.foundation.server.services.search.query.solr.AbstractSolrSearchQueryPostprocessor;

public class CustomSolrSearchStatsQueryPostprocessor extends
    AbstractSolrSearchQueryPostprocessor implements
    SearchQueryPostprocessor {

public CustomSolrSearchStatsQueryPostprocessor(String componentId) {
    super();
}

public void invoke(SelectionCriteria selectionCriteria,
        Object... queryResponseObjects) throws RuntimeException {

    // Initializing ...
    // iQueryResponse - Native response object from Solr
    // iResponseObject - Physical data object model for representing this response
    super.invoke(selectionCriteria, queryResponseObjects);

    System.out.println("iResponseObject" + iResponseObject.getEntityObjects().size());
    // special physical type of SolrDocuments so the BOM factory can map this down to a mediator.
    SolrCatalogNavigationViewImpl entity = (SolrCatalogNavigationViewImpl) iResponseObject.getEntityObjects().get(0);
   
    // get the extended data map and set our custom value in here. This will then we retrieved by the mediator
    Map<String , Object> extData = entity.getExtendedData();
    System.out.println(((QueryResponse)queryResponseObjects[0]).getFieldStatsInfo());
    Map statsInfo = ((QueryResponse)queryResponseObjects[0]).getFieldStatsInfo();
   
    System.out.println(statsInfo.keySet().toString());
   
    FieldStatsInfo statsinfo = (FieldStatsInfo)statsInfo.get("price_GBP");
    System.out.println(
            statsinfo.getMax());
    // get the stats info and set on the extended data map
    extData.put("maxPrice",statsinfo.getMax().toString());
    entity.setExtendedData(extData);
    //put the entity object back on the response object
    iResponseObject.getEntityObjects().set(0, entity);
}

}



Step 3 :  register a a part mediator to populate the custom value into the catalognavigationview
(http://www-01.ibm.com/support/knowledgecenter/SSZLC2_7.0.0/com.ibm.commerce.developer.doc/tasks/tsdsearchgudemediator.htm)
To do this we have to update the object mediator xml file. (use -ext folder)
/WC/xml/config/com.ibm.commerce.catalog-fep/wc-business-object-mediator.xml

Below is the OOB config with the change highlighted  to register the mediator

    <_config:object logicalType="com.ibm.commerce.catalog.facade.datatypes.CatalogNavigationViewType"
                    physicalType="com.ibm.commerce.catalog.facade.server.services.search.metadata.solr.SolrCatalogNavigationViewImpl">
          <_config:mediator interfaceName="com.ibm.commerce.foundation.server.services.dataaccess.bom.mediator.ReadBusinessObjectMediator"
                            className="com.ibm.commerce.catalog.facade.server.services.dataaccess.bom.mediator.solr.SolrReadCatalogNavigationViewMediator">
            <_config:mediator-properties>
                <_config:mediator-property name="CatalogEntryView/UniqueID" value="catentry_id" />
                <_config:mediator-property name="CatalogEntryView/CatalogEntryTypeCode" value="catenttype_id_ntk_cs" />
                <_config:mediator-property name="CatalogEntryView/Buyable" value="buyable" />
                <_config:mediator-property name="CatalogEntryView/Published" value="published" />
                <_config:mediator-property name="CatalogEntryView/DisallowRecurringOrder" value="disallowRecOrder" />
                <_config:mediator-property name="CatalogEntryView/SubscriptionTypeCode" value="subscripType" />
                <_config:mediator-property name="CatalogEntryView/Name" value="name" />
                <_config:mediator-property name="CatalogEntryView/ShortDescription" value="shortDescription" />
                <_config:mediator-property name="CatalogEntryView/LongDescription" value="longDescription" />
                <_config:mediator-property name="CatalogEntryView/Keyword" value="keyword" />
                <_config:mediator-property name="CatalogEntryView/Thumbnail" value="thumbnail" />
                <_config:mediator-property name="CatalogEntryView/FullImage" value="fullImage" />
                <_config:mediator-property name="CatalogEntryView/Manufacturer" value="mfName_ntk" />
                <_config:mediator-property name="CatalogEntryView/PartNumber" value="partNumber_ntk" />
                <_config:mediator-property name="CatalogEntryView/StoreID" value="storeent_id" />
                <_config:mediator-property name="CatalogEntryView/Price/Value[(Currency='*')]" value="price_*" />
                <_config:mediator-property name="CatalogEntryView/UserData[(Name='listpricetext_GBP')]" value="xf_listprice_GBP" />
                <_config:mediator-property name="CatalogEntryView/UserData[(Name='listpricetext_USD')]" value="xf_listprice_USD" />
                <_config:mediator-property name="CatalogEntryView/UserData[(Name='listpricetext_EUR')]" value="xf_listprice_EUR" />
                <_config:mediator-property name="CatalogEntryView/UserData[(Name='listpricetext_AUD')]" value="xf_listprice_AUD" />
                <_config:mediator-property name="CatalogEntryView/UserData[(Name='availabilitydate')]" value="availabilitydate" />               
                <_config:mediator-property name="CatalogEntryView/CatalogGroupID" value="parentCatgroup_id_search" />
                <_config:mediator-property name="CatalogEntryView/ParentCatalogGroupID" value="parentCatgroup_id_facet" />
                <_config:mediator-property name="CatalogEntryView/ParentCatalogEntryID" value="parentCatentry_id" />
                <_config:mediator-property name="CatalogEntryView/ChildCatalogEntryID" value="childCatentry_id" />
                <_config:mediator-property name="CatalogEntryView/CatalogID" value="catalog_id" />
                <_config:mediator-property name="CatalogGroupView/UniqueID" value="parentCatgroup_id_facet" />
                <_config:mediator-property name="CatalogGroupView/CatalogID" value="catalog_id" />
                <_config:mediator-property name="CatalogGroupView/CatalogGroupUniqueID" value="catgroup_id" />
                <_config:mediator-property name="CatalogGroupView/ParentCatalogGroupID" value="parentCatgroup_id_facet" />
                <_config:mediator-property name="CatalogGroupView/Identifier" value="identifier_ntk" />
                <_config:mediator-property name="CatalogGroupView/Name" value="name" />
                <_config:mediator-property name="CatalogGroupView/ShortDescription" value="shortDescription" />
                <_config:mediator-property name="CatalogGroupView/LongDescription" value="longDescription" />
                <_config:mediator-property name="CatalogGroupView/Thumbnail" value="thumbnail" />
                <_config:mediator-property name="CatalogGroupView/FullImage" value="fullImage" />
                <_config:mediator-property name="CatalogEntryView/Attachments/AttachmentAssetID" value="attachment_id" />
                <_config:mediator-property name="CatalogEntryView/Attachments/AttachmentAssetPath" value="path" />
                <_config:mediator-property name="CatalogEntryView/Attachments/Usage" value="rulename" />
                <_config:mediator-property name="CatalogEntryView/Attachments/MimeType" value="mimetype" />
                <_config:mediator-property name="CatalogEntryView/Attachments/MetaData[(Name='name')]" value="name" />
                <_config:mediator-property name="CatalogEntryView/Attachments/MetaData[(Name='identifier')]" value="identifier" />
                <_config:mediator-property name="CatalogEntryView/Attachments/MetaData[(Name='shortdesc')]" value="shortdesc" />
                <_config:mediator-property name="CatalogEntryView/Attachments/MetaData[(Name='longdesc')]" value="longdesc" />
                <_config:mediator-property name="CatalogEntryView/Attachments/MetaData[(Name='image')]" value="image" />
                <_config:mediator-property name="CatalogEntryView/Attachments/MetaData[(Name='size')]" value="tika_stream_size" />
                <_config:mediator-property name="WebContentView/UniqueID" value="attachmentrel_id" />
                <_config:mediator-property name="WebContentView/Name" value="name" />
                <_config:mediator-property name="WebContentView/URL" value="path" />
                <_config:mediator-property name="WebContentView/MetaData[(Name='mimetype')]" value="mimetype" />
                <_config:mediator-property name="WebContentView/MetaData[(Name='shortdesc')]" value="shortdesc" />
                <_config:mediator-property name="WebContentView/MetaData[(Name='longdesc')]" value="longdesc" />
               
                <_config:mediator-property name="CatalogEntryView/UserData[(Name='bestseller')]" value="bestseller" />
                <_config:mediator-property name="CatalogEntryView/UserData[(Name='savings')]" value="savings" />
                <_config:mediator-property name="CatalogEntryView/UserData[(Name='ratings')]" value="ratings" />
               
                <!--
                    The following section is used for mapping search index field to
                    property in CatalogEntryView/UserData.
                   
                    For example:
                   
                    <_config:mediator-property name="CatalogEntryView/UserData[(Name='SKU')]" value="partNumber_ntk" />
                   
                    and its value can be retrieved from the JSP as follows:
                   
                    ${catalogEntryView.userData.userDataField.SKU}
                 -->
            </_config:mediator-properties>
               <_config:part-mediator interfaceName="com.ibm.commerce.foundation.server.services.dataaccess.bom.mediator.ReadBusinessObjectPartMediator">
                <_config:part-mediator-implementation className="com.ibm.commerce.catalog.facade.server.services.dataaccess.bom.mediator.solr.SolrReadCatalogEntryViewPartMediator" />
                <_config:part-mediator-implementation className="com.ibm.commerce.catalog.facade.server.services.dataaccess.bom.mediator.solr.SolrReadCatalogGroupViewPartMediator" />
                <_config:part-mediator-implementation className="com.ibm.commerce.catalog.facade.server.services.dataaccess.bom.mediator.solr.SolrReadAttachmentAssetViewPartMediator" />
                <_config:part-mediator-implementation className="com.ibm.commerce.catalog.facade.server.services.dataaccess.bom.mediator.solr.SolrReadSuggestionViewPartMediator" />
                <_config:part-mediator-implementation className="com.ibm.commerce.catalog.facade.server.services.dataaccess.bom.mediator.solr.SolrReadFacetViewPartMediator" />               
                <_config:part-mediator-implementation className="com.ibm.commerce.catalog.facade.server.services.dataaccess.bom.mediator.solr.SolrReadBreadCrumbTrailViewPartMediator" />
                <_config:part-mediator-implementation className="com.ibm.commerce.catalog.facade.server.services.dataaccess.bom.mediator.solr.SolrReadWebContentViewPartMediator" />
                <!--
                    Note: SolrReadCatalogNavigationViewPostMediator must be declared as the last entry.
                 -->
                <_config:part-mediator-implementation className="com.ibm.commerce.catalog.facade.server.services.dataaccess.bom.mediator.solr.SolrReadCatalogNavigationViewPostMediator" />
                <_config:part-mediator-implementation className="com.tmpl.commerce.search.CustomPriceStatObjectMediator" />
            </_config:part-mediator>
          </_config:mediator>
    </_config:object>


and the mediator code

package com.tmpl.commerce.search;
import java.util.Collection;
import java.util.List;
import org.apache.solr.common.SolrDocument;
import org.apache.solr.common.SolrDocumentList;
import com.ibm.commerce.catalog.facade.datatypes.CatalogEntryViewType;
import com.ibm.commerce.catalog.facade.datatypes.CatalogNavigationViewType;
import com.ibm.commerce.catalog.facade.server.services.search.metadata.solr.SolrCatalogNavigationViewImpl;
import com.ibm.commerce.foundation.common.exception.AbstractApplicationException;
import com.ibm.commerce.foundation.server.services.dataaccess.bom.mediator.AbstractReadBusinessObjectPartMediatorImpl;

public class CustomPriceStatObjectMediator  extends AbstractReadBusinessObjectPartMediatorImpl{
   
          
      public void buildNounPart(Object noun, Object physicalEntity)
          throws AbstractApplicationException {

        // Create logical types for catalog navigation
        CatalogNavigationViewType catalogNavigationView = (CatalogNavigationViewType) noun;
       
        // Get the physical entity populated with retrieved information
        SolrCatalogNavigationViewImpl entity = (SolrCatalogNavigationViewImpl) physicalEntity;
       
        if (entity != null && catalogNavigationView != null) {
            // If maxprice is available (set in the postprocessor) then set it on the user data field
            if(entity.getExtendedData()!=null && entity.getExtendedData().containsKey("maxPrice") )
            catalogNavigationView.getUserData().getUserDataField().put("maxPrice", entity.getExtendedData().get("maxPrice").toString());
       
        }
      }
    }

   
   
Thats it....the maxPrice will now be available in the userData field on the catalognavigationview object.


To illustarte that , i put the below lines of code on my jsp


<c:forEach var="entry" items="${catalogNavigationView.userData.userDataField}">
  Key: <c:out value="${entry.key}"/>
  Value: <c:out value="${entry.value}"/>
</c:forEach>


This is how it gets printed on the front end





Although this is just a small part, hope this illustration is helpful and will give you all an idea into how the search framework works.

4 comments:

  1. Thanks Sandeep this blog definitely helps clarify customizations to Search in WCS using the BOD framework.

    ReplyDelete
  2. I've re-declared access profile in -ext folder, As part of customization I've added filter to the profile. Now I run into class not found error for the filter newly added.

    Any thoughts.

    Thanks,
    Vamsi

    ReplyDelete
  3. Hi Vamsi,

    Were you able to solve your class not found error for custom filter? I m facing similar problem. I have added my class in extensionlogic. Please can you tell me how to solve this issue

    Thanks

    ReplyDelete