A newer version of this documentation is available.

View Latest

Full Text Search (FTS) Using the Java SDK with Couchbase Server

You can use the Full Text Search service (FTS) to create queryable full-text indexes in Couchbase Server.

Couchbase offers Full-text search support, allowing you to search for documents that contain certain words or phrases. In the Java SDK you can search full-text indexes by using the Bucket.query(SearchQuery) API.

The FTS feature is a developer preview in Couchbase Server 4.5. As such, the support in the SDK is still uncomitted and the API is subject to change (although no major changes are expected as of this writing).

Querying a FTS index through the Java client is performed through the Bucket.query(SearchQuery q) method, providing a SearchQuery. Building a SearchQuery takes two parameters, the index name to query and the actual search query itself (kind of a statement). Additional search options may be specified by using the SearchQuer as a builder, chaining setters for each relevant option.

This method returns a SearchQueryResult whose iterator yields the results of the query (in the form of SearchQueryRow objects). It also exposes a status() for the request, some execution metrics() and facets() results if some facets have been requested. Instead of iterating directly on the result, you can access rows as a list through the hits() method, and in case of execution errors you can inspect the error messages in the errors() method. Note that partial results can happen in this case (and hits() will return them). Instead of getting partial results through hits(), one can combine results and errors and get an exception through the use of hitsOrFail().

The SearchQueryRow object contains the index, id and score properties, respectively identifying the exact FTS index that returned the hit, the id of the document that matched and a decimal score for the match. It also contains optional sections depending on the request and the availability of all relevant settings in the FTS mapping. Those are explanation() (an explanation of the plan followed by the FTS index to execute the query), locations() (a map-like listing of the location of all matching terms inside each relevant field that was queried), fragments() (a map-like listing of occurrences of the search terms in each field, with the context of the terms) and fields() (a map of the complete value of each requested field). Most of these need that the index be configured to store the data of a searched field.

Bucket bkt = CouchbaseCluster.create("192.168.33.101").openBucket("travel-sample");
MatchQuery fts = SearchQuery.match("term");
SearchQueryResult result = bkt.query(new SearchQuery("travel-search", fts));
for (SearchQueryRow row : result) {
    System.out.println(row);
}

Query Types

There are many different flavours of search queries, and each can be constructed through static factory methods in the SearchQuery class. All of these types derive from the AbstractFtsQuery and can be found in the com.couchbase.client.java.search.queries.AbstractFtsQuery package. It contains query classes corresponding to those enumerated in the FTS generic documentation.

It is important to distinguish between query options and general search options. Some options affect the search process in general (such as the limit, indicating how many results to return) while others only affect a specific query (such as fuzziness for a given query). Because multiple queries can be combined in a single search operation, query specific options can be specified only in the query object itself, while search options are specified at the level of the SearchQuery class, using builder methods.

Bucket bkt = CouchbaseCluster.create("192.168.33.101").openBucket("travel-sample");
MatchQuery fts = SearchQuery.match("term")
    //query options:
    .fuzziness(2).field("content");
SearchQuery query = new SearchQuery("travel-search", fts)
    //search options:
    //will show value for activity and country fields
    .fields("activity", "country")
    //will have max 3 hits
    .limit(3);

SearchQueryResult result = bkt.query(query);
for (SearchQueryRow row : result) {
    System.out.println(row);
}

Here’s some sample output for the previous query:

DefaultSearchQueryRow{index='travel-search_33760129d0737bff_b7ff6b68', id='landmark_11778', score=0.0313815325019958, explanation={}, \
locations=DefaultHitLocations{size=3, locations=[HitLocation{field='content', term='tea', pos=39, start=254, end=257},HitLocation{field='content', \
term='teas', pos=56, start=353, end=357},HitLocation{field='content', term='tart', pos=17, start=95, end=99}]}, fragments={}, fields={activity=eat, \
country=United States}}

DefaultSearchQueryRow{index='travel-search_33760129d0737bff_b7ff6b68', id='landmark_25547', score=0.02536160834515202, explanation={}, \
locations=DefaultHitLocations{size=3, locations=[HitLocation{field='content', term='tea', pos=33, start=191, end=194},HitLocation{field='content', \
term='try', pos=30, start=177, end=180},HitLocation{field='content', term='per', pos=57, start=337, end=340}]}, fragments={}, fields={activity=eat, \
country=United States}}

DefaultSearchQueryRow{index='travel-search_33760129d0737bff_8b80958a', id='landmark_26854', score=0.02079624734659704, explanation={}, \
locations=DefaultHitLocations{size=10, locations=[HitLocation{field='content', term='trim', pos=227, start=1255, end=1259},HitLocation{field='content', \
term='steam', pos=7, start=41, end=46},HitLocation{field='content', term='steam', pos=38, start=213, end=218},HitLocation{field='content', \
term='steam', pos=74, start=424, end=429},HitLocation{field='content', term='steam', pos=93, start=532, end=537},HitLocation{field='content', \
term='steam', pos=114, start=651, end=656},HitLocation{field='content', term='steam', pos=126, start=715, end=720},HitLocation{field='content', \
term='steam', pos=145, start=819, end=824},HitLocation{field='content', term='steam', pos=300, start=1611, end=1616},HitLocation{field='content', \
term='team', pos=59, start=335, end=339}]}, fragments={}, fields={activity=see, country=United States}}

Query Facets

Query facets may also be added to the general search parameters by using the addFacet(String name, SearchFacet facet) builder method on SearchQuery. You can create facet queries by instantiating facets through factory methods in the SearchFacet class.

SearchQuery query = new SearchQuery("travel-search", fts)
    //will have max 3 hits
    .limit(3)
    //will have a "category" facet on the top 3 countries in terms of hits
    .addFacets(SearchFacet.term("countries", "country", 3));
SearchQueryResult result = bkt.query(query);
System.out.println(result.facets());

Here is the facet part of the result from the query above:

{countries=TermFacetResult{name='countries', field='country', total=451, missing=0, other=0, terms=[{name='United States', \
                count=217}, {name='United Kingdom', count=188}, {name='France', count=46}]}}