9.1 JQL Fundamentals, Clauses, Operators & History (WAS / CHANGED)
Key Takeaways
- JQL clauses combine fields, operators, values, and keywords (AND, OR, NOT, EMPTY, NULL, ORDER BY), and AND binds tighter than OR, so use parentheses.
- Negative operators such as != and NOT IN don't return issues where the field is empty; add OR field IS EMPTY when you need them.
- WAS, WAS IN, WAS NOT, WAS NOT IN, and CHANGED work only on Assignee, Fix Version, Priority, Reporter, Resolution, and Status.
- CHANGED supports seven predicates (FROM, TO, BY, BEFORE, AFTER, ON, DURING); WAS tests a past state, and CHANGED tests a change event.
- Basic search can't do OR across different fields, parentheses, history operators, or most functions, so complex needs require JQL.
9.1 JQL Fundamentals, Clauses, Operators & History (WAS / CHANGED)
Quick Summary: Jira Query Language (JQL) is the core search engine powering issue discovery, agile boards, dashboard gadgets, automation triggers, and SLA tracking across Jira Cloud. A JQL query consists of fields, operators, values, and connecting keywords. Beyond querying the current state of an issue, JQL provides powerful historical auditing capabilities through the
WASandCHANGEDoperators. Understanding how historical searching interfaces with Jira's changelog, which fields support historical analysis, and how to chain historical predicates (FROM,TO,BY,DURING,BEFORE,AFTER,ON) is essential for both daily administration and passing the ACP-120 exam.
Anatomy of a JQL Query
JQL is a structured, SQL-like query language designed specifically for searching issues within Jira. When navigating to Filters > Advanced issue search (or selecting Switch to JQL from the basic search bar), administrators write queries composed of one or more clauses:
+--------------------------------------------------------------------------+
| ANATOMY OF A JQL CLAUSE |
+--------------------------------------------------------------------------+
| [ FIELD ] [ OPERATOR ] [ VALUE ] [ KEYWORD ] |
| project = "ALPHA" AND |
| status IN ("In Dev", "Testing") AND |
| assignee = currentUser() ORDER BY |
| created DESC |
+--------------------------------------------------------------------------+
1. Fields
A field represents a specific issue attribute in Jira. Fields fall into two primary categories:
- System Fields: Native attributes built into Jira Cloud, such as
project,issuetype,status,resolution,priority,assignee,reporter,created,updated, andduedate. - Custom Fields: Custom attributes created by Jira Administrators. In JQL, custom fields can be referenced by their human-readable name (e.g.,
"Account ID" = "ACC-9921"). If multiple custom fields share identical names, or if a custom field name contains reserved words or special characters, administrators must reference the field by its immutable custom field ID using the formatcf[XXXXX](e.g.,cf[10042] = "ACC-9921").
2. Operators
An operator defines the logical relationship between the field and the value. JQL operators include:
- Comparison Operators:
=,!=,>,>=,<,<= - Set Operators:
IN,NOT IN - Null Validation Operators:
IS EMPTY,IS NOT EMPTY,IS NULL,IS NOT NULL - Text Matching Operators:
~(CONTAINS),!~(DOES NOT CONTAIN) - Historical Operators:
WAS,WAS IN,WAS NOT IN,CHANGED
3. Values and Functions
A value represents the actual data evaluated against the field. Values can be string literals enclosed in double quotes ("High"), numbers (42), ISO-formatted dates ("2026-06-15"), or dynamic function calls that resolve at query runtime (such as currentUser() or startOfWeek()).
4. Keywords and Logical Precedence
Keywords join multiple clauses or control the final result presentation:
- Boolean Connectors:
AND,OR,NOT - Sorting:
ORDER BY <field> [ASC | DESC] - Parentheses
()for Grouping: JQL evaluatesANDoperators beforeORoperators by default. To prevent logical ambiguity, administrators must use parentheses to explicitly group logical conditions.
Consider the following query:
project = PROJ AND status = Open OR assignee = currentUser()
Because AND binds more tightly than OR, Jira interprets this query as: (Issues in project PROJ that are Open) OR (Issues assigned to the current user in ANY project across the entire instance).
To correctly restrict both conditions to project PROJ, parentheses are mandatory:
project = PROJ AND (status = Open OR assignee = currentUser())
Comparison, Set, and Text Search Operators
Understanding the precise mechanics and limitations of standard JQL operators prevents common data reporting errors.
| Operator | Supported Field Types | Description & Operational Behavior | Example JQL Clause |
|---|---|---|---|
= | Fields with defined values (priority, status, users, versions, select lists) | Exact match on the value. Free-text fields such as Summary and Description use ~ instead. | priority = Critical |
!= | All single-value fields | Inequality matching. Note: != excludes issues where the field is empty! | status != Closed |
>, >=, <, <= | Date, numeric, duration fields | Quantitative comparison against dates, numbers, and time estimates. | created >= "2026-01-01" |
IN | Fields supporting multiple options | Evaluates whether the field matches any value in a comma-separated list (logical OR). | issuetype IN (Bug, Incident) |
NOT IN | Fields supporting multiple options | Evaluates whether the field does not match any value in the list. Excludes empty fields unless combined with OR is EMPTY. | priority NOT IN (Low, Lowest) |
IS EMPTY / IS NULL | Nullable fields | Matches issues where the field contains no value. Equivalent to IS NULL. | resolution IS EMPTY |
IS NOT EMPTY | Nullable fields | Matches issues where the field has been populated with any value. | duedate IS NOT EMPTY |
~ (CONTAINS) | Free-text fields (Summary, Description, Environment, Comments) | Performs a full-text search with stemming and wildcard support. | summary ~ "database connection" |
!~ (DOES NOT CONTAIN) | Free-text fields | Matches issues where the free-text field does not contain the specified search term. | description !~ "reproduced" |
[!IMPORTANT] A critical ACP-120 exam trap involves the behavior of negative operators (
!=andNOT IN) on empty fields. If an administrator queriespriority != High, Jira returns all issues where priority is explicitly set to Medium, Low, or Lowest. However, if priority were nullable and empty on an issue, that issue would not be returned. To include unassigned or empty values in negative queries, use:(priority != High OR priority IS EMPTY).
Historical Searching: The WAS Operator
Standard JQL clauses evaluate only the current state of an issue. However, compliance audits, SLA reporting, and team velocity metrics frequently require knowing whether an issue previously held a specific value, regardless of its current state.
Jira provides the WAS operator to search the historical audit changelog of an issue.
+--------------------------------------------------------------------------+
| THE WAS OPERATOR MECHANICS |
+--------------------------------------------------------------------------+
| [FIELD] [WAS OPERATOR] [VALUE] [PREDICATE] |
| status WAS "Resolved" BY currentUser() |
| assignee WAS IN ("alex", "sam") DURING ("2026-01-01", |
| "2026-03-31") |
+--------------------------------------------------------------------------+
Supported Fields for WAS
Atlassian documents that WAS, WAS IN, WAS NOT, WAS NOT IN, and CHANGED work with six fields only:
assigneefixVersionpriorityreporterresolutionstatus
WAS matches the value name as it was configured when the change happened, and also matches the value's ID. If an issue has more than 10,000 changes, history operators search only the most recent ones.
[!WARNING] Custom fields do not support the
WASoperator in native Jira Cloud. Attempting to executecf[10050] WAS "Approved"will throw a JQL syntax validation error. Administrators must rely on Jira Automation audit trails, external audit apps, or changelog REST APIs for historical tracking of custom fields.
Historical Predicates for WAS
The WAS operator can be qualified using temporal and actor predicates to isolate exactly when or by whom a historical value was set:
BEFORE "date": Matches issues where the field held the specified value before a certain date.status WAS "In Progress" BEFORE "2026-01-01"AFTER "date": Matches issues where the field held the specified value after a certain date.priority WAS High AFTER "2026-06-01"ON "date": Matches issues where the field held the value on a specific calendar date.assignee WAS "jsmith" ON "2026-05-15"DURING ("date1", "date2"): Matches issues where the field held the value during an inclusive date range.status WAS "Blocked" DURING ("2026-03-01", "2026-03-15")BY user: Matches issues where the value was set by a specific user (account ID), a list of users, or a function such ascurrentUser().status WAS "Under Review" BY currentUser()
WAS vs. WAS IN vs. WAS NOT IN
status WAS "Done": Returns issues that occupied the "Done" status at any point in their lifecycle, even if they were subsequently reopened to "In Progress".status WAS IN ("In Review", "QA Testing"): Returns issues that occupied either "In Review" or "QA Testing" at some historical point.status WAS NOT IN ("Done", "Cancelled"): Returns issues that have never been in Done or Cancelled. It's equivalent tostatus WAS NOT "Done" AND status WAS NOT "Cancelled".
Historical Event Searching: The CHANGED Operator
While the WAS operator queries historical states (i.e., whether an issue resided in a state), the CHANGED operator queries historical events (i.e., whether a field underwent a change or transition).
+--------------------------------------------------------------------------+
| THE CHANGED OPERATOR & PREDICATES |
+--------------------------------------------------------------------------+
| [FIELD] [CHANGED] [FROM value] [TO value] [TEMPORAL/ACTOR] |
| status CHANGED FROM "In Progress" TO "Done" BY currentUser() |
| DURING ( |
| startOfWeek(), |
| now() |
| ) |
+--------------------------------------------------------------------------+
Supported Fields for CHANGED
CHANGED works on the same six fields as WAS: assignee, fixVersion, priority, reporter, resolution, and status.
The Seven Predicates for CHANGED
The CHANGED operator supports seven optional predicates that can be chained together in a single clause:
| Predicate | Purpose & Syntax | Practical Use Case |
|---|---|---|
FROM "value" | Specifies the previous value before the change event. | status CHANGED FROM "In Review" |
TO "value" | Specifies the new value after the change event. | status CHANGED TO "Done" |
BY user | Identifies the user who executed the modification. | assignee CHANGED BY currentUser() |
BEFORE "date" | Restricts the change event to have occurred prior to a timestamp. | resolution CHANGED TO Done BEFORE "2026-01-01" |
AFTER "date" | Restricts the change event to have occurred following a timestamp. | priority CHANGED TO Critical AFTER startOfWeek() |
ON "date" | Restricts the change event to a specific calendar date. | status CHANGED TO Shipped ON "2026-04-10" |
DURING ("date1", "date2") | Restricts the change event to a specific window of time. | status CHANGED TO Resolved DURING (startOfWeek(), now()) |
Chaining Predicates Together
Administrators can combine transition predicates to construct highly specific audit queries. For example, to find all issues transitioned from "In Progress" directly to "Done" by the current user during the active working week:
status CHANGED FROM "In Progress" TO "Done" BY currentUser() DURING (startOfWeek(), now())
Crucial Distinction: WAS vs. CHANGED
Consider an issue that was transitioned to "Resolved" on March 1st and remained in "Resolved" until March 10th.
status CHANGED TO "Resolved" ON "2026-03-05": Returns FALSE. The transition event occurred on March 1st, not on March 5th.status WAS "Resolved" ON "2026-03-05": Returns TRUE. On March 5th, the issue resided in the "Resolved" status.
This conceptual difference between an event (CHANGED) and a state (WAS) is a frequent focal point of ACP-120 exam scenarios.
Basic Search vs. JQL: Know the Limits
Blueprint 6.1 lists the limitations of basic search. Basic search builds a query from dropdowns (project, type, status, assignee, and "more" fields) plus a text box. It's quick, but it can't express everything JQL can:
| Need | Basic Search | JQL (Advanced) |
|---|---|---|
OR between different fields (status = Blocked OR priority = Highest) | Not possible; criteria are always combined with AND | Yes |
| Parentheses to control precedence | No | Yes |
History operators (WAS, CHANGED) | No | Yes |
Functions such as membersOf(), startOfWeek("-1"), spacesWhereUserHasRole() | Only a few built-in choices (for example, current user) | Full function library |
Custom sorting with ORDER BY on any field | Limited to column sorting in results | Yes |
If a JQL query is too complex to show as basic criteria, Jira won't let you switch it back to basic mode. That's a common sign in exam scenarios that the query uses OR, parentheses, or functions.
Performance Tips for Historical Queries
History operators search each issue's change history, which is far more work than checking current values. Keep these queries fast and readable:
- Scope first: add
project,issuetype, or date limits before the history clause:project = PROJ AND status CHANGED TO Resolved. - Bound the time: use
DURING,AFTER, orBEFORErather than searching an issue's entire history. - Combine current and past state:
status != Done AND status WAS "QA Rejected"finds open issues that were rejected at some point.
A compliance auditor requires a list of all Jira issues across the 'FIN' project where the Priority field was escalated to 'Critical' at any time during the previous quarter (from 2026-01-01 to 2026-03-31), regardless of who performed the update or what priority the issue currently holds. Which JQL query correctly retrieves these issues?
An enterprise QA team lead wants to identify all defect issues in project QA that were transitioned from 'Under Test' directly to 'Closed' by a specific test engineer (account ID: 557058:abc-123) within the past 7 days. Which JQL query complies with Jira Cloud historical search syntax?
A newly appointed Jira Administrator attempts to execute the query project = SEC AND "Security Clearance Level" WAS "Top Secret" to audit past changes to a custom single-select dropdown field. The query fails to return results and generates a JQL syntax error. What is the root cause of this failure?