[[bootstrap:lucene]]
= Apache Lucene Integration

{x-data-store-website}[{data-store-name}] integrates with https://lucene.apache.org/[Apache Lucene] to let you
index and search on data stored in {data-store-name} by using Lucene queries. Search-based queries also include
the ability to page through query results.

Additionally, {sdg-name} adds support for query projections based on the Spring Data Commons projection infrastructure.
This feature lets the query results be projected into first-class application domain types as needed by the application.

A Lucene `Index` must be created before any Lucene search-based query can be run. A `LuceneIndex`
can be created in Spring (Data for {data-store-name}) XML config as follows:

[source,xml]
----
<gfe:lucene-index id="IndexOne" fields="fieldOne, fieldTwo" region-path="/Example"/>
----

Additionally, Apache Lucene allows the specification of
https://lucene.apache.org/core/6_5_0/core/org/apache/lucene/analysis/Analyzer.html[analyzers]
per field and can be configured as shown in the following example:

[source,xml]
----
<gfe:lucene-index id="IndexTwo" lucene-service-ref="luceneService" region-path="/AnotherExample">
    <gfe:field-analyzers>
        <map>
            <entry key="fieldOne">
                <bean class="example.AnalyzerOne"/>
             </entry>
            <entry key="fieldTwo">
                <bean class="example.AnalyzerTwo"/>
             </entry>
        </map>
    </gfe:field-analyzers>
</gfe:lucene-index>
----

The `Map` can be specified as a top-level bean definition and referenced by using the `ref` attribute
in the nested `<gfe:field-analyzers>` element, as follows:
`<gfe-field-analyzers ref="refToTopLevelMapBeanDefinition"/>`.

{sdg-name}'s `LuceneIndexFactoryBean` API and {sdg-acronym}'s XML namespace also lets a
{x-data-store-javadoc}/org/apache/geode/cache/lucene/LuceneSerializer.html[`org.apache.geode.cache.lucene.LuceneSerializer`]
be specified when you create the `LuceneIndex`. The `LuceneSerializer` lets you configure the way objects are converted
to Lucene documents for the index when the object is indexed.

The following example shows how to add an `LuceneSerializer` to the `LuceneIndex`:

[source,xml]
----
<bean id="MyLuceneSerializer" class="example.CustomLuceneSerializer"/>

<gfe:lucene-index id="IndexThree" lucene-service-ref="luceneService" region-path="/YetAnotherExample">
    <gfe:lucene-serializer ref="MyLuceneSerializer">
</gfe:lucene-index>
----

You can specify the `LuceneSerializer` as an anonymous, nested bean definition as well, as follows:

[source,xml]
----
<gfe:lucene-index id="IndexThree" lucene-service-ref="luceneService" region-path="/YetAnotherExample">
    <gfe:lucene-serializer>
        <bean class="example.CustomLuceneSerializer"/>
    </gfe:lucene-serializer>
</gfe:lucene-index>
----

Alternatively, you can declare or define a `LuceneIndex` in Spring Java config, inside a `@Configuration` class,
as the following example shows:

[source,java]
----
@Bean(name = "Books")
@DependsOn("bookTitleIndex")
PartitionedRegionFactoryBean<Long, Book> booksRegion(GemFireCache gemfireCache) {

    PartitionedRegionFactoryBean<Long, Book> peopleRegion =
        new PartitionedRegionFactoryBean<>();

    peopleRegion.setCache(gemfireCache);
    peopleRegion.setClose(false);
    peopleRegion.setPersistent(false);

    return peopleRegion;
}

@Bean
LuceneIndexFactoryBean bookTitleIndex(GemFireCache gemFireCache,
        LuceneSerializer luceneSerializer) {

    LuceneIndexFactoryBean luceneIndex = new LuceneIndexFactoryBean();

    luceneIndex.setCache(gemFireCache);
    luceneIndex.setFields("title");
    luceneIndex.setLuceneSerializer(luceneSerializer);
    luceneIndex.setRegionPath("/Books");

    return luceneIndex;
}

@Bean
CustomLuceneSerializer myLuceneSerialier() {
    return new CustomeLuceneSerializer();
}
----

There are a few limitations of {data-store-name}'s, Apache Lucene integration and support.

First, a `LuceneIndex` can only be created on a {data-store-name} `PARTITION` Region.

Second, all `LuceneIndexes` must be created before the Region to which the `LuceneIndex` applies.

NOTE: To help ensure that all declared `LuceneIndexes` defined in a Spring container are created before the Regions
on which they apply, {sdg-acronym} includes the `org.springframework.data.gemfire.config.support.LuceneIndexRegionBeanFactoryPostProcessor`.
You may register this Spring {spring-framework-javadoc}/org/springframework/beans/factory/config/BeanFactoryPostProcessor.html[`BeanFactoryPostProcessor`]
in XML config by using `<bean class="org.springframework.data.gemfire.config.support.LuceneIndexRegionBeanFactoryPostProcessor"/>`.
The `o.s.d.g.config.support.LuceneIndexRegionBeanFactoryPostProcessor` may only be used when using {sdg-acronym} XML config.
More details about Spring's `BeanFactoryPostProcessors` can be found {spring-framework-docs}/core.html#beans-factory-extension-factory-postprocessors[here].

It is possible that these {data-store-name} restrictions will not apply in a future release which is why
the {sdg-acronym} `LuceneIndexFactoryBean` API takes a reference to the Region directly as well,
rather than just the Region path.

This is more ideal when you want to define a `LuceneIndex` on an existing Region with data at a later point
during the application's lifecycle and as requirements demand. Where possible, {sdg-acronym} strives to adhere to
strongly-typed objects. However, for the time being, you must use the `regionPath` property to specify the Region
to which the `LuceneIndex` is applied.

NOTE: Additionally, in the preceding example, note the presence of Spring's `@DependsOn` annotation
on the `Books` Region bean definition. This creates a dependency from the `Books` Region bean to the `bookTitleIndex`
`LuceneIndex` bean definition, ensuring that the `LuceneIndex` is created before the Region on which it applies.

Now that once we have a `LuceneIndex`, we can perform Lucene-based data access operations, such as queries.

== Lucene Template Data Accessors

{sdg-name} provides two primary templates for Lucene data access operations, depending on how low of a level
your application is prepared to deal with.

The `LuceneOperations` interface defines query operations by using {data-store-name}
{x-data-store-javadoc}/org/apache/geode/cache/lucene/package-summary.html[Lucene types],
which are defined in the following interface definition:

[source,java]
----
public interface LuceneOperations {

    <K, V> List<LuceneResultStruct<K, V>> query(String query, String defaultField [, int resultLimit]
        , String... projectionFields);

    <K, V> PageableLuceneQueryResults<K, V> query(String query, String defaultField,
        int resultLimit, int pageSize, String... projectionFields);

    <K, V> List<LuceneResultStruct<K, V>> query(LuceneQueryProvider queryProvider [, int resultLimit]
        , String... projectionFields);

    <K, V> PageableLuceneQueryResults<K, V> query(LuceneQueryProvider queryProvider,
        int resultLimit, int pageSize, String... projectionFields);

    <K> Collection<K> queryForKeys(String query, String defaultField [, int resultLimit]);

    <K> Collection<K> queryForKeys(LuceneQueryProvider queryProvider [, int resultLimit]);

    <V> Collection<V> queryForValues(String query, String defaultField [, int resultLimit]);

    <V> Collection<V> queryForValues(LuceneQueryProvider queryProvider [, int resultLimit]);
}
----

NOTE: The `[, int resultLimit]` indicates that the `resultLimit` parameter is optional.

The operations in the `LuceneOperations` interface match the operations provided by the {data-store-name}'s
{x-data-store-javadoc}/org/apache/geode/cache/lucene/LuceneQuery.html[LuceneQuery] interface.
However, {sdg-acronym} has the added value of translating proprietary {data-store-name} or Apache Lucene `Exceptions`
into Spring's highly consistent and expressive DAO
https://docs.spring.io/spring/docs/current/spring-framework-reference/htmlsingle/#dao-exceptions[exception hierarchy],
particularly as many modern data access operations involve more than one store or repository.

Additionally, {sdg-acronym}'s `LuceneOperations` interface can shield your application from interface-breaking changes
introduced by the underlying {data-store-name} or Apache Lucene APIs when they occur.

However, it would be sad to offer a Lucene Data Access Object (DAO) that only uses {data-store-name} and Apache Lucene
data types (such as {data-store-name}'s `LuceneResultStruct`). Therefore, {sdg-acronym} gives you the
`ProjectingLuceneOperations` interface to remedy these important application concerns.  The following listing shows
the `ProjectingLuceneOperations` interface definition:

[source,java]
----
public interface ProjectingLuceneOperations {

    <T> List<T> query(String query, String defaultField [, int resultLimit], Class<T> projectionType);

    <T> Page<T> query(String query, String defaultField, int resultLimit, int pageSize, Class<T> projectionType);

    <T> List<T> query(LuceneQueryProvider queryProvider [, int resultLimit], Class<T> projectionType);

    <T> Page<T> query(LuceneQueryProvider queryProvider, int resultLimit, int pageSize, Class<T> projectionType);
}
----

The `ProjectingLuceneOperations` interface primarily uses application domain object types that let you work with
your application data. The `query` method variants accept a projection type, and the template applies the query results
to instances of the given projection type by using the Spring Data Commons Projection infrastructure.

Additionally, the template wraps the paged Lucene query results in an instance of the Spring Data Commons
`Page` abstraction. The same projection logic can still be applied to the results in the page and are lazily projected
as each page in the collection is accessed.

By way of example, suppose you have a class representing a `Person`, as follows:

[source,java]
----
class Person {

    Gender gender;

    LocalDate birthDate;

    String firstName;
    String lastName;

    ...

    String getName() {
        return String.format("%1$s %2$s", getFirstName(), getLastName());
    }
}
----

Additionally, you might have a single interface to represent people as `Customers`, depending on your application view,
as follows:

[source,java]
----
interface Customer {

    String getName()

}
----

If I define the following `LuceneIndex`...

[source,java]
----
@Bean
LuceneIndexFactoryBean personLastNameIndex(GemFireCache gemfireCache) {

    LuceneIndexFactoryBean personLastNameIndex =
        new LuceneIndexFactoryBean();

    personLastNameIndex.setCache(gemfireCache);
    personLastNameIndex.setFields("lastName");
    personLastNameIndex.setRegionPath("/People");

    return personLastNameIndex;
}
----

Then you could query for people as `Person` objects, as follows:

[source,java]
----
List<Person> people = luceneTemplate.query("lastName: D*", "lastName", Person.class);
----

Alternatively, you could query for a `Page` of type `Customer`, as follows:

[source,java]
----
Page<Customer> customers = luceneTemplate.query("lastName: D*", "lastName", 100, 20, Customer.class);
----

The `Page` can then be used to fetch individual pages of the results, as follows:

[source,java]
----
List<Customer> firstPage = customers.getContent();
----

Conveniently, the Spring Data Commons `Page` interface also implements `java.lang.Iterable<T>`, making it easy
to iterate over the contents.

The only restriction to the Spring Data Commons Projection infrastructure is that the projection type must be
an interface. However, it is possible to extend the provided SDC Projection infrastructure and provide a custom
https://docs.spring.io/spring-data/commons/docs/current/api/org/springframework/data/projection/ProjectionFactory.html[`ProjectionFactory`]
that uses https://github.com/cglib/cglib[CGLIB] to generate proxy classes as the projected entity.

You can use `setProjectionFactory(:ProjectionFactory)` to set a custom `ProjectionFactory` on a Lucene template.

== Annotation Configuration Support

Finally, {sdg-name} provides annotation configuration support for `LuceneIndexes`.

Eventually, the {sdg-acronym} Lucene support will finds its way into the Repository infrastructure extension for
{data-store-name} so that Lucene queries can be expressed as methods on an application `Repository` interface,
in much the same way as the <<gemfire-repositories.queries.executing,OQL support>> works today.

However, in the meantime, if you want to conveniently express `LuceneIndexes`, you can do so directly on
your application domain objects, as the following example shows:

[source,java]
----
@PartitionRegion("People")
class Person {

    Gender gender;

    @Index
    LocalDate birthDate;

    String firstName;

    @LuceneIndex;
    String lastName;

    ...
}
----

To enable this feature, you must use {sdg-acronym}'s annotation configuration support specifically with the
`@EnableEntityDefineRegions` and `@EnableIndexing` annotations, as follows:

[source,java]
----
@PeerCacheApplication
@EnableEntityDefinedRegions
@EnableIndexing
class ApplicationConfiguration {

  ...
}
----

NOTE: `LuceneIndexes` can only be created on {data-store-name} servers since `LuceneIndexes` only apply
to `PARTITION` Regions.

Given our earlier definition of the `Person` class, the {sdg-acronym} annotation configuration support finds
the `Person` entity class definition and determines that people are stored in a `PARTITION` Region called "`People`"
 and that the `Person` has an OQL `Index` on `birthDate` along with a `LuceneIndex` on `lastName`.
