Thursday 9 July 2015

Creating custom request handler in Solr

Scenario
We have two Solr cores, Product List and Store Locator. The “ProductList” stores the details of the product like ProductID, ProductName and ProductDescription. The “StoreLocator” stores the details of the stores like StoreID, ProductID, StoreLocation and ProductAvailability. This core provides us information about the store and product availability in each store.We need to display the product details and the store location (that are coming from two different cores) of each product in the solr response.

Description of the handler

The newly created request handler in Solr named “/retrieve” will search for all records in ProductList core and then iterates through all the Solr documents to read the ProductID attribute. It then establishes a connection with the StoreLocator core and reads the StoreLocation attribute for each product based on this ProductID. Finally, the handler displays all the attributes from ProductList core along with the store location attribute from the StoreLocator core for each product.

Steps to follow

Step 1 - Create new cores - ProductList and StoreLocator

Navigate to solr directory at ..\solr-4.3.1\example\solr and add the following lines (in bold) in solr.xml file -

  <cores adminPath="/admin/cores" defaultCoreName="collection1" host="${host:}" hostPort="${jetty.port:8983}" hostContext="${hostContext:solr}"
zkClientTimeout="${zkClientTimeout:15000}">
<core name="collection1" instanceDir="collection1" />
<core name="ProductList" instanceDir="ProductList" />
<core name="StoreLocator" instanceDir="StoreLocator" />
 </cores>

Step 2 - Place the configuration files for ProductList core

Copy and paste the existing ‘collection1’ folder at ..\solr-4.3.1\example\solr. Now rename the folder as ‘ProductList’.

Step 3 - Schema Design for ProductList core

Navigate to conf folder at ..\solr-4.3.1\example\solr\ProductList\conf and add the following lines (in bold) in schema.xml file -
<fields>
<!--Product List -->
<field name="ProductID" type="string" indexed="true" stored="true" required="true"/>
<field name="ProductName" type="string" indexed="true" stored="true" required="false"/>
<field name="ProductDescription" type="string" indexed="true" stored="true" required="false"/>
</fields>

Please note that the unique key is the ProductID here.

Step 4 - Place the configuration files for StoreLocator core

Copy and paste the existing ‘collection1’ folder at ..\solr-4.3.1\example\solr\collection1. Now rename the folder as ‘StoreLocator’.

Step 5 - Schema Design for StoreLocator core

Navigate to conf folder at ..\solr-4.3.1\example\solr\StoreLocator\conf and add the following lines (in bold) in schema.xml file -
<fields>
<!--Store Locator Details -->
<field name="StoreID" type="string" indexed="true" stored="true" required="true"/>
<field name="ProductID" type="string" indexed="true" stored="true" required="false"/>
<field name="StoreLocation" type="string" indexed="true" stored="true" required="false"/>
<field name="ProductAvailability" type="string" indexed="true" stored="true" required="false"/>
</fields>

Please note that the unique key is the StoreID here.

Step 6 - Start the Solr sever

Navigate to ..\solr-4.3.1\example and run the command –
java -jar start.jar
Once the server is started, you could see the ProductList and StoreLocator cores getting listed in the core selector in the Solr Admin Page as shown below.

Step 7 - Indexing data into both the cores

Index data into ProductList and StoreLocator cores. Please ensure that the ProductID is the common attribute in these cores.

After indexing data into ProductList core –



After indexing data into StoreLocator core –


Step 8 - Configure the Request Handler in both the cores

Configure the request handler in the solrconfig.xml of both the cores. The handler is configured to trigger in response to  ‘../solr/retrieve’ request.

<requestHandler name="/retrieve" class="com.custom.solr.handlers.SolrJSearcher">
</requestHandler>

Step 9 - Create a custom subclass of RequestHandlerBase and override the handleRequestBody(SolrQueryRequest, SolrQueryResponse) method

package com.custom.solr.handlers;

