© 2012-2019 The original author(s).

Copies of this document may be made for your own use and for distribution to others, provided that you do not charge any fee for such copies and further provided that each copy contains this Copyright Notice, whether distributed in print or electronically.

Preface

The Spring Data for Apache Solr project applies core Spring concepts to the development of solutions by using the Apache Solr Search Engine. We provide a “template” as a high-level abstraction for storing and querying documents. You may notice similarities to the MongoDB support in the Spring Framework.

Project Metadata

Requirements

Spring Data Solr requires Java 8 runtime and Apache Solr 8.0. We recommend using the latest 8.0.x version.

The following Maven dependency snippet shows how to include Apache Solr in your project:

<dependency>
  <groupId>org.apache.solr</groupId>
  <artifactId>solr-solrj</artifactId>
  <version>${solr.version}</version>
</dependency>

1. New & Noteworthy

1.1. What’s New in Spring Data for Apache Solr 2.1

  • Autoselect Solr core by using SolrTemplate.

  • Assert upwards compatibility with Apache Solr 6 (including 6.3).

  • Support for combined Facet and Highlight queries.

  • Allow reading single-valued multi-value fields into non-collection properties.

  • Use the native SolrJ schema API.

1.2. What’s New in Spring Data for Apache Solr 2.0

  • Upgrade to Apache Solr 5.

  • Support RequestMethod when querying.

1.3. What’s New in Spring Data for Apache Solr 1.5

1.4. What’s New in Spring Data for Apache Solr 1.4

  • Upgraded to recent Solr 4.10.x distribution (requires Java 7).

  • Added support for Real-time Get.

  • Get Field Stats (max, min, sum, count, mean, missing, stddev and distinct calculations).

  • Use @Score to automatically add projection on the document score (See: Special Fields).

Unresolved directive in index.adoc - include::../../../../spring-data-commons/src/main/asciidoc/repositories.adoc[leveloffset=+1]

Reference Documentation

2. Solr Repositories

This chapter covers details of the Solr repository implementation.

2.1. Spring Namespace

The Spring Data Solr module contains a custom namespace that allows definition of repository beans and has elements for instantiating a SolrClient .

Using the repositories element looks up Spring Data repositories as described in [repositories.create-instances].

The following example shows how to set up Solr repositories that use the Spring Data Solr namespace:

Example 1. Setting up Solr repositories using the namespace
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xmlns:solr="http://www.springframework.org/schema/data/solr"
  xsi:schemaLocation="http://www.springframework.org/schema/beans
    https://www.springframework.org/schema/beans/spring-beans.xsd
    http://www.springframework.org/schema/data/solr
    https://www.springframework.org/schema/data/solr/spring-solr.xsd">

  <solr:repositories base-package="com.acme.repositories" />
</beans>

Using the solr-server or embedded-solr-server element registers an instance of SolrClient in the context.

The following examples shows how to set up a Solr client for HTTP:

Example 2. HttpSolrClient using the namespace
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xmlns:solr="http://www.springframework.org/schema/data/solr"
  xsi:schemaLocation="http://www.springframework.org/schema/beans
    https://www.springframework.org/schema/beans/spring-beans.xsd
    http://www.springframework.org/schema/data/solr
    https://www.springframework.org/schema/data/solr/spring-solr.xsd">

  <solr:solr-client id="solrClient" url="https://locahost:8983/solr" />
</beans>

The following example shows how to set up a load-balancing Solr client:

Example 3. LBSolrClient using the namespace
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xmlns:solr="http://www.springframework.org/schema/data/solr"
  xsi:schemaLocation="http://www.springframework.org/schema/beans
    https://www.springframework.org/schema/beans/spring-beans.xsd
    http://www.springframework.org/schema/data/solr
    https://www.springframework.org/schema/data/solr/spring-solr.xsd">

  <solr:solr-client id="solrClient" url="https://locahost:8983/solr,http://localhost:8984/solr" />
</beans>

The following example shows how to set up an embedded Solr server:

Example 4. EmbeddedSolrServer using the namespace
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xmlns:solr="http://www.springframework.org/schema/data/solr"
  xsi:schemaLocation="http://www.springframework.org/schema/beans
    https://www.springframework.org/schema/beans/spring-beans.xsd
    http://www.springframework.org/schema/data/solr
    https://www.springframework.org/schema/data/solr/spring-solr.xsd">

  <solr:embedded-solr-server id="solrClient" solrHome="classpath:com/acme/solr" />
