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

It's not the same without you

Join the community to find out what other Atlassian users are discussing, debating and creating.

Atlassian Community Hero Image Collage

Custom JQL function is giving me inconsistent results

Martin Chalány Community Leader Apr 02, 2020


I'm looking for someone who is expert in custom JQL functions. I've implemented one cool custom JQL function and it's playing games with me . Same search with this JQL function is giving me different results. And it's completely random. I get 21 results and then 21, 21, 4, 21, 4, 21, 21, 21, 4, .... I've tried it debug it in Script Console but it works perfectly ther. Can anyone here help me to move along? 


package com.onresolve.jira.groovy.jql

import com.atlassian.jira.JiraDataType
import com.atlassian.jira.JiraDataTypes
import com.atlassian.jira.component.ComponentAccessor
import com.atlassian.jira.issue.Issue
import com.atlassian.jira.jql.operand.QueryLiteral
import com.atlassian.jira.jql.parser.JqlQueryParser
import com.atlassian.jira.jql.query.QueryCreationContext
import com.atlassian.jira.permission.ProjectPermissions
import com.atlassian.jira.user.ApplicationUser
import com.atlassian.jira.web.bean.PagerFilter
import com.atlassian.query.clause.TerminalClause
import com.atlassian.query.operand.FunctionOperand

import java.util.regex.Matcher

class ScopeOf extends AbstractScriptedJqlFunction implements JqlFunction {

PermissionManager permissionManager = ComponentAccessor.getPermissionManager()
List<Issue> finalListOfIssues

String getDescription() {
"Function finds all issues in scope of the issue"

List<Map> getArguments() {
"description": "Issue key or a subquery in \"\"",
optional: false
] as List<Map>

String getFunctionName() {

JiraDataType getDataType() {

List<QueryLiteral> getValues(QueryCreationContext queryCreationContext, FunctionOperand operand, TerminalClause terminalClause) {
finalListOfIssues = []
ApplicationUser currentUser = queryCreationContext.getApplicationUser()
getListOfIssuesInScope(initialListOfIssues(operand.args[0], currentUser))

finalListOfIssues.unique().findAll {
queryCreationContext.securityOverriden || permissionManager.hasPermission(ProjectPermissions.BROWSE_PROJECTS, it as Issue, currentUser)
}.collect {
new QueryLiteral(operand,

List<Issue> initialListOfIssues(String operand, ApplicationUser currentUser) {
String pattern = /(?i)^"?[A-Z]+_?[A-Z\d]+-\d+"?$/
Matcher matcher = operand =~ pattern
String finalJQL

// argument is only one issue key
if(matcher) {
String issueKey = (matcher[0] as String).replace('"', '').replace("'", "")
finalJQL = "issue = ${issueKey}"
} else {
finalJQL = operand

return executeJqlQuery(finalJQL, currentUser)

List<Issue> executeJqlQuery(String queryString, ApplicationUser currentUser) {
def jqlQueryParser = ComponentAccessor.getComponent(JqlQueryParser)
def searchProvider = ComponentAccessor.getComponent(SearchProvider)

def query = jqlQueryParser.parseQuery(queryString)

def results =, currentUser, PagerFilter.getUnlimitedFilter())

return results.getIssues()

List<Issue> getSubTasks(Issue issue) {
return ComponentAccessor.getSubTaskManager()
.getSubTaskObjects(issue) as List<Issue>

List<Issue> getIssuesUnderEpic(Issue epicIssue) {
return ComponentAccessor.getIssueLinkManager()
.findAll {link -> == "Epic-Story Link"}
.collect { link -> return link.destinationObject}

List<Issue> getSolvingIssues(Issue solvedIssue) {
return ComponentAccessor.getIssueLinkManager()
.findAll {link -> == "Solves"}
.collect { link -> return link.sourceObject}

def getListOfIssuesInScope(List<Issue> listOfIssues) {
List<Issue> listToContinueWith

for (Issue issue : listOfIssues) {
switch (issue.getIssueType().getName()) {
case ["Story", "Task", "Improvement"]:
finalListOfIssues += getSubTasks(it)
case ["Epic"]:
listToContinueWith = getIssuesUnderEpic(issue) - finalListOfIssues
finalListOfIssues += listToContinueWith
case ["Idea", "Theme"]:
listToContinueWith = getSolvingIssues(issue) - finalListOfIssues
finalListOfIssues += listToContinueWith


2 answers

Have you tried to use issueFunction IN linkedIssuesOfAllRecursive(<subQuery>) fuction? I believe is the same you are trying

Martin Chalány Community Leader Apr 20, 2020

I know the JQL function, but the query for our full hierarchy (Theme -> Grand Idea -> Idea -> Epic -> Story -> Subtask) will be insane. We need our custom JQL function to it easier for anyone to get same result.

0 votes

Get some debug logging in there to show you what the JQL is being turned into for the Lucene query part. Use one of the Lucene index tools to analyze your index to check it is not changing.

Martin Chalány Community Leader Apr 20, 2020

I've added a ton of logging into the script and the result is very interesting. The function getValues returns always the correct result. Inconsistency arises beyond this point. I don't understand why :'(

Suggest an answer

Log in or Sign up to answer
Community showcase
Published in Jira

Admins, notify your Jira instance of system-wide changes with the new admin announcement banner

Hi All! We’re excited to share the launch of an announcement banner that lets Jira site administrators communicate directly to their users across their  Jira Cloud instance.  ...

561 views 15 18
Read article

Community Events

Connect with like-minded Atlassian users at free events near you!

Find an event

Connect with like-minded Atlassian users at free events near you!

Unfortunately there are no Community Events near you at the moment.

Host an event

You're one step closer to meeting fellow Atlassian users at your local event. Learn more about Community Events

Events near you