import java.util.Iterator;
import org.apache.solr.client.solrj.impl.HttpSolrServer;
import org.apache.solr.client.solrj.response.QueryResponse;
import org.apache.solr.common.SolrDocument;
import org.apache.solr.common.SolrDocumentList;
import org.apache.solr.common.params.ModifiableSolrParams;
import org.apache.solr.handler.RequestHandlerBase;
import org.apache.solr.request.SolrQueryRequest;
import org.apache.solr.response.SolrQueryResponse;

/**
* Title : SolrJSearcher Description : Searches for all entries in ProductList collection and gets the ProductID entries.
* Makes a connection with the StoreLocation collection, for every ProductID retrieved in the above step, it queries and
* gets the corresponding StoreLocation. It displays the product details and the store location
* of each product in the Solr response.
*
* Revision History ----------------
*
* @version 1.0
* @author 5/22/15 sofia l {None.} Written Code
*/
public class SolrJSearcher extends RequestHandlerBase {
HttpSolrServer serverProductList =  null;
HttpSolrServer serverStoreLocator =  null;
static org.apache.log4j.Logger logger = org.apache.log4j.Logger.getLogger(SolrJSearcher.class.getName());

public SolrJSearcher(){     
serverProductList = new HttpSolrServer("http://localhost:8983/solr/ProductList");
serverStoreLocator = new HttpSolrServer("http://localhost:8983/solr/StoreLocator");
}

@Override
public String getDescription() {
return "SolrJSearcher";
}

@Override
public String getSource() {
return null;
}

@Override
public void handleRequestBody(SolrQueryRequest req, SolrQueryResponse resp) throws Exception {

String productID = null;
ModifiableSolrParams qparamsProductID=new ModifiableSolrParams();
qparamsProductID.add("q","*:*");
QueryResponse qresProductID=serverProductList.query(qparamsProductID);
SolrDocumentList resultsProductID=qresProductID.getResults();
SolrDocumentList resultsStoreLocation = null;
long numFoundProductID = qresProductID.getResults().getNumFound();

// Iterate through solr response
SolrDocument solrDocumentforProductID = null;
for (Iterator<SolrDocument> iterator = resultsProductID.iterator(); iterator.hasNext();) {
for (int i = 0; i < numFoundProductID; i++) {
solrDocumentforProductID = (SolrDocument) iterator.next();
productID = (String) solrDocumentforProductID.getFieldValue("ProductID");
ModifiableSolrParams qparamsStoreLocation=new ModifiableSolrParams();
qparamsStoreLocation.add("q","ProductID:" +productID);
QueryResponse qresStoreLocation=serverStoreLocator.query(qparamsStoreLocation);
resultsStoreLocation=qresStoreLocation.getResults();
long numFoundStoreLocation = qresStoreLocation.getResults().getNumFound();

// Iterate through solr response
SolrDocument solrDocumentforStoreLocation = null;
String storeLocation = null;
for (Iterator<SolrDocument> iterator1 = resultsStoreLocation.iterator();iterator1.hasNext();) {
for (int j = 0; j < numFoundStoreLocation; j++) {
solrDocumentforStoreLocation = (SolrDocument) iterator1.next();
storeLocation = (String) solrDocumentforStoreLocation.getFieldValue("StoreLocation");
solrDocumentforProductID.addField("StoreLocation", storeLocation);
}
}
}
resp.add("Results", resultsProductID);                       
}
}
}

Now compile the code and generate the jar file “SolrJSearcher.jar”.

Step 10 - Place the library files

Place all the required jar files (solr-solrj-4.3.1.jar, solr-core-4.3.1.jar and log4j-1.2.16.jar) including the SolrJSearcher.jar inside a directory named ‘handerlib’ at solrhome . Specify the path of the directory in the solrconfig.xml of both the cores as given below –

<lib dir="C:\..\..\..\solr-4.3.1\solr-4.3.1\example\solr\handlerlib" regex=".*\.jar" />

Step 11 - Re-Start the solr server and hit the below url to access the custom request handler

No comments:

Post a Comment