Chapter 16. Apache Solr Add-On

The Apache Solr add-on provides integration between the Roo generated domain model and the Apache Solr search platform. If you haven't heard of the open source Solr system, here's a quick description from the project web site:

Solr is the popular, blazing fast open source enterprise search platform from the Apache Lucene project. Its major features include powerful full-text search, hit highlighting, faceted search, dynamic clustering, database integration, and rich document (e.g., Word, PDF) handling. Solr is highly scalable, providing distributed search and index replication, and it powers the search and navigation features of many of the world's largest internet sites.

Solr is written in Java and runs as a standalone full-text search server within a servlet container such as Tomcat. Solr uses the Lucene Java search library at its core for full-text indexing and search, and has REST-like HTTP/XML and JSON APIs that make it easy to use from virtually any programming language. Solr's powerful external configuration allows it to be tailored to almost any type of application without Java coding, and it has an extensive plugin architecture when more advanced customization is required.

16.1. Solr Server Installation

The addon requires a running instance of the Apache Solr server. To install a Solr server just follow these four easy steps:

  1. Download the server: http://www.apache.org/dyn/closer.cgi/lucene/solr/

  2. Unzip (untar) the download: tar xf apache-solr-1.4.0.tgz

  3. Change into the solr example directory: cd apache-solr-1.4.0/example

  4. Start the Solr server: java -jar start.jar

  5. Verify Solr is running correctly: http://localhost:8983/solr/admin/

16.2. Solr Add-On Commands

Once the server is running you can setup the Solr integration for your project using the following Roo commands:

  1. roo> solr setup

    This command installs the SolrJ driver dependency into the project pom.xml and registers your Solr server in application context so it can be injected whereever you need it in your project.

  2. ~.Person roo> solr add 

    This command allows you to mark an individual entity for automatic Solr indexing. The @RooSolrSearchable annotation will be added to the target entity (Person). Furthermore, the following ITD is generated:

    privileged aspect Person_Roo_SolrSearch {
        
        @Autowired
        transient SolrServer Person.solrServer;
        
        public static QueryResponse Person.search(String queryString) {
            return search(new SolrQuery("person.solrsummary_t:" + queryString.toLowerCase()));
        }
        
        public static QueryResponse Person.search(SolrQuery query) {
            try {
                QueryResponse rsp = solrServer().query(query);
                return rsp;
            } catch (Exception e) {
                e.printStackTrace();
            }
            return new QueryResponse();
        }
        
        public static void Person.indexPerson(Person person) {
            List<Person> people = new ArrayList<Person>();
            people.add(person);
            indexPeople(people);
        }
        
        public static void Person.indexPeople(Collection<Person> people) {
            List<SolrInputDocument> documents = new ArrayList<SolrInputDocument>();
            for (Person person : people) {
                SolrInputDocument sid = new SolrInputDocument();
                sid.addField("id", "person." + person.getId());
                sid.addField("person.birthday_dt", person.getBirthDay());
                sid.addField("person.id_l", person.getId());
                sid.addField("person.name_s", person.getName());
                //add summary field to allow searching documents for objects of this type
                sid.addField("person.solrsummary_t", new StringBuilder().append(
                              person.getBirthDay()).append(" ").append(
                              person.getId()).append(" ").append(person.getName()));
                documents.add(sid);
            }
            try {
                SolrServer solrServer = solrServer();
                solrServer.add(documents);
                solrServer.commit();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        
        public static void Person.deleteIndex(Person person) {
            SolrServer solrServer = solrServer();
            try {
                solrServer.deleteById("person." + person.getId());
                solrServer.commit();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        
        @PostUpdate
        @PostPersist
        private void Person.postPersistOrUpdate() {
            indexPerson(this);
        }
        
        @PreRemove
        private void Person.preRemove() {
            deleteIndex(this);
        }
        
        public static final SolrServer Person.solrServer() {
            SolrServer _solrServer = new Person().solrServer;
            if (_solrServer == null) throw new IllegalStateException("Entity manager \ 
                                        has not been injected (is the Spring Aspects JAR \
                                        configured as an AJC/AJDT aspects library?)");
            return _solrServer;
        }
        
    }
    

    The ITD introduces two search methods; one for conducting simple searches against Solr documents for Person, and another one which works with a preconfigured SolrQuery object. The SolrQuery object allows you to leverage all functionalities of the Solr search server (like faceting, sorting, term highliting, pagination, etc).

    The indexPerson(..) and indexPeople(..) methods allow you to add new person instances or even collections of persons to the Solr index. The deleteIndex(..) method allows you to remove a person from the Solr index.

    All indexing, and delete operations are executed in s separate thread and will therefore not impact the performance of your Web application (this is currently achieved through the SolrSearchAsyncTaskExecutor.aj aspect).

    Furthermore, to trigger automatic indexing of new person instances (or updated person instances) this itd registers the postPersistOrUpdate() method which hooks into the JPA lifecycle through the JPA @PostUpdate and @PostPersist annotations. Similarly, the preRemove() method hooks in the JPA lifecylce through the @PreRemove annotation.

  3. roo> solr all

    This command will mark all entities in the project for automatic Solr indexing. The generated functionality is the same as shown above.

16.3. The @RooSolrSearchable Annotation

The @RooSolrSearchable annotation allows you to change all method names through their respective attributes in the annotation. Marking a method name with an empty String will instruct the Roo Solr add-on to not generate that method (i.e. @RooSolrSearchable(preRemoveMethod="")).

By default all fields in a domain entity are indexed as dynamic fields (defined in the default schema.xml which Solr ships with). The default format of a field name is as follows:

<simple-entity-name>.<field-name>_<field-type>
person.birthday_dt

This ensures each field is uniquely mapped across your domain model by prepending the entity name followed by the field name and field type (which is used to trigger the dynamic field mapping). You can change field names by adding a @Field annotation to a field in the domain object (i.e. Person) which contains your own field (you need to provide a field definition in the Solr schema for it as well):

@Field("my:field:name:birthday")
@Temporal(TemporalType.TIMESTAMP)
@DateTimeFormat(style = "M-")
private Date birthDay;

To index existing DB entity tables each entity exposes a convenience method (example for Person entity):

Person.indexPeople(Person.findAllPeople()); 

The URL of the solr server location can be changed in the project src/main/resources/META-INF/spring/solr.properties config file.

Front-end (controller and MVC/JSP views) are currently a work-in-progress. However, the following Ajax Library offers a neat front-end for those who want to take this a step further: http://github.com/evolvingweb/ajax-solr It is planned to provide a out of the box integration with the Ajax-Solr front-end through this addon in the medium term.