</beans>

2.2. Annotation-based Configuration

The Spring Data Solr repositories support can be activated both through an XML namespace and by using an annotation through Java configuration.

The following example shows how to set up Solr repositories with Java configuration:

Example 5. Spring Data Solr repositories using Java configuration
@Configuration
@EnableSolrRepositories
class ApplicationConfig {

  @Bean
  public SolrClient solrClient() {
    EmbeddedSolrServerFactory factory = new EmbeddedSolrServerFactory("classpath:com/acme/solr");
    return factory.getSolrServer();
  }

  @Bean
  public SolrOperations solrTemplate() {
    return new SolrTemplate(solrClient());
  }
}

The preceding configuration sets up an EmbeddedSolrServer, which is used by the SolrTemplate . Spring Data Solr repositories are activated by using the @EnableSolrRepositories annotation, which essentially carries the same attributes as the XML namespace. If no base package is configured, it use the package in which the configuration class resides.

2.3. Using CDI to Set up Solr Repositores

You can also use CDI to set up the Spring Data Solr repositories, as the following example shows:

Example 6. Spring Data Solr repositories using Java configuration
class SolrTemplateProducer {

  @Produces
  @ApplicationScoped
  public SolrOperations createSolrTemplate() {
    return new SolrTemplate(new EmbeddedSolrServerFactory("classpath:com/acme/solr"));
  }
}

class ProductService {

  private ProductRepository repository;

  public Page<Product> findAvailableProductsByName(String name, Pageable pageable) {
    return repository.findByAvailableTrueAndNameStartingWith(name, pageable);
  }

  @Inject
  public void setRepository(ProductRepository repository) {
    this.repository = repository;
  }
}

2.4. Transaction Support

Solr support for transactions on the server level means that create, update, and delete actions since the last commit, optimization, or rollback are queued on the server and committed, optimized, or rolled back as a group. Spring Data Solr repositories participate in Spring Managed Transactions and commit or rollback changes on complete.

The following example shows how to use the @Transactional annotation to define a transaction (a save in this case):

@Transactional
public Product save(Product product) {
  Product savedProduct = jpaRepository.save(product);
  solrRepository.save(savedProduct);
  return savedProduct;
}

2.5. Query Methods

This section covers creating queries by using methods within Java classes.

2.5.1. Query Lookup Strategies

The Solr module supports defining a query manually as String or having it be derived from the method name.

There is no QueryDSL support at this time.
Declared Queries

Deriving the query from the method name is not always sufficient and may result in unreadable method names. In this case you can either use Solr named queries (see “Using Named Queries”) or use the @Query annotation (see “Using the @Query Annotation”).

2.5.2. Query Creation

Generally, the query creation mechanism for Solr works as described in [repositories.query-methods] . The following example shows what a Solr query method:

Example 7. Query creation from method names
public interface ProductRepository extends Repository<Product, String> {
  List<Product> findByNameAndPopularity(String name, Integer popularity);
}

The preceding example translates into the following Solr query:

q=name:?0 AND popularity:?1

The following table describes the supported keywords for Solr:

Table 1. Supported keywords inside method names
Keyword Sample Solr Query String

And

findByNameAndPopularity

q=name:?0 AND popularity:?1

Or

findByNameOrPopularity

q=name:?0 OR popularity:?1

Is

findByName

q=name:?0

Not

findByNameNot

q=-name:?0

IsNull

findByNameIsNull

q=-name:[* TO *]

IsNotNull

findByNameIsNotNull

q=name:[* TO *]

Between

findByPopularityBetween

q=popularity:[?0 TO ?1]

LessThan

findByPopularityLessThan

q=popularity:[* TO ?0}

LessThanEqual

findByPopularityLessThanEqual

q=popularity:[* TO ?0]

GreaterThan

findByPopularityGreaterThan

q=popularity:{?0 TO *]

GreaterThanEqual

findByPopularityGreaterThanEqual

q=popularity:[?0 TO *]

Before

findByLastModifiedBefore

q=last_modified:[* TO ?0}

