Available: | Jira 11.2 and later. |
When developing Jira apps that need to perform custom search operations, you may need to create query mappers that can translate your custom query objects into the appropriate search engine queries. This is particularly important when supporting both Lucene and OpenSearch backends.
Before you create your query mappers, you need to have:
Create a query interface that extends com.atlassian.jira.search.Query
.
1 2package com.example.plugin.search.query; import com.atlassian.jira.search.Query; public interface RegexpQuery extends Query { String field(); String value(); }
Create a concrete implementation of your query interface:
1 2package com.example.plugin.search.query; import java.util.Objects; import static java.util.Objects.requireNonNull; public class DefaultRegexpQuery implements RegexpQuery { private final String field; private final String value; public DefaultRegexpQuery(final String field, final String value) { this.field = requireNonNull(field); this.value = requireNonNull(value); } @Override public String field() { return field; } @Override public String value() { return value; } @Override public boolean equals(final Object obj) { if (obj == this) return true; if (obj == null || obj.getClass() != this.getClass()) return false; final var that = (DefaultRegexpQuery) obj; return Objects.equals(this.field, that.field) && Objects.equals(this.value, that.value); } @Override public int hashCode() { return Objects.hash(field, value); } @Override public String toString() { return "DefaultRegexpQuery[" + "field=" + field + ", " + "value=" + value + ']'; } }
Depending on your search platform, implement the query mapper that translates your custom query to Lucene or OpenSeearch queries.
For Lucene, use the code below.
1 2public class RegexpQueryMapper implements LuceneQueryMapper<RegexpQuery> { @Override public org.apache.lucene.search.RegexpQuery map(final RegexpQuery query) { return new org.apache.lucene.search.RegexpQuery( new org.apache.lucene.index.Term(query.field(), query.value()) ); } }
For OpenSearch, use the code below.
1 2public class RegexpQueryMapper implements OpenSearchQueryMapper<RegexpQuery> { @Override public Query map(final RegexpQuery query) { return org.opensearch.client.opensearch._types.query_dsl.Query.of(q -> q.regexp(r -> r .field(query.field()) .value(query.value()) )); } }
Depending on your search platform, register the mapper.
For Lucene, use the code below.
1 2final LuceneQueryMapperRegistry registry = ComponentAccessor.getComponent(LuceneQueryMapperRegistry.class); registry.registerMapper(RegexpQuery.class, this);
For OpenSearch, use the code below.
1 2final OpenSearchQueryMapperRegistry registry = ComponentAccessor.getComponent(OpenSearchQueryMapperRegistry.class); registry.registerMapper(RegexpQuery.class, this);
Here's an example of how to use your custom query in a search.
1 2final IndexAccessorRegistry registry = ComponentAccessor.getComponent(IndexAccessorRegistry.class); final IndexAccessor indexAccessor = registry.getIssuesIndexAccessor(); final SearchResponse response = indexAccessor.getSearcher().search( SearchRequest.builder() .query(new DefaultRegexpQuery(field, pattern)) .documentType(DocumentTypes.ISSUE) .build(), PageRequest.of(0, 100));
You can use the Jira App Lifecycle annotations to properly register and deregister query mappers:
@PostConstruct
: Called during component initialization after dependency injection is complete. This is where you can register your mapper with the appropriate registry.@PreDestroy
: Called during component destruction when the app is being disabled or uninstalled. Make sure you clean up by deregistering your mapper.1 2@PostConstruct public void onStarted() { final LuceneQueryMapperRegistry registry = ComponentAccessor.getComponent(LuceneQueryMapperRegistry.class); if (registry == null) { return; } registry.registerMapper(RegexpQuery.class, this); } @PreDestroy public void onStopped() { final LuceneQueryMapperRegistry registry = ComponentAccessor.getComponent(LuceneQueryMapperRegistry.class); if (registry == null) { return; } registry.deregisterMapper(RegexpQuery.class); }
Null registry depends on the search platform:
OpenSearchQueryMapperRegistry
will be null.LuceneQueryMapperRegistry
will be null.Pay attention to the null registry because it allows your app to work seamlessly regardless of which search platform is active.
1 2if (registry == null) { return; }
@PostConstruct
and deregister in @PreDestroy
to prevent memory leaks.Rate this page: