Migrating FieldIndexers in Jira 11
If your app uses a FieldIndexer through the customfield-searcher element, you need to update its configuration to align with the new search API. This update will prepare your app for future search tool enhancements that do not rely on Lucene. For a refresher on customfield-searcher, explore the custom field searcher configuration guide.
This page highlights the key modifications to indexers in the search API to assist you in updating your app. For more information, refer to the Search API upgrade guide
We're actively working on the upgrade documentation and will continue to update this article. Additionally, we will communicate any changes in our changelog to make tracking them easier. Explore the changelog
On this page:
- Indexers
- Registering fields on the index schema at runtime
- Search API to Lucene field mappings
- Maintaining backwards compatability
Indexers
Indexers that use the Search API are designed to be platform agnostic. To ensure compatibility across multiple platforms, the following field definition restrictions apply:
- Fields must be pre-defined and registered as part of the index schema.
- Field names must be unique.
Starting with Jira Data Center 11, com.atlassian.jira.issue.index.indexers.FieldIndexer is replaced by com.atlassian.jira.search.issue.index.indexers.FieldIndexer. The table below provides method mappings for clear reference and implementation guidance.
Legacy Lucene | Search API | Notes |
|---|---|---|
|
| Returns the ID of the Jira field that this indexer is indexing. This must be unique for each If the indexer doesn't represent a System or Custom field in Jira, this must still return a unique string that describes the indexer. |
|
| Defines the fields on the index schema to configure how those fields will be stored and indexed on Lucene (and future search platforms). |
|
| |
|
| |
|
| To maintain existing behavior, the Lucene implementation of Search API won't index fields that are invisible. |
|
|
To create a custom field indexer, ensure you implement the new FieldIndexer interface or extend one of its base classes. Additionally, make sure all fields have unique names and field IDs.
- Define a field. In this example, we'll use 'currency'.
- Add this field to the index schema.
- Index the field.
Registering fields on the index schema at runtime
Because Search API fields must be pre-defined and registered as part of the index schema, a FieldIndexer can only index values for field names the schema already knows about. Emitting a field name that isn't on the schema throws an InvalidDocumentFieldException and fails the document reindex job. This is deliberate: it surfaces a registration problem immediately, rather than letting it appear later as missing search results or broken JQL clauses.
In most cases you don't need to register anything yourself. When your indexer is reachable through a customfield-searcher, everything it returns from getFields() is collected into the issue index schema for you, and Jira rebuilds the schema whenever a custom field is created, updated, or deleted, or when the plugin module providing the searcher is enabled or disabled. Returning every field your indexer writes to from getFields() is all that's required.
You only need to register fields explicitly when they can't be discovered from a registered FieldIndexer at the time Jira builds the schema, for example, when your app's field names are only known at runtime, or when your app adds an indexer after the schema has already been built. For these cases, use IndexAccessor.updateSchema(...):
public class CurrencyFieldSchemaRegistrar {
private final IndexAccessorRegistry indexAccessorRegistry;
public CurrencyFieldSchemaRegistrar(final IndexAccessorRegistry indexAccessorRegistry) {
this.indexAccessorRegistry = indexAccessorRegistry;
}
public void registerCurrencyFields() {
indexAccessorRegistry.getIssuesIndexAccessor().updateSchema(this::addCurrencyFields);
}
private IndexSchema addCurrencyFields(final IndexSchema existingSchema) {
final IndexSchema.Builder builder = existingSchema.toBuilder();
if (!builder.fieldIsDefined("currency")) {
builder.addField(KeywordField.builder("currency")
.indexed()
.docValues()
.stored()
.build());
}
return builder.build();
}
}
Keep the following in mind:
Always start from the schema you're given. Build the new schema with
existingSchema.toBuilder()so you keep the fields registered by Jira and by other apps, then add your own. Never construct a schema from scratch.Field names are unique across the index. The schema is keyed by field name, so adding a field with a name already in use replaces the existing definition and Jira logs a warning. Guard with
fieldIsDefined()and namespace your field names so they can't collide with another app's e.g.com.mycompany.myapp.fields.currencyfield.A new field is picked up by the underlying search platform straight away, but documents indexed before the change aren't rewritten. Changing the type or properties of a field that's already in use requires a reindex.
Search API to Lucene field mappings
Search API fields can be defined with the following properties:
- Indexed: The field value is indexed and can be used for searching.
- Stored: The original field value is stored and can be retrieved in a query result.
- DocValues: The field can be used for non-query features such as sorting and aggregation.
- MultiValues: A single document can have multiple values for the same field.
The Search API provides different types of fields for various data types and access patterns. To assist in migrating your app's Lucene fields to Search API fields, here is a description of how Jira maps Search API fields into the underlying Lucene index:
| Field | Mapping |
|---|---|
| KeywordField |
For example:
Results in Lucene fields:
|
AnalyzedTextField |
|
IntField |
|
LongField, DateTimeField |
|
DoubleField |
|
Maintaining backwards compatibility
Some configurations are possible in Lucene but not supported in OpenSearch, and therefore aren't part of the ongoing Search API. However, some mechanisms have been created to make this transition easier.
LuceneNameOverride
Lucene supports storing multiple different types of data with the same field name but Search API doesn’t support that. Due to this, we introduced a new method overrideLuceneName() in Jira 10.4 but it was marked as deprecated in the same version and completely removed in Jira 11, only being there for backwards compatibility.
Affected fields in Jira 11
As we've removed overrideLuceneName() in Jira 11, the following Jira fields have been affected:
Field name | Legacy Lucene field name | Search API field name | Product |
|---|---|---|---|
Summary | pq_support_summary | summary.pq_support | Jira |
Description | pq_support_description | description.pq_support | |
Environment | pq_support_environment | environment.pq_support | |
Comment body | pq_support_body | body.pq_support | |
Issue progress | progress | progress | |
progress.sort | |||
Issue workratio | workratio | workratio | |
workratio.sort | |||
Text Field custom field | pq_support_customfield_xxxxx | customfield_xxxxx.pq_support | |
Epic Label | customfield_xxxxx | customfield_xxxxx | JSW |
sort_customfield_xxxxx | |||
pq_support_customfield_xxxxx | customfield_xxxxx.pq_support | ||
customfield_xxxxx.pq_support_sort | |||
customfield_xxxxx_folded | customfield_xxxxx_folded | ||
customfield_xxxxx_folded.sort | |||
Satisfaction | customfield_xxxxx | customfield_xxxxx | JSM |
customfield_xxxxx.sort | |||
Organisation | customfield_xxxxx | customfield_xxxxx | |
customfield_xxxxx.sort |
RetrievalName
By default, you can read the value of a field from a document using the field's name that was used to index the value. Specifying the retrieval name of a field allows you to use a different name. For example:
KeywordField textField = KeywordField.builder("searchApiFieldName")
.retrievalName("oldFieldName")
.build();
...
String documentValue = document.getString("oldFieldName");