After

findByLastModifiedAfter

q=last_modified:{?0 TO *]

Like

findByNameLike

q=name:?0*

NotLike

findByNameNotLike

q=-name:?0*

StartingWith

findByNameStartingWith

q=name:?0*

EndingWith

findByNameEndingWith

q=name:*?0

Containing

findByNameContaining

q=name:*?0*

Matches

findByNameMatches

q=name:?0

In

findByNameIn(Collection<String> names)

q=name:(?0…​ )

NotIn

findByNameNotIn(Collection<String> names)

q=-name:(?0…​ )

Within

findByStoreWithin(Point, Distance)

q={!geofilt pt=?0.latitude,?0.longitude sfield=store d=?1}

Near

findByStoreNear(Point, Distance)

q={!bbox pt=?0.latitude,?0.longitude sfield=store d=?1}

Near

findByStoreNear(Box)

q=store[?0.start.latitude,?0.start.longitude TO ?0.end.latitude,?0.end.longitude]

True

findByAvailableTrue

q=inStock:true

False

findByAvailableFalse

q=inStock:false

OrderBy

findByAvailableTrueOrderByNameDesc

q=inStock:true&sort=name desc

Collections types can be used along with 'Like', 'NotLike', 'StartingWith', 'EndingWith' and 'Containing'.
Page<Product> findByNameLike(Collection<String> name);

2.5.3. Using the @Query Annotation

Using named queries (see “Using Named Queries”) to declare queries for entities is a valid approach and works fine for a small number of queries. As the queries themselves are tied to the Java method that executes them, you actually can bind them directly by using the Spring Data Solr @Query annotation. The following example uses the @Query annotation to declare a query:

Example 8. Declare query at the method using the @Query annotation.
public interface ProductRepository extends SolrRepository<Product, String> {
  @Query("inStock:?0")
  List<Product> findByAvailable(Boolean available);
}

2.5.4. Using Named Queries

Named queries can be kept in a properties file and wired to the corresponding method. You should keep in mind the naming convention described in “[repositories.query-methods.query-lookup-strategies]” or use @Query. The following example shows how to declare name queries in a properties file:

Example 9. Declare named query in a properties file
Product.findByNamedQuery=popularity:?0
Product.findByName=name:?0

The following example uses one of the named queries (findByName) declared in the preceding example:

public interface ProductRepository extends SolrCrudRepository<Product, String> {

  List<Product> findByNamedQuery(Integer popularity);

  @Query(name = "Product.findByName")
  List<Product> findByAnnotatedNamedQuery(String name);

}

2.6. Document Mapping

Though there is already support for Entity Mapping within SolrJ, Spring Data Solr ships with its own mapping mechanism (described in the following section).

DocumentObjectBinder has superior performance. Therefore, we recommend using it if you have no need for customer mapping. You can switch to DocumentObjectBinder by registering SolrJConverter within SolrTemplate.

Unresolved directive in reference/data-solr.adoc - include::../../../../../spring-data-commons/src/main/asciidoc/object-mapping.adoc[leveloffset=+2]

2.6.1. MappingSolrConverter

MappingSolrConverter lets you register custom converters for your SolrDocument and SolrInputDocument as well as for other types nested within your beans. The converter is not 100% compatible with DocumentObjectBinder, and @Indexed has to be added with readonly=true to ignore fields from being written to Solr. The following example maps a number of fields within a document:

Example 10. Sample Document Mapping
public class Product {
  @Field
  private String simpleProperty;

  @Field("somePropertyName")
  private String namedPropery;

  @Field
  private List<String> listOfValues;

  @Indexed(readonly = true)
  @Field("property_*")
  private List<String> ignoredFromWriting;

  @Field("mappedField_*")
  private Map<String, List<String>> mappedFieldValues;

  @Dynamic
  @Field("dynamicMappedField_*")
  private Map<String, String> dynamicMappedFieldValues;

  @Field
  private GeoLocation location;

}

The following table describes the properties you can map with MappingSolrConverter:

Property Write Mapping

simpleProperty

<field name="simpleProperty">value</field>

namedPropery

<field name="somePropertyName">value</field>

listOfValues

<field name="listOfValues">value 1</field> <field name="listOfValues">value 2</field> <field name="listOfValues">value 3</field>

