Applicable: | This tutorial applies to Confluence 7.0 or higher. |
Level of experience: | Advanced. You should complete at least one intermediate tutorial before working through this tutorial. |
The v2 search API is a low level search API that is available to third-party app developers. Because it's low level, we only recommended you use it when you can't implement your use case using CQL, which uses Confluence's high level search API.
The API is search engine agnostic, so your app is not likely to be affected if Confluence's search engine changes in the future. This tutorial will show you how to create a reusable custom query, sort it, and use it to implement a REST resource. By defining a custom query we can make the business logic reusable, easy to use, and robust to changes in the physical structure of the document in the search index.
In this tutorial we will create a plugin that defines a v2 custom query and use it to implement a REST service. The tutorial consists of 3 main elements:
After completion you should be able to use the v2 search API to query data by title via REST.
To complete this tutorial, you'll need to be familiar with:
rm
, ls
, curl
You can find the source code for this tutorial on Atlassian Bitbucket.
To clone the repository, run the following command:
1 2git clone https://bitbucket.org/atlassian_tutorial/confluence-v2-search-query-tutorial.git
Alternatively, you can download the source as a ZIP archive.
This tutorial was last tested with Confluence 7.0 using Atlassian SDK 8.0.2.
The TitleQuery
implements the SearchQuery
interface. It takes an input string and uses it to construct a query against the targeted search index. The method SearchQuery#expand
expresses the logic using Confluence's built-in v2 queries.
In this case we want to return content with a title matching the given input or that has a prefix of the unstemmed title itself, or its parent, that matches the input.
1 2public class TitleQuery implements SearchQuery { private static final String KEY = "titleQuery"; private final String query; public TitleQuery(String query) { this.query = query; } ... @Override public SearchQuery expand() { return BooleanQuery.builder() .addShould(new QueryStringQuery(singletonList(SearchFieldNames.TITLE), query, BooleanOperator.OR)) .addShould(new PrefixQuery(SearchFieldNames.CONTENT_NAME_UNSTEMMED_FIELD, query)) .addShould(new PrefixQuery(SearchFieldNames.PARENT_TITLE_UNSTEMMED_FIELD, query)) .build(); } }
SortBySpaceKey
implements the SearchSort
interface. The method SearchSort#expand
specifies that we want to sort the result by space key, then content type, using Confluence's built-in v2 sort.
1 2public class SortBySpaceKey implements SearchSort { private static final String KEY = "sortBySpaceKey"; private final Order order; public SortBySpaceKey(Order order) { this.order = order; } ... @Override public Order getOrder() { return order; } @Override public SearchSort expand() { return new FieldSort(SearchFieldNames.SPACE_KEY, Type.STRING, order); } }
This is a REST module that performs a search using the above query and sort. We execute a search using the v2 SearchManager which is injected into the REST module using the @ComponentImport
annotation. Note that we are passing an instance of ContentSearch
to the SearchManager, so the search will target the content index.
1 2@Path("/search") @Consumes(value = MediaType.APPLICATION_JSON) @Produces(value = MediaType.APPLICATION_JSON) public class SearchByTitleResource { private final SearchManager searchManager; @Autowired public SearchByTitleResource(@ComponentImport SearchManager searchManager) { this.searchManager = requireNonNull(searchManager); } @Path("/title") @GET public List<Map<String, String>> search(@QueryParam("query") String query) { SearchResults result; try { result = searchManager.search(new ContentSearch( new TitleQuery(query), new SortBySpaceKey(SearchSort.Order.ASCENDING), SiteSearchPermissionsSearchFilter.getInstance(), 0, 10)); } catch (InvalidSearchException e) { throw new RuntimeException(e); } List<Map<String, String>> response = new ArrayList<>(); result.forEach(x -> response.add(ImmutableMap.of("title", x.getDisplayTitle(), "url", x.getUrlPath()))); return response; } }
We specify the root path for all REST modules of the plugin in atlassian-plugin.xml
.
1 2... <rest key="titleSearch" path="/v2/search/query/tutorial" version="none"> </rest> ...
Make sure you have saved all your code changes to this point.
Open a Terminal and navigate to the plugin root folder (where the pom.xml
file is stored).
Run the following command:
1 2atlas-run
This command builds your plugin code, starts a Confluence instance, and installs your plugin. This may take a while. When the process is complete, you’ll see many status lines on your screen concluding with something like this:
1 2[INFO] Confluence started successfully in 71s at http://localhost:1990/confluence [INFO] Type CTRL-D to shutdown gracefully [INFO] Type CTRL-C to exit
You can also build the plugin and deploy it into an existing Confluence instance.
Run the following command:
1 2curl -u admin:admin -G "http://localhost:1990/confluence/v2/search/query/tutorial/title" \ --data-urlencode "query=confluence"
This will return any content with a title matching "confluence", sorted by space key.
Learn more about extending Confluence's search capabilities with these tutorials:
Rate this page: