Scriptrunner Enhanced Search


The ScriptRunner Enhanced Search view allows you to search through JIRA Cloud using advanced JQL functions. You may be familiar with them if you use the JIRA Server version of ScriptRunner. The Cloud infrastructure differs from the Server version, therefore some queries are not available or are implemented in different way. All changes are duly described.

enhanced search

Getting Started

As you can see in the image above, the ScriptRunner Enhanced Search view allows users to create JIRA filters using the powerful functions listed below.

These filters can then be used in Dashboards and JIRA Software Boards, for example.

On Settings page you can specify how often ScriptRunner synchronises your filters. The default interval is five minutes. The maximum allowed interval is an hour. We recommend you to use the default value, unless you notice that filter synchronising makes too many requests to your JIRA instance and causes a performance problem.

interval filter sync period

Every X minute, ScriptRunner will check if something has changed in your JIRA issues or someone has changed the ScriptRunner add-on settings recently. If check is positive, then filters are synced with the changes made to the issues in your JIRA instance.
Filters are always run with the same set of permissions as the user who created the filter.

Warning

You must make sure that the ScriptRunner Add-On User has the Global Permisson to Browse Users in order for this feature to work.

If you want to update or delete these Enhanced Search filters, use the ScriptRunner Enhanced Search page instead of the JIRA filters user interface.

Insert Function

We have provided an Insert Function button to help new users generate the correct JQL for their query.

Insert Function

You can select functions from the list on the left-hand-side and you’ll be presented with a form you can fill in to generate valid JQL for that function.

Most of the functions require a JQL 'subquery' that is used as the starting point for the function you’ve selected. In the image above, the subquery "project = EXAMPLE" tells the linkedIssuesOffunction that it should find issues linked to the results of that subquery.

Click on 'Insert into JQL' to have the form add the relevant query clause to the end of your existing search query.

Technical Background

As a add-on developers we don’t have access to underlying database. The only way we can make a search to retrieve data is by using Search API. We make simple queries using the available APIs and then process the results using pattern matching, comparing, aggregation and so on.

Let’s have a look at this example query:

assignee = currentUser() AND issueFunction in dateCompare("project = SRCLOUD", "created +1w < firstCommented") AND status = "In progress"

This contains a nested query. As you can see, the date compare function in the middle of the expression is not natively supported by JIRA Cloud. We parse this query and process the dateCompare subquery as a first step. We make a standard search using the subquery "project = SRCLOUD" and then we apply the comparision expression on the results. At the end we make a final search to JIRA Cloud with structure:

assignee = currentUser() AND issue in ("SRCLOUD-12", "SRCLOUD-13", "SRCLOUD-55") AND status = "In progress"

As you can observe, the dateCompare query was replaced by another query: issue in (..). It contains issue ids/keys that were evaluated from the subquery. If you specify more than one advanced subquery, all of them will be evaluated and replaced in the final search.

Troubleshooting Tips

Occasionally you may see a red warning triangle show up next to your filter on the enhanced search filter, which indicates that there is an issue with the filter. The screenshot below shows an example of what the warning triangle looks like.

Enhanced Search Warning Triangle

When you see this warning triangle then you can hover over the warning triangle in order to see further information on what is causing the warning.

If hovering over the filter does not indicate an issue related to missing fields or projects then this could indicate that the filter needs re synchronising, and you can re synchronise the filter by running the search again and clicking the Sync Filter button in order to see if the warning is removed.

If you are still encountering warnings with your filter then you should create a ticket in our support portal located here.

Comparison with ScriptRunner for JIRA Server

Differences

  • If you want to use a custom field, you can use either field key customfield_10500 or field name e.g. 'StoryPoints'. Custom field names are likely to have spaces, which can’t be parsed. If so, remove the spaces. It’s not case-sensitive but use camel-case for maximum readability. If your field names have any other punctuation you must use the format customfield_10500.

  • When you specify regular expressions, you don’t have to escape backslash characters. Example: write \d+ instead of \\d+