ignoredFromWriting

//not written to document

mappedFieldValues

<field name="mapentry[0].key">mapentry[0].value[0]</field> <field name="mapentry[0].key">mapentry[0].value[1]</field> <field name="mapentry[1].key">mapentry[1].value[0]</field>

dynamicMappedFieldValues

<field name="'dynamicMappedField_' + mapentry[0].key">mapentry[0].value[0]</field> <field name="'dynamicMappedField_' + mapentry[0].key">mapentry[0].value[1]</field> <field name="'dynamicMappedField_' + mapentry[1].key">mapentry[1].value[0]</field>

location

<field name="location">48.362893,14.534437</field>

You can register a custom converter by adding CustomConversions to SolrTemplate and initializing it with your own Converter implementation, as the following example shows:

<bean id="solrConverter" class="org.springframework.data.solr.core.convert.MappingSolrConverter">
	<constructor-arg>
		<bean class="org.springframework.data.solr.core.mapping.SimpleSolrMappingContext" />
	</constructor-arg>
	<property name="customConversions" ref="customConversions" />
</bean>

<bean id="customConversions" class="org.springframework.data.solr.core.convert.SolrCustomConversions">
	<constructor-arg>
		<list>
			<bean class="com.acme.MyBeanToSolrInputDocumentConverter" />
		</list>
	</constructor-arg>
</bean>

<bean id="solrTemplate" class="org.springframework.data.solr.core.SolrTemplate">
	<constructor-arg ref="solrClient" />
	<property name="solrConverter" ref="solrConverter" />
</bean>

3. Miscellaneous Solr Operation Support

This chapter covers additional support for Solr operations (such as faceting) that cannot be directly accessed via the repository interface. It is recommended to add those operations as custom implementation as described in [repositories.custom-implementations] .

3.1. Collection / Core Name

Using the @SolrDocument annotation it is possible to customize the used collection name by either giving it a static value or use SpEL for dynamic evaluation.

@SolrDocument(collection = "techproducts")
class StaticCollectionName { ... }

@SolrDocument(collection = "#{@someBean.getCollectionName()}")
class DynamicCollectionName { ... }
The the type annotated with @SolrDocument is available via the targetType variable within the expression.

3.2. Partial Updates

PartialUpdates can be done using PartialUpdate which implements Update.

PartialUpdate update = new PartialUpdate("id", "123");
update.add("name", "updated-name");
solrTemplate.saveBean("collection-1", update);

3.3. Projection

Projections can be applied via @Query using the fields value.

@Query(fields = { "name", "id" })
List<ProductBean> findByNameStartingWith(String name);

3.4. Faceting

Faceting cannot be directly applied by using the SolrRepository, but the SolrTemplate has support for this feature. The following example shows a facet query:

FacetQuery query = new SimpleFacetQuery(new Criteria(Criteria.WILDCARD).expression(Criteria.WILDCARD))
  .setFacetOptions(new FacetOptions().addFacetOnField("name").setFacetLimit(5));
FacetPage<Product> page = solrTemplate.queryForFacetPage("collection-1", query, Product.class);

Facets on fields or queries can also be defined by using @Facet. Keep in mind that the result is a FacetPage.

Using @Facet lets you define place holders that use your input parameter as a value.

The following example uses the @Facet annotation to define a facet query:

@Query(value = "*:*")
@Facet(fields = { "name" }, limit = 5)
FacetPage<Product> findAllFacetOnName(Pageable page);

The following example shows another facet query, with a prefix:

@Query(value = "popularity:?0")
@Facet(fields = { "name" }, limit = 5, prefix="?1")
FacetPage<Product> findByPopularityFacetOnName(int popularity, String prefix, Pageable page);

Solr allows definition of facet parameters on a per field basis. In order to add special facet options to defined fields, use FieldWithFacetParameters, as the following example shows:

// produces: f.name.facet.prefix=spring
FacetOptions options = new FacetOptions();
options.addFacetOnField(new FieldWithFacetParameters("name").setPrefix("spring"));

3.4.1. Range Faceting

You can create range faceting queries by configuring required ranges on FacetOptions. You can request ranges by creating a FacetOptions instance, setting the options to a FacetQuery, and querying for a facet page through SolrTemplate, as follows.

