2001 2002 2003 2004 2005 2006 2007 2008 2009 2010 2011 2012 2013 2014 2015 2016 2017 2018 2020

05/04/2008: Installation and Configuration of LocalSolr.

A client of mine asked me to investigate localsolr which= extends Apache Solr server with the ability to perform text searches filtered on geographical distance from a given point specified by latitude and longitude. Here are the steps that I followed:

Download the Software

  1. Downland Ant from http://apache.mirror99.com/ant/binaries/apache-ant-1.7.0-bin.tar.gz
  2. Download Tomcat from http://mirror.nyi.net/apache/tomcat/tomcat-6/v6.0.16/bin/apache-tomcat-6.0.16.tar.gz
  3. Download LocalLucene from http://downloads.sourceforge.net/locallucene/locallucene-r1.5.tar.gz
  4. Download LocalSolr from http://downloads.sourceforge.net/locallucene/localsolr-r1.5.tar.gz
  5. Download Solr-1.3-dev, via svn, to get the source code
    svn checkout http://svn.apache.org/repos/asf/lucene/solr/trunk apache-solr-1.3.dev
  6. Uncompress all of the software packages.

Create Environment Variables

  1. In Unix variants, update your .bash_login script to include the following. Of course, you'll need to set the variables correctly according to where you uncompressed the software. And don't forget to source the .bash_login file after you have changed it.
    export SUPPORT_DIR=/home/medined/support
    export LOCAL_LUCENE_HOME=$SUPPORT_DIR/locallucene-r1.5
    export LOCAL_SOLR_HOME=$SUPPORT_DIR/localsolr-r1.5
    export SOLR_HOME=$SUPPORT_DIR/apache-solr-1.3-dev
    export SOLR_CONFIG=/home/medined/.solr
    export TOMCAT_HOME=$SUPPORT_DIR/apache-tomcat-6.0.16
    
    export JAVA_OPTS="$JAVA_OPTS -Dsolr.solr.home=$SOLR_CONFIG"

Create Apache Solr War

  1. cd $SOLR_HOME
  2. ant dist