Unsupported Functions

Compared to ScriptRunner for JIRA Server, we are not able to deliver the functions described below.

myProjects/recentProjects

We are not able to support queries that are based on user-specific data.

aggregateExpression

At this time we are not implementing this function.

Included Functions

There are a large number of included functions, documented below.

It may be useful to make a request to the JIRA Cloud REST API to see which fields you can use inside of the function expressions, e.g. customfield_10024 or priority.id

https://your-jira.atlassian.net/rest/api/2/issue/{issueIdOrKey}?expand=schema

Almost all of the functions require a subquery as a first parameter. If you provide an empty string (e.g. "") your query will be really inefficient because it will match each issue in your JIRA instance.

You cannot use the statement below inside of your subqueries, because user-specific data is not supported inside the subqueries:

assignee = currentUser()

Dates

dateCompare

dateCompare(Subquery, date comparison expression)

This function lets you compare two dates on the same issue, for instance to find all issues that were updated later than created:

issueFunction in dateCompare("project = SRCLOUD", "created < updated")

You can use time windows on both side of the expression. Eg to find issues resolved before or up to six days after their due date:

issueFunction in dateCompare("project = SRCLOUD", "resolutionDate +1d < dueDate +1w")

You can also use created or updated.

To find issues that were resolved within two weeks of creation, use:

issueFunction in dateCompare("project = SRCLOUD", "created +2w > resolutionDate ")

You can also use date and datetime custom fields. Example:

issueFunction in dateCompare("project = SRCLOUD", "resolutionDate > customfield_10500")

where customfield_10500 is the name of a 'date' custom field.

You can also use the equality operator = to find issues that have been resolved on the same date that’s in a custom field

issueFunction in dateCompare("project = SRCLOUD", "resolutionDate = customfield_10500")
TIP
If your date contains time, equality operator won’t be useful. Use the clearTime() method described below.

You can also use the greater or equal operator >= to find issues that have been resolved on or after the same date that’s in a custom field

issueFunction in dateCompare("project = SRCLOUD", "resolutionDate >= customfield_10500")

You can also use the smaller or equal operator  to find issues that have been resolved on or before the same date that’s in a custom field

issueFunction in dateCompare("project = SRCLOUD", "resolutionDate <= customfield_10500")

You can also use the not equal operator != to find issues that have been resolved on a different date than that’s in a custom field

issueFunction in dateCompare("project = SRCLOUD", "resolutionDate != customfield_10500")

You can use the .clearTime() method to get the date part of a date or datetime field. The date will be converted to the user’s timezone, before the date is extracted

issueFunction in dateCompare("project = SRCLOUD", "firstCommented.clearTime() = lastCommented.clearTime()")

Subtasks

subtasksOf

subtasksOf(Subquery)

Returns the subtasks of issues specified by the subquery, eg:

issueFunction in subtasksOf("project = SRCLOUD")

To find unresolved subtasks of resolved issues you might do:

issueFunction in subtasksOf("resolution IS NOT EMPTY") AND resolution IS EMPTY

To find subtasks that are Open, but their parent issue has a resolution of Fixed, you could use:

issueFunction in subtasksOf("resolution = Fixed") AND status = Open

subtasksOf is analagous to saying "subtasks which have a parent selected by the subquery".

TIP
You can leave the subquery as an empty string in all these examples if you want, but it’ll make your query inefficient

parentsOf

parentsOf(Subquery)

Returns the parents of issues specified by the subquery. For example, to find closed parents with open subtasks in the project SRCLOUD, you could do:

status = Closed AND issueFunction in parentsOf("project = SRCLOUD AND status = open")

To show parent issues that have at least one open subtask, in the SRCLOUD project, you might do:

issueFunction in parentsOf("project = SRCLOUD AND status = Open")

linkedIssuesOf

linkedIssuesOf(Subquery, [link name])

This is similar to parentsOf and subtasksOf, in that it will return the linked issues.

