Searching for child or parent Issues using JQL functions report wrong results in Jira
Platform notice: Server and Data Center only. This article only applies to Atlassian products on the Server and Data Center platforms.
Support for Server* products ended on February 15th 2024. If you are running a Server product, you can visit the Atlassian Server end of support announcement to review your migration options.
*Except Fisheye and Crucible
Summary
When searching for Issues using 3rd party apps JQL functions that deal with an Issue's hierarchy, the search may report more Issues than what's on Jira's View Issue Screen or Advanced Roadmaps Plans.
This may or may not happen when using JQL functions provided by Jira itself, like childIssuesOf("")
and parentIssuesOf("")
.
This issue may be more present on instances in which users make extensive use of the Issue Move operation, or regularly change Issue's Types. This may also happen on instances that have had a Hierarchy configuration changed recently.
Environment
Any version of Jira Software Data Center or Server.
Any version of Advanced Roadmaps or Portfolio for Jira (bundled into JSW from 8.15 onward).
Diagnosis
Search for Issues using 3rd party apps JQL functions that traverse Issue hierarchies.
Compare the resulting Issues to a Roadmap Plan containing those same Issues. Notice how some Issues aren't displayed in the Hierarchy as suggested by the 3rd party app JQL function.
Check if the additional Issues being reported have been moved or have had their Issue Type changed anytime in their timeline (or if their Parent's or Epics have had).
Cause
When an Issue Type changes, Jira doesn't cleanup the Parent Link data automatically, but instead warns on the UI the Parent Link is not valid and the user should update it.
If the Parent Link field isn't present in the Issue screen or it has an Epic Link instead, the Parent Link data won't be visible, making this situation harder to identify.
Jira keeps the Parent Link data even if it's become invalid (through Issue type change, Issue Move operation or Hierarchy configuration) and searches may report unexpected results. If there Issue has had an Epic Link set, the Parent Link data remains but Jira prioritizes the Epic Link instead — this is when 3rd party apps JQL function may differ in behavior: they may consider the Parent Link data still.
This is the Issue tracked to clear the data to make it easier for 3rd party apps to iterate over Issue Hierarchy:
JSWSERVER-24811 - Getting issue details... STATUS
Solution
1. Mitigating new occurrences
While JSWSERVER-24811 isn't implemented or the 3rd party app vendors update the apps to consider the Parent Link only in the absence of Epic Links, you may implement an Automation Rule to clear the Parent Link data every time an Issue Type is changed:
2. Cleaning up existing Issues
To cleanup existing Issue's Parent Link data is more laborious, because Jira will disregard Parent Link updates if there's an Epic Link present in the Issue.
We can achieve this in a 3-step procedure:
- Query the DB for the Hierarchy
- Query the DB for Issues with invalid Parent Links
- Bulk removing the Link through REST API
2.1 Query the DB for the Hierarchy
Run this DB query on Jira's DB:
select "ID", "ISSUE_TYPE_IDS", "TITLE" from "AO_D9132D_HIERARCHY_CONFIG" order by "ID" desc;
It'll output something like:
ID | ISSUE_TYPE_IDS | TITLE
----+----------------+------------
9 | 10805 | Initiative
8 | 198 | Capability
7 | | Epic
6 | | Story
5 | | Sub-task
(5 rows)
This is the Roadmaps Hierarchy config. There may be multiple Issue Types on each row.
Also confirm which Issue Type's the Epic in your instance:
select id, pname from issuetype where pname = 'Epic';
id | pname
-------+-------
5 | Epic
(1 row)
2.2 Query the DB for Issues with invalid Parent Links
You'll need to change this query depending on your Hierarchy configuration. This is for the example above:
select
il.id as "Link Id",
concat(ps.pkey, concat('-', concat(source.issuenum, concat(' (', concat(il.source, ')'))))) as "Source Issue",
concat(its.pname, concat(' (', concat(its.id, ')'))) as "Source Issue Type",
concat(ilt.linkname, concat(' (', concat(il.linktype, ')'))) as "Link Type",
concat(pd.pkey, concat('-', concat(dest.issuenum, concat(' (', concat(il.destination, ')'))))) as "Destination Issue",
concat(itd.pname, concat(' (', concat(itd.id, ')'))) as "Destination Issue Type"
from issuelink il
left join issuelinktype ilt on ilt.id = il.linktype
left join jiraissue source on source.id = il.source
left join project ps on ps.id = source.project
left join issuetype its on its.id = source.issuetype
left join jiraissue dest on dest.id = il.destination
left join project pd on pd.id = dest.project
left join issuetype itd on itd.id = dest.issuetype
where
ilt.linkname = 'Parent-Child Link'
and (
(dest.issuetype not in ('5', '198')) /* Epic, Capability */
or
(dest.issuetype = '5' and source.issuetype != '198') /* Epic to not Capability */
or
(dest.issuetype = '198' and source.issuetype != '10805') /* Capability to not Initiative */
);
We're fetching all Parent-Child Link links from invalid Issues. In our sample configuration, only Epic and Capability should have the Parent Link. Everything else shouldn't (they should have Epic Link, Issue-subtask link or be the top of the hierarchy).
We're also querying Links from Epics that aren't to Capabilities, and Capabilities that aren't to Initiatives.
2.3 Bulk removing the Link through REST API
For every "Link Id" reported in the query from step 2.2, we need to make a REST API request to Jira to remove that specific link:
DELETE https://jira-base-url/rest/api/2/issueLink/{{link_id}}
You may find this article on How to send bulk API requests using Postman useful for this. Since we're doing this through the REST API, no Full Reindex or restart is necessary.
You can then validate the Issue search again and check the results are more consistent.