FacetOptions facetOptions = new FacetOptions()
  .addFacetByRange(
     new FieldWithNumericRangeParameters("price", 5, 20, 5)
       .setHardEnd(true)
       .setInclude(FacetRangeInclude.ALL)
  )
  .addFacetByRange(
    new FieldWithDateRangeParameters("release", new Date(1420070400), new Date(946684800), "+1YEAR")
      .setInclude(FacetRangeInclude.ALL)
      .setOther(FacetRangeOther.BEFORE)
  );
facetOptions.setFacetMinCount(0);

Criteria criteria = new SimpleStringCriteria("*:*");
SimpleFacetQuery facetQuery = new SimpleFacetQuery(criteria).setFacetOptions(facetOptions);
FacetPage<ExampleSolrBean> statResultPage = solrTemplate.queryForFacetPage("collection-1", facetQuery, ExampleSolrBean.class);

There are two implementations of fields for facet range requests:

  • Numeric Facet Range: Used to perform range faceting over numeric fields. To request range faceting, you can use an instance of the org.springframework.data.solr.core.query.FacetOptions.FieldWithNumericRangeParameters class. Its instantiation requires a field name, a start value (number), an end value (number), and a gap (number);

  • Date Facet Range: Used to perform range faceting over date fields. To request range faceting, you can use an instance of the org.springframework.data.solr.core.query.FacetOptions.FieldWithDateRangeParameters class. Its instantiation requires a field name, a start value (date), an end value (date), and a gap (string). You can define the gap for this kind of field by using org.apache.solr.util.DateMathParser (for example, +6MONTHS+3DAYS/DAY means six months and three days in the future, rounded down to the nearest day).

Additionally, the following properties can be configured for a field with range parameters (org.springframework.data.solr.core.query.FacetOptions.FieldWithRangeParameters):

  • Hard End: setHardEnd(Boolean) defines whether the last range should be abruptly ended even if the end does not satisfy (start - end) % gap = 0.

  • Include: setInclude(org.apache.solr.common.params.FacetParams.FacetRangeInclude) defines how boundaries (lower and upper) should be handled (exclusive or inclusive) on range facet requests.

  • Other: setOther(org.apache.solr.common.params.FacetParams.FacetRangeOther) defines the additional (other) counts for the range facet (such as count of documents that are before the start of the range facet, after the end of the range facet, or even between the start and the end).

3.4.2. Pivot Faceting

Pivot faceting (decision tree) is also supported and can be queried by using @Facet annotation, as follows:

public interface {

	@Facet(pivots = @Pivot({ "category", "dimension" }, pivotMinCount = 0))
	FacetPage<Product> findByTitle(String title, Pageable page);

	@Facet(pivots = @Pivot({ "category", "dimension" }))
	FacetPage<Product> findByDescription(String description, Pageable page);

}

Alternatively, it can be queried by using SolrTemplate, as follows:

FacetQuery facetQuery = new SimpleFacetQuery(new SimpleStringCriteria("title:foo"));
FacetOptions facetOptions = new FacetOptions();
facetOptions.setFacetMinCount(0);
facetOptions.addFacetOnPivot("category","dimension");
facetQuery.setFacetOptions(facetOptions);
FacetPage<Product> facetResult = solrTemplate.queryForFacetPage("collection-1", facetQuery, Product.class);

In order to retrieve the pivot results, use the getPivot method, as follows:

List<FacetPivotFieldEntry> pivot = facetResult.getPivot(new SimplePivotField("categories","available"));

3.5. Terms

A terms vector cannot directly be used within SolrRepository but can be applied through SolrTemplate. Keep in mind that the result is a TermsPage. The following example shows how to create a terms query:

TermsQuery query = SimpleTermsQuery.queryBuilder().fields("name").build();
TermsPage page = solrTemplate.queryForTermsPage("collection-1", query);

3.6. Result Grouping and Field Collapsing

Result grouping cannot directly be used within SolrRepository but can be applied through SolrTemplate. Keep in mind that the result is a GroupPage. The following example shows how to create a result group:

Field field = new SimpleField("popularity");
Function func = ExistsFunction.exists("description");
Query query = new SimpleQuery("inStock:true");