To find all the unresolved issues that are blocked by issues in the Open state you could use:

issueFunction in linkedIssuesOf("status = Open", "blocks") AND resolution IS EMPTY

With no link name argument, this function will search for the linked issues whatever the link type:

issueFunction in linkedIssuesOf("resolution = unresolved")

linkedIssuesOfRecursive

linkedIssuesOfRecursive(Subquery, [Link name])

This is similar to linkedIssuesOf, in that it will return the linked issues, however this function traverses issue links recursively to return all issues that are linked (directly and indirectly) to the results of the initial subquery.

To find all direct and indirectly linked issues of an particular issue you can use:

issueFunction in linkedIssuesOfRecursive("issue = DEMO-1")

So if we have the following setup:

jql recursive

Then our query would return: DEMO-1, DEMO-2, DEMO-3, DEMO-4 and DEMO-5.

NOTE
DEMO-1 is returned by the query above as it has "is blocked by" links from DEMO-2 and DEMO-3

You can limit the type (and direction) of links that are traversed using the second parameter.

issueFunction in linkedIssuesOfRecursive("issue = DEMO-1", "blocks")

In this instance, if we use the example already mentioned, only DEMO-2, DEMO-3 and DEMO-4 will be returned.

The Link type parameter behaves exactly the same as the Link description parameter for linkedIssuesOf.

WARNING
If you have 1000s of indirectly linked issues, traversal of all of the links will take a long time.

linkedIssuesOfRecursiveLimited

linkedIssuesOfRecursiveLimited(Subquery, Traversal depth, [Link name])

This function is exactly the same as linkedIssuesOfRecursive but it allows us to limit the depth of traversals along issue links that the function will do.

The following query will follow all links, recursively, from all issues in the DEMO project until it has traversed a maximum of 2 links deep along any link path.

issueFunction in linkedIssuesOfRecursiveLimited("project = DEMO", 2)

Using the following setup:

jql recursive limited

Our query with the depth parameter would return: DEMO-1, DEMO-2, TEST-1, TEST-2, TEST-4 and TEST-5.

We could specify the link name as well, to get different results:

issueFunction in linkedIssuesOfRecursiveLimited("issue = TEST-2", 3, "is blocked by")

which would return: TEST-3, TEST-6 and TEST-5

epicsOf

epicsOf(subquery)

JIRA Software users can query on epic links, eg find all Epics that have unresolved stories:

issueFunction in epicsOf("resolution = unresolved")

Jira Software users can also query on epic links to find all epics which do contain any issues.

