At the end of October, I attended a 3-days Solr training. It was very interesting, in light of the former Elastic Search talk I attended mid-year. As an old Spring Data fan, when I found out Spring Data offered a Solr module, I jumped at the chance to try it.
In this case, I’m well aware that an abstraction layer over Solr doesn’t mean we can easily change the underlying datastore: Solr is designed as an inverted index, while other Spring Data modules are much more specialized (JPA, MongoDB, etc.). However, there are still some advantages of using Spring Data over Solr:
- Quick prototyping, when you don’t need the whole nine yards
- Use Solr when most of the team already knows about Spring Data and not Solr
- Spring integration from the bottom
- Last but not least important of all, you can easily switch between the embedded Solr and the standalone one. Spring Data nicely wraps both the Solrj API and HTTP behind its own API. If you think this is not a relevant use-case as it never will happen, think about integration testing
After having initialized Solr with relevant data, here are steps you have to go through to start developing:
- The initial step is to configure the Spring context with the underlying Solr.
Spring Data Solr requires a bean named
solrTemplate
of typeSolrOperations
. Let use the JavaConfig for this:@Configuration @EnableSolrRepositories("ch.frankel.blog.springdata.solr.repository") public class JavaConfig { @Bean protected HttpSolrServerFactoryBean solrServerFactory() { HttpSolrServerFactoryBean factory = new HttpSolrServerFactoryBean(); factory.setUrl("http://localhost:8983/solr"); return factory; } @Bean public SolrOperations solrTemplate() throws Exception { return new SolrTemplate(solrServerFactory().getObject()); } }
- Create the managed entity.
All fields stored in Solr have to be annotated with
@Field
. If the entity attribute name is different from Solr field name,@Field
accepts a value to override the name:public class Loan { @Field private String id; @Field("gov_type") private String governmentType; // Getters and setters }
- Create the repository interface: either inherit from
SolrRepository<Bean,ID>
orSolrCrudRepository<Bean,ID>
. Note the former only provides thecount()
method.public interface LoanRepository extends SolrRepository<Loan, String> {}
- Add query methods to the former interface:
- Simple query methods benefit from Spring Data parsing:
List<Loan> findByLoanType(String)
will translate into?q=loan_type:…
- More advanced queries (or method whose name do not follow the pattern) should be annotated with
@Query
:@Query("state_name:[* TO *]")
will find all loans which have a value forstate_name
. The annotation can bind as many parameters as necessary, through the use of?x
where x is the parameter index in the method parameters list. - Facets are also handled through the
@Facet
annotation, which takes the field names the facet should use. This changes the method signature, though, to aFacetPage
and it is up to the calling method (probably located the service layer) to handle that.
- Simple query methods benefit from Spring Data parsing:
The final class should look something like that, which is awesome considering all capabilities it provides:
public interface LoanRepository extends SolrRepository<Loan, String> {
List<Loan> findByLoanType(String loan);
List<Loan> findByTitleContaining(String title);
@Query("state_name:[* TO *]")
List<Loan> findLocalized();
@Query("*:*")
@Facet(fields = "loan_type")
FacetPage<Loan> findAllLoanTypes(Pageable page);
}
The entire project source code is available there in IntelliJ/Maven format, complete with tests.
Note that you’ll need a running Solr instance with a specific Solr schema and documents in it.
Both the schema and the document files (federal.xml) are provided, but those are not automated:
you’ll need to overwrite your schema and the document file to the running instance (with the classic java -jar post.jar
command line).