SimpleQuery groupQuery = new SimpleQuery(new SimpleStringCriteria("*:*"));
GroupOptions groupOptions = new GroupOptions()
	.addGroupByField(field)
	.addGroupByFunction(func)
	.addGroupByQuery(query);
groupQuery.setGroupOptions(groupOptions);

GroupPage<Product> page = solrTemplate.queryForGroupPage("collection-1", query, Product.class);

GroupResult<Product> fieldGroup = page.getGroupResult(field);
GroupResult<Product> funcGroup = page.getGroupResult(func);
GroupResult<Product> queryGroup = page.getGroupResult(query);

3.7. Field Stats

Field stats are used to retrieve statistics (max, min, sum, count, mean, missing, stddev, and distinct calculations) of given fields from Solr. You can provide StatsOptions to your query and read the FieldStatsResult from the returned StatsPage. You could do so, for instance, by using SolrTemplate, as follows:

// simple field stats
StatsOptions statsOptions = new StatsOptions().addField("price");

// query
SimpleQuery statsQuery = new SimpleQuery("*:*");
statsQuery.setStatsOptions(statsOptions);
StatsPage<Product> statsPage = solrTemplate.queryForStatsPage("collection-1", statsQuery, Product.class);

// retrieving stats info
FieldStatsResult priceStatResult = statResultPage.getFieldStatsResult("price");
Object max = priceStatResult.getMax();
Long missing = priceStatResult.getMissing();

You could achieve the same result by annotating the repository method with @Stats, as follows:

@Query("name:?0")
@Stats(value = { "price" })
StatsPage<Product> findByName(String name, Pageable page);

Distinct calculation and faceting are also supported:

// for distinct calculation
StatsOptions statsOptions = new StatsOptions()
    .addField("category")
    // for distinct calculation
    .setCalcDistinct(true)
    // for faceting
    .addFacet("availability");

// query
SimpleQuery statsQuery = new SimpleQuery("*:*");
statsQuery.setStatsOptions(statsOptions);
StatsPage<Product> statsPage = solrTemplate.queryForStatsPage("collection-1", statsQuery, Product.class);

// field stats
FieldStatsResult categoryStatResult = statResultPage.getFieldStatsResult("category");

// retrieving distinct
List<Object> categoryValues = priceStatResult.getDistinctValues();
Long distinctCount = categoryStatResult.getDistinctCount();

// retrieving faceting
Map<String, StatsResult> availabilityFacetResult = categoryStatResult.getFacetStatsResult("availability");
Long availableCount = availabilityFacetResult.get("true").getCount();

The annotated (and consequently much shorter) version of the preceding example follows:

@Query("name:?0")
@Stats(value = "category", facets = { "availability" }, calcDistinct = true)
StatsPage<Product> findByName(String name);

In order to perform a selective faceting or selective distinct calculation, you can use @SelectiveStats, as follows:

// selective distinct faceting
...
Field facetField = getFacetField();
StatsOptions statsOptions = new StatsOptions()
    .addField("price")
    .addField("category").addSelectiveFacet("name").addSelectiveFacet(facetField);
...
// or annotating repository method as follows
...
@Stats(value = "price", selective = @SelectiveStats(field = "category", facets = { "name", "available" }))
...

// selective distinct calculation
...
StatsOptions statsOptions = new StatsOptions()
    .addField("price")
    .addField("category").setSelectiveCalcDistinct(true);
...
// or annotating repository method as follows
...
@Stats(value = "price", selective = @SelectiveStats(field = "category", calcDistinct = true))
...

3.8. Filter Query

Filter Queries improve query speed and do not influence the document score. We recommend implementing geospatial search as a filter query.

In Solr, unless otherwise specified, all units of distance are kilometers and points are in degrees of latitude and longitude.

The following example shows a filter query for a geographical point (in Austria, in this case):

Query query = new SimpleQuery(new Criteria("category").is("supercalifragilisticexpialidocious"));
FilterQuery fq = new SimpleFilterQuery(new Criteria("store")
  .near(new Point(48.305478, 14.286699), new Distance(5)));
query.addFilterQuery(fq);

You can also define simple filter queries by using @Query.

Using @Query lets you define place holders that use your input parameter as a value.

The following example shows a query with placeholders (:):