project = <ProjectKeyHere> and issuetype = Epic and NOT (issueFunction in epicsOf("project = <ProjectKeyHere>")

This function will only return issues of the Epic type.

issuesInEpics

issuesInEpics(subquery)

This function allows you to find issues related to epics found by the subquery e.g. find all stories for open Epics in a project

issueFunction in issuesInEpics("project = DEMO AND status = 'To Do'")

Find unresolved stories in resolved Epics:

issueFunction in issuesInEpics("resolution IS NOT EMPTY") AND resolution IS EMPTY

Agile functions

The board parameter in the three functions below may be either the name of the board, or the board ID. You can find the board ID in the page URL when viewing the board e.g. https://example.atlassian.net/secure/RapidBoard.jspa?rapidView=24&projectKey=EXAMPLE shows the board ID to be 24

inSprint

inSprint(board, sprint)

Query the issues of a sprint of an agile board.

issueFunction in inSprint("EX Scrum Board", "Sprint 3")

nextSprint

nextSprint(board)

Query the issues of the next active sprint of an agile board.

issueFunction in nextSprint(452) AND assignee = myself()

previousSprint

previousSprint(board)

Query the issues of the previous active sprint of an agile board.

issueFunction in previousSprint("Kanban Board") AND fixVersion IS EMPTY

Differences with ScriptRunner for JIRA Server

The following agile functions are available on ScriptRunner for JIRA Server, but not available on ScriptRunner for JIRA Cloud:

  • addedAfterSprintStart

  • removedAfterSprintStart

  • incompleteInSprint

  • completeInSprint

Others

issueFieldMatch

issueFieldMatch (subquery, fieldname, regexp)

Query on any field by regular expression. Performance will be roughly proportional to the number of issues selected by the subquery, so use the query that selects the smallest set of issues you can, eg just your projects.

To find all issues where the description contains a ABC0000 where 0000 is any number, you could use:

issueFunction in issueFieldMatch("project = SRCLOUD", "description", "ABC\d{4}")
WARNING

Note - the function searches for the regular expression anywhere within the field. To match the entirety of the field, use ^ and $, e.g. ^ABC\d{4}$

issueFieldExactMatch

issueFieldExactMatch (subquery, fieldname, regexp)

Find issues by matching the text of a field exactly.

projectMatch / componentMatch / versionMatch

projectMatch(reg exp)
componentMatch(reg exp)
versionMatch(reg exp)

These functions provide lists of projects, components, versions respectively that match the provided regular expression.

Example: all issues that have a component beginning with Web:

component in componentMatch("^Web.*")

All issues in the JRA project that have a fix version beginning with RC:

fixVersion in versionMatch("^RC.*")

expression

expression(Subquery, expression)

This is an absurdly powerful function that lets you compare attributes of fields. What you can compare are the system estimate and date fields, and any numeric, date, or datetime custom field. It’s probably easiest to explain through some examples, starting from the simple.

Find issues where more work was logged than originally estimated:

issueFunction in expression("project = SRCLOUD", "timespent > timeoriginalestimate")

Note that this could also be done by using plain JQL: workratio > 1. However with plain JQL, you could not find issues which are likely to exceed their estimate:

issueFunction in expression("project = SRCLOUD", "timespent + timeestimate > timeoriginalestimate")

You would probably want to use resolution is empty as the subquery, to filter out issues that have been completed.

Search for issues where the work logged exceeded the original estimate by more than 5 days (normalised for timetracking, so > 40 hours work logged):

issueFunction in expression("project = SRCLOUD", "timespent > timeoriginalestimate + 5*wd")
TIP
Do use 5*d or 5*w and not 5d as in dateCompare - the syntax is (unfortunately) different.
NOTE
Notes on time tracking

When comparing calendar dates it is useful to think of days as periods of 24 hours. However when working with time estimates we think in terms of working hours, days and weeks where a working day is typically 8 hours (configurable in JIRA) and a working week is 5 days.

You can use working day units:

wdNumber of milliseconds in a working day (according to your specification in Admin → Time trackingwwNumer of days in a working week multiplied by the value above.

Or, if you are comparing timetracking fields with non-time tracking fields, for example remaining effort and due date, you need to use the special fromTimeTracking function. For example, search for issues which, if their remaining estimate is valid, are going to miss their due date. You could devote extra resources to these to ensure that doesn’t happen:

issueFunction in expression("resolution is empty", "now() + fromTimeTracking(remainingestimate) > duedate")

When you specify an estimate of 3 days, JIRA stores that internally as 24 hours, and renders it as 3 days for estimate fields. The fromTimeTracking function converts that to 72 hours so it can be used to manipulate and compare with other dates.

Find issues where the product of two number custom fields is greater than X:

issueFunction in expression("project = SRCLOUD", "customfield_10026 * customfield_10028 > 100")

Find issues where the creator is not equal to the reporter

issueFunction in expression("project = SRCLOUD", "creator != reporter")

Find issues that have a high ratio of votes to complexity (assuming Complexity is a numeric field):

issueFunction in expression("project = SRCLOUD", "votes / Complexity > 100")
Using functions

You can use Date functions, for instance now(), startOfDay() etc, anywhere you would use a date. Supported functions:

CAUTION
Some arguments will need to be quoted. For example, if you want to say one week before the start of the month you would write startOfMonth('-1w').