Configure Local Solr

  1. cp -R $SOLR_HOME/example/solr $SOLR_CONFIG
  2. cp $SOLR_HOME/libs/solr/apache-solr-1.3-dev.war $TOMCAT_HOME/webapps/solr.war mkdir $SOLR_CONFIG/lib
  3. cp $LOCAL_LUCENE_HOME/dist/locallucene.jar $SOLR_CONFIG/lib
  4. cp $LOCAL_SOLR_HOME/dist/localsolr.jar $SOLR_CONFIG/lib
  5. cp $LOCAL_LUCENE_HOME/lib/gt2-referencing-2.3.1.jar $SOLR_CONFIG/lib
  6. cp $LOCAL_LUCENE_HOME/lib/geoapi-nogenerics-2.1-M2.jar $SOLR_CONFIG/lib
  7. cp $LOCAL_LUCENE_HOME/lib/jsr108-0.01.jar $SOLR_CONFIG/lib
  8. Add the following to the end of $SOLR_CONFIG/conf/solrconfig.xml (just before the closing CONFIG tag:
      <updateRequestProcessor>
        <factory name="standard" class="solr.ChainedUpdateProcessorFactory" default="true">
          <chain class="com.pjaol.search.solr.update.LocalUpdateProcessorFactory">
            <str name="latField">lat</str>
            <str name="lngField">lng</str>
            <int name="startTier">9</int>
            <int name="endTier">17</int>
          </chain>
          <chain class="solr.LogUpdateProcessorFactory" >
           <!-- <int name="maxNumToLog">100</int> -->
          </chain>
          <chain class="solr.RunUpdateProcessorFactory" />
        </factory>
      </updateRequestProcessor>
    
     <requestHandler name="geo" class="com.pjaol.search.solr.LocalSolrRequestHandler">
        <!-- Custom latitude longitude fields, below are the defaults if not otherwise
        specified -->
         <str name="latField">lat</str>
         <str name="lngField">lng</str>
      </requestHandler>
  9. Add the following to the fields tag of $SOLR_CONFIG/conf/schema.xml
    <field name="lat" type="sdouble" indexed="true" stored="true"/>
       <field name="lng" type="sdouble" indexed="true" stored="true"/>
       <dynamicField name="_local*" type="sdouble" indexed="true" stored="true"/>

Controlling Tomcat

  • $TOMCAT_HOME/bin/startup.sh - this command starts Tomcat.
  • tail -f $TOMCAT_HOME/logs/catalina.out - this command lets you watch Tomcat's output log.
  • $TOMCAT_HOME/bin/shutdown.sh - this command stops Tomcat.

If you're lucky enough to be using Unix then combine the first two commands onto one line:

$TOMCAT_HOME/bin/startup.sh; tail -f $TOMCAT_HOME/logs/catalina.out

Importing Data Into Apache Solr

When LocalSolr is deployed into Tomcat, the default port is 8080. However, the Apache Solr import tools use a different hardcoded port. This causes me a but of angst until I realized that I could easily copy the SimplePostTool and change the port. So copy $SOLR_HOME/src/java/org/apache/solr/util/SimplePostTool.java, change the port specified on line 46 (see below) and compile it.

public static final String DEFAULT_POST_URL = "http://localhost:8080/solr/update";

I create a data file that looked like this

<add>
 <doc>
   <field name="id">01</field>
   <field name="name">HOUSE01</field>
   <field name="lat">39.36</field>
   <field name="lng">-77.4027</field>
   <field name="text">zxy</field>
 </doc>
 <doc>
   <field name="id">02</field>
   <field name="name">HOUSE02</field>
   <field name="lat">38.36</field>
   <field name="lng">-77.4027</field>
   <field name="text">zxy</field>
 </doc>
</add>

Then I simply executed the SimplePostTool program passing it the name of the data file as the program argument.

Apache Solr Admin Screen

With Tomcat running, you should be able to connect with http://localhost:8080/solr/admin/

Local Solr GIS Query

With Tomcat running, you should be able to execute a GIS-based query by connecting to http://localhost:8080/solr/select?&qt=geo&lat=38.8700&long=-77.4027&q=zxy&radius=1. The q=zxy tells Apache Solr to return all documents. The lat and long parameters indicate the center of the circle to search using decimal degrees. While the radius parameter indicates the radius, in miles, of the circle.

Good Luck!

04/30/2008: Struts Error/Exception: No getter method available for property name for bean under name

Recently I was using Struts v1.x and I needed to display a select list. The information about how to use the html:optionsCollection tag was sketchy. I was able to get the tag working this way:

On the JSP page, I added this inside the html:form tag:

<html:select property="selectedEquipment">
  <html:optionsCollection property="equipment" label="name" value="id"/>
</html:select>

Then I create a Java class called SelectOption like this:

package com.codebits.struts;

public class SelectOption {

    String id;
    String name;

    SelectOption() {
    }

    SelectOption(final String _id, final String _name) {
        setId(_id);
        setName(_name);
    }

    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

Next I created an ActionForm bean:

package com.codebits.struts;

public class AddForm extends ActionForm {

    private Integer           selectedEquipment = null;

    private List              equipment         = new ArrayList();

    public Integer getSelectedEquipment() {
        return selectedEquipment;
    }

    public void setSelectedEquipment(Integer selectedEquipment) {
        this.selectedEquipment = selectedEquipment;
    }

    public List getEquipment() {
        return equipment;
    }

    public void addEquipment(final String equipmentId, final String emtEquipmentId) {
        this.equipment.add(new SelectOption(equipmentId, emtEquipmentId));
    }

    public void setEquipment(List equipment) {
        this.equipment = equipment;
    }

}

And finally, in my action class, I prepopulated the ActionForm bean:

  AddForm addForm = (AddForm)form;
  addForm.addEquipment("1", "AAA");
  addForm.addEquipment("2", "BBBB");