@Query(value = "*:*", filters = { "inStock:true", "popularity:[* TO 3]" })
List<Product> findAllFilterAvailableTrueAndPopularityLessThanEqual3();

3.9. Time Allowed for a Search

You can set the time allowed for a search to finish. This value only applies to the search and not to requests in general. Time is in milliseconds. Values less than or equal to zero imply no time restriction. Partial results may be returned, if there are any. The following example restricts the time for a search to 100 milliseconds:

Query query = new SimpleQuery(new SimpleStringCriteria("field_1:value_1"));
// Allowing maximum of 100ms for this search
query.setTimeAllowed(100);

3.10. Boosting the Document Score

You can boost the document score for matching criteria to influence the result order. You can do so either by setting boost on Criteria or by using @Boost for derived queries. The following example boosts the name parameter of the findByNameOrDescription query:

Page<Product> findByNameOrDescription(@Boost(2) String name, String description);

3.10.1. Index Time Boosts

Both document-based and field-based index time boosting have been removed from Apache Solr 7 and, therefore, from Spring Data for Apache Solr 4.x.

3.11. Selecting the Request Handler

You can select the request handler through the qt Parameter directly in Query or by adding @Query to your method signature. The following example does so by adding @Query:

@Query(requestHandler = "/instock")
Page<Product> findByNameOrDescription(String name, String description);

3.12. Using Joins

You can use joins within one Solr core by defining a Join attribute of a Query.

Join is not available prior to Solr 4.x.

The following example shows how to use a join:

SimpleQuery query = new SimpleQuery(new SimpleStringCriteria("text:ipod"));
query.setJoin(Join.from("manu_id_s").to("id"));

3.13. Highlighting

To highlight matches in search result, you can add HighlightOptions to the SimpleHighlightQuery. Providing HighlightOptions without any further attributes applies highlighting on all fields within a SolrDocument.

You can set field-specific highlight parameters by adding FieldWithHighlightParameters to HighlightOptions.

The following example sets highlighting for all fields in the query:

SimpleHighlightQuery query = new SimpleHighlightQuery(new SimpleStringCriteria("name:with"));
query.setHighlightOptions(new HighlightOptions());
HighlightPage<Product> page = solrTemplate.queryForHighlightPage("collection-1", query, Product.class);

Not all parameters are available through setters and getters but can be added directly.

The following example sets highlighting on two fields:

SimpleHighlightQuery query = new SimpleHighlightQuery(new SimpleStringCriteria("name:with"));
query.setHighlightOptions(new HighlightOptions().addHighlightParameter("hl.bs.country", "at"));

To apply Highlighting to derived queries, you can use @Highlight. If no fields are defined, highlighting is applied on all fields.

@Highlight(prefix = "<b>", postfix = "</b>")
HighlightPage<Product> findByName(String name, Pageable page);

3.14. Spellchecking

Spellchecking offers search term suggestions based on the actual query. See the Solr Reference for more details.

3.14.1. Spellcheck Options

Spellcheck query parameters are added to a request when SpellcheckOptions has been set, as the following example shows:

SimpleQuery q = new SimpleQuery("name:gren");
q.setSpellcheckOptions(SpellcheckOptions.spellcheck()               (1)
  .dictionaries("dict1", "dict2")                                   (2)
  .count(5)                                                         (3)
  .extendedResults());                                              (4)
q.setRequestHandler("/spell");                                      (5)

SpellcheckedPage<Product> found = template.query(q, Product.class); (6)
1 Enable spellcheck by setting SpellcheckOptions. Sets the spellcheck=on request parameter.
2 Set up the dictionaries to use for lookup.
3 Set the maximum number of suggestions to return.
4 Enable extended results, including term frequency and others.
5 Set the request handler, which must be capable of processing suggestions.
6 Run the query.

3.14.2. @Spellcheck

The @Spellcheck annotation allows usage of the spellcheck feature on Repository level. The following example shows how to use it:

public interface ProductRepository extends Repository<Product, String> {

  @Query(requestHandler = "/spell")
  @Spellcheck(dictionaries = { "dict1", "dic2" }, count=5, extendedResults = true)
  SpellcheckedPage<Product> findByName(String name, Pageable page);

}

3.15. Using Functions

