Last updated Oct 20, 2022

How to index and search with a SearchIndexAccessor

Applicable:This tutorial applies to Confluence 7.17.0 or higher.
Level of experience:Advanced. You should complete at least one intermediate tutorial before working through this tutorial.

Overview

The SearchIndexAccessor is a V2-level Java API for accessing Confluence's indices and any custom search indices. You might be familiar with the similar SearchManager, however that interface is purely for searching, hence only provides a sub-set of functionality, and remains for legacy purposes.

A SearchIndexAccessor can be obtained by creating a custom search index or hooking into Confluence's existing search indices via Spring's dependency injection. Both the content index and change index are available to access. However, being under the same interface, this will require explicitly qualifying the bean id using @Qualifier("changeIndexAccessor") or @Qualifier("contentIndexAccessor").

Permissions handling

Warning: If this is not done, your app may leak functionality or data which should not be visible for the current user.

Permissions checking should be done explicitly if required, as no permissions checking is promised by SearchIndexAccessor.

Just like the SearchManager, if permissions checks are required when searching, this must be explicitly done by adding a permissions checking query like SiteSearchPermissionsQuery or by using the PredefinedSearchBuilder to build an ISearch which contains permissions checking.

Examples

Getting a SearchIndexAccessor

Use constructor dependency injection to get access to the content index.

1
2
public class AppService {
    private SearchIndexAccessor searchIndexAccessor;
    private SiteSearchPermissionsQueryFactory siteSearchPermissionsQueryFactory;

    public AppService(@ComponentImport @Qualifier("contentIndexAccessor") SearchIndexAccessor searchIndexAccessor,
                      @ComponentImport SiteSearchPermissionsQueryFactory siteSearchPermissionsQueryFactory) {
        this.searchIndexAccessor = searchIndexAccessor;
        this.siteSearchPermissionsQueryFactory = siteSearchPermissionsQueryFactory;
    }
    ...

Searching

Construct SearchQuery and perform a search against the content index.

1
2
    ...
    public SearchResults search(String fieldName, String fieldValue) throws InvalidSearchException {
        SearchQuery query = BooleanQuery.builder()
                .addMust(new TermQuery(fieldName, fieldValue),
                        siteSearchPermissionsQueryFactory.create())
                .build();

        ISearch search = new ContentSearch(query, null);

        return searchIndexAccessor.search(search, null);
    }
    ...

Executing and Indexing

Execute a SearchIndexAction against the content index. A SearchIndexAction is a functional description of how to use a SearchIndexWriter.

1
2
    ...
    public void create(String fieldName, String fieldValue) {
        AtlassianDocument atlassianDocument = new AtlassianDocument();
        atlassianDocument.addField(new StringFieldDescriptor(fieldName, fieldValue, FieldDescriptor.Store.YES));

        searchIndexAccessor.execute(writer -> writer.add(atlassianDocument));
        }
    ...

Scanning

Scan the content index with a SearchQuery, consuming the results by adding them to a List.

1
2
    ...
    public List<Map<String, String[]>> scan(String fieldName, String fieldValue) {
        SearchQuery query = BooleanQuery.builder()
                .addMust(new TermQuery(fieldName, fieldValue),
                        siteSearchPermissionsQueryFactory.create())
                .build();

        List<Map<String, String[]>> results = new ArrayList<>();

        searchIndexAccessor.scan(query, null, results::add);

        return results;
    }
    ...

Batching

Batch update the content index with the addition of two documents, one being a related companion document that must always exist alongside the parent.

1
2
    ...
    public void create(String fieldName, String fieldValue) {
        String parentId = UUID.randomUUID().toString();

        AtlassianDocument atlassianDocument = new AtlassianDocument();
        atlassianDocument.addField(new StringFieldDescriptor(fieldName, fieldValue, FieldDescriptor.Store.YES));
        atlassianDocument.addField(new StringFieldDescriptor("id", parentId, FieldDescriptor.Store.YES));

        AtlassianDocument companionDocument = new AtlassianDocument();
        atlassianDocument.addField(new StringFieldDescriptor("id", UUID.randomUUID().toString(), FieldDescriptor.Store.YES));
        atlassianDocument.addField(new StringFieldDescriptor("parent", parentId, FieldDescriptor.Store.YES));

        searchIndexAccessor.withBatchUpdate(() ->
            searchIndexAccessor.execute((writer) -> {
                writer.add(atlassianDocument);
                writer.add(companionDocument);
                })
        );
    }
}

Further reading

Learn more about extending Confluence's search capabilities with these tutorials:

Rate this page: