Heads up! On March 5, starting at 4:30 PM Central Time, our community will be undergoing scheduled maintenance for a few hours. During this time, you will find the site temporarily inaccessible. Thanks for your patience. Read more.

×
Create
cancel
Showing results for 
Search instead for 
Did you mean: 
Sign up Log in

Simple (custom) JQL to search all children (recursively) including issues in Epic of ticket

Thomas König October 15, 2024

We have a ticket structure in which there can be several levels of hierarchies (Initiatives, Sub-Initiatives, Epics, Tasks, Sub-Tasks). Initiatives, Sub-Initiatives and Epics are linked with "is parent of" links. Epics and Tasks can either be linked by the "is parent of" link or by the Epic link (or both).

Now, I need a simple JQL to return all the tickets in an Initiative, e.g. `issueFunction in featureFilter("key=KEY-123")`. I can achieve that by combining several "issueFunction in linkedIssuesOfRecursive" functions. However, that JQL is rather complex and the key of the Initiative has to be put in several places, so this is quite cumbersome and error prone.

I've tried to achieve the desired result with a custom JQL in script runner. For this, I modified the example in the documentation, see below. As long as I don't use any scriptrunner jql in the TEMPLATE_QUERY, this seems to work fine but it fails as soon as I start using other scriptrunner jqls. However, I need the "linkedIssuesOfRecursive".

So my questions are:

  • Can I somehow use other scriptrunner jqls in a custom jql?
  • Is the code for the "linkedIssuesOfRecursive" available somwhere? (Maybe I could then just re-implement it in a way that fulfills my need.)
  • Of course, I'm also happy if someone would offer a different solution.
package com.onresolve.jira.groovy.jql
 
import com.atlassian.jira.component.ComponentAccessor
import com.atlassian.jira.jql.parser.JqlQueryParser
import com.atlassian.jira.jql.query.LuceneQueryBuilder
import com.atlassian.jira.jql.query.QueryCreationContext
import com.atlassian.jira.jql.validator.NumberOfArgumentsValidator
import com.atlassian.jira.user.ApplicationUser
import com.atlassian.jira.util.MessageSet
import com.atlassian.query.clause.TerminalClause
import com.atlassian.query.operand.FunctionOperand
import org.apache.lucene.search.Query
 
import java.text.MessageFormat
 
class JqlAliasFunction extends AbstractScriptedJqlFunction implements JqlQueryFunction {
 
    /**
     * Modify this query as appropriate.
     *
     * See {@link java.text.MessageFormat} for details
     */
    public static final String TEMPLATE_QUERY =
        "issueFunction in linkedIssuesOfRecursive('{0}', 'is parent of') OR {0}"
        /*
        "issueFunction in linkedIssuesOfRecursive('key in ({0})', 'is parent of') OR issueFunction in linkedIssuesOfRecursive('issueFunction in linkedIssuesOfRecursive(\"key in ({0})\", \"is parent of\")', 'is Epic of') OR key ={0}"
        */
 
    JqlQueryParser queryParser = ComponentAccessor.getComponent(JqlQueryParser)
    LuceneQueryBuilder luceneQueryBuilder = ComponentAccessor.getComponent(LuceneQueryBuilder)
 
    @Override
    String getDescription() {
        "Filter for all children (recursively) including epic links."
    }
 
    @Override
    MessageSet validate(ApplicationUser user, FunctionOperand operand, TerminalClause terminalClause) {
        def messageSet = new NumberOfArgumentsValidator(1, 1, getI18n()).validate(operand)
 
        if (messageSet.hasAnyErrors()) {
            return messageSet
        }
 
        def query = mergeQuery(operand)
        messageSet = searchService.validateQuery(user, query)
        messageSet
    }
 
    @Override
    List getArguments() {
        [
            [
                description: "query to define the feature",
                optional   : false,
            ]
        ]
    }
 
    @Override
    String getFunctionName() {
        "featureFilter"
    }
 
    @Override
    Query getQuery(QueryCreationContext queryCreationContext, FunctionOperand operand, TerminalClause terminalClause) {
        def query = mergeQuery(operand)
        luceneQueryBuilder.createLuceneQuery(queryCreationContext, query.whereClause)
    }
 
    private com.atlassian.query.Query mergeQuery(FunctionOperand operand) {
        def queryStr = MessageFormat.format(TEMPLATE_QUERY, operand.args.first())
        queryParser.parseQuery(queryStr)
    }
}

1 answer

0 votes
Prosper Agwegiokhe
Contributor
October 15, 2024

Hi @Thomas König ,

I’m Prosper, a support engineer at Appfire, and I’m here to help.

If you're open to other plugin suggestions, I recommend trying out our app, JQL Search Extensions (JQLSE). You can use this query

issue in childrenOfIssuesInQueryRecursive("parent")

to retrieve the children of a "parent" issue. Additionally, you can specify a depth if needed.

For more information about this query, please refer to our documentation. We also have other helper functions that you might find useful.

Don't hesitate to contact our support team if you have any further questions. We'd be happy to assist!

Best regards,
Prosper

Suggest an answer

Log in or Sign up to answer