Solr supports several functional expressions within queries and includes a number of functions. You can add custom functions by implementing Function. The following table lists which functions are supported:

Table 2. Functions
Class Solr Function

CurrencyFunction

currency(field_name,[CODE])

DefaultValueFunction

def(field|function,defaultValue)

DistanceFunction

dist(power, pointA, pointB)

DivideFunction

div(x,y)

ExistsFunction

exists(field|function)

GeoDistanceFunction

geodist(sfield, latitude, longitude)

GeoHashFunction

geohash(latitude, longitude)

IfFunction

if(value|field|function,trueValue,falseValue)

MaxFunction

max(field|function,value)

NotFunction

not(field|function)

ProductFunction

product(x,y,…​)

QueryFunction

query(x)

TermFrequencyFunction

termfreq(field,term)

The following example uses a QueryFunction:

SimpleQuery query = new SimpleQuery(new SimpleStringCriteria("text:ipod"));
query.addFilterQuery(new FilterQuery(Criteria.where(QueryFunction.query("name:sol*"))));

3.16. Real-time Get

Real-time get allows retrieval of the latest version of any document by using a unique key, without the need to reopen searchers.

Real-time get relies on the update log feature.

The following example shows a real-time get:

Example 11. Real-time get
Optional<Product> product = solrTemplate.getById("collection-1", "123", Product.class);

You can retrieve multiple documents by providing a collection of ids, as follows:

Example 12. Realtime multi-get
Collection<String> ids = Arrays.asList("123", "134");
Collection<Product> products = solrTemplate.getByIds("collection-1", ids, Product.class);

3.17. Special Fields

Solr includes a number of special fields, including a score field.

3.17.1. @Score

In order to load score information of a query result, you can add a field annotated with the @Score annotation, indicating the property holds the document’s score.

The score property needs to be numerical and can only appear once per document.

The following example shows a document with a score field:

public class MyEntity {

    @Id
    private String id;

    @Score
    private Float score;

    // setters and getters ...

}

3.18. Nested Documents

Nested documents allow for documents inside of other documents in a parent-child relationship.

The nested documents need to be indexed along with the parent one and cannot be updated individually. However, nested documents appear as individual documents in the index. Resolving the parent-child relation is done at query time.

To indicate that a property should be treated as a nested object, it has to be annotated with either @o.a.s.c.solrj.beans.Field(child=true) or @o.s.d.s.core.mapping.ChildDocument. The following uses the @ChildDocument annotation:

public class Book {

    @Id String id;
    @Indexed("type_s") String type;
    @Indexed("title_t") String title;
    @Indexed("author_s") String author;
    @Indexed("publisher_s") String publisher;

    @ChildDocument List<Review> reviews;      (1)

    // setters and getters ...

}

public class Review {

    @Id String id;                            (2)
    @Indexed("type_s") String type;
    @Indexed("review_dt") Date date;
    @Indexed("stars_i") int stars;
    @Indexed("author_s") String author;
    @Indexed("comment_t") String comment;

}
1 Multiple child documents can be associated with a parent one or use the domain type to store a single relationship.
2 Note that the nested document also needs to have a unique id assigned.

Assuming Book#type is book, and Review#type resolves to review, retrieving Book with its child relations reviews can be done by altering the fl query parameter, as the following example shows:

Query query = new SimpleQuery(where("id").is("theWayOfKings"));
query.addProjectionOnField(new SimpleField("*"));
query.addProjectionOnField(new SimpleField("[child parentFilter=type_s:book]")); (1)

return solrTemplate.queryForObject("books", query, Book.class);
1 The parent filter always defines the complete set of parent documents in the index, not the one for a single document.

Appendix

Unresolved directive in index.adoc - include::../../../../spring-data-commons/src/main/asciidoc/repository-namespace-reference.adoc[leveloffset=+1] Unresolved directive in index.adoc - include::../../../../spring-data-commons/src/main/asciidoc/repository-populator-namespace-reference.adoc[leveloffset=+1] Unresolved directive in index.adoc - include::../../../../spring-data-commons/src/main/asciidoc/repository-query-keywords-reference.adoc[leveloffset=+1] Unresolved directive in index.adoc - include::../../../../spring-data-commons/src/main/asciidoc/repository-query-return-types-reference.adoc[v]