ServiceNow
Conditions and Query Building in ServiceNow: Filters, Encoded Queries, Condition Builder, and Script Equivalents
Three ways to filter records
In ServiceNow, you filter records in three places. They all generate the same thing underneath — an encoded query.
| Where | How | Used by |
|---|---|---|
| List filter (UI) | Condition builder — dropdown menus | Admins, end users |
| Encoded query string | Text string like priority=1^active=true | URL params, scripts, reports |
| GlideRecord addQuery | JavaScript method calls | Developers in scripts |
The condition builder (UI)
Every list view has a filter icon (funnel). Click it, and you get the condition builder. You pick a field, an operator, and a value. You can chain conditions with AND/OR.
After building your filter, you can get the encoded query string: right-click the breadcrumb above the list and select "Copy query". This gives you a string you can paste into scripts.
Encoded query syntax
An encoded query is a string representation of filter conditions. ^ means AND. ^OR means OR. ^NQ means a new query (OR group).
// Simple conditions priority=1 // priority equals 1 active=true // active is true state!=6 // state is not 6 (Resolved) // Chaining with AND (^) priority=1^active=true^state!=6 // priority = 1 AND active = true AND state != 6 // OR conditions (^OR) priority=1^ORpriority=2 // priority = 1 OR priority = 2 // Grouped OR with AND priority=1^ORpriority=2^active=true // (priority = 1 OR priority = 2) AND active = true // Special operators short_descriptionLIKEvpn // contains 'vpn' short_descriptionSTARTSWITHEmail // starts with 'Email' short_descriptionENDSWITHfailed // ends with 'failed' assigned_toISEMPTY // field is empty assigned_toISNOTEMPTY // field is not empty sys_created_on>2026-01-01 // created after Jan 1 sys_created_onONToday // created today sys_created_onRELATIVEGE@hour@ago@24 // created in last 24 hours // Reference field dot-walking assignment_group.name=Network Ops // group name (through reference) caller_id.department.name=IT // caller's department name // IN operator stateIN1,2,3 // state is 1, 2, or 3 stateNOT IN6,7 // state is not 6 or 7
Using encoded queries in scripts
// Instead of multiple addQuery calls:
var gr = new GlideRecord('incident');
gr.addQuery('priority', 1);
gr.addQuery('active', true);
gr.addQuery('state', '!=', 6);
gr.query();
// You can do this in one line:
var gr = new GlideRecord('incident');
gr.addEncodedQuery('priority=1^active=true^state!=6');
gr.query();
// Both produce the exact same SQL query. Use whichever is clearer.
// For complex filters, encoded queries are often easier to read
// and you can copy them directly from the list filter UI.
Building encoded queries dynamically
// Build a query string from variables
var table = 'incident';
var maxAge = 30;
var groups = ['Network Ops', 'Database Admins'];
var query = 'active=true';
query += '^sys_created_on>=' + gs.daysAgo(maxAge);
query += '^assignment_group.nameIN' + groups.join(',');
var gr = new GlideRecord(table);
gr.addEncodedQuery(query);
gr.query();
gs.info('Matching records: ' + gr.getRowCount());
Dot-walking
Dot-walking lets you traverse reference fields to access fields on related records. It works in queries, getValue, and getDisplayValue.
var gr = new GlideRecord('incident');
gr.addQuery('priority', 1);
gr.query();
while (gr.next()) {
// Direct field
gs.info(gr.getValue('number'));
// One level: caller's name
gs.info(gr.getDisplayValue('caller_id'));
// Two levels: caller's department name
gs.info(gr.getValue('caller_id.department')); // sys_id of department
gs.info(gr.getDisplayValue('caller_id.department')); // 'IT'
// Three levels: caller's department head's email
gs.info(gr.getValue('caller_id.department.dept_head.email'));
}
// Dot-walking in queries works the same way:
var gr = new GlideRecord('incident');
gr.addQuery('caller_id.department.name', 'IT');
gr.addQuery('caller_id.vip', true);
gr.query();
// Finds incidents where the caller is in the IT department AND is a VIP
Dot-walking performance trap
// BAD: dot-walking inside a loop fires a separate query per row
var gr = new GlideRecord('incident');
gr.addQuery('active', true);
gr.setLimit(500);
gr.query();
while (gr.next()) {
// Each of these is a separate database query!
var email = gr.caller_id.email.toString();
var dept = gr.caller_id.department.name.toString();
}
// For 500 rows, this could be 1000+ extra queries.
// BETTER: query the related table separately and build a lookup
var users = {};
var userGr = new GlideRecord('sys_user');
userGr.addQuery('sys_id', 'IN', callerIds.join(','));
userGr.query();
while (userGr.next()) {
users[userGr.getValue('sys_id')] = {
email: userGr.getValue('email'),
dept: userGr.getDisplayValue('department')
};
}
Condition builder in Business Rules, Notifications, etc.
Many ServiceNow configuration records (Business Rules, Notifications, UI Policies, ACLs) have a "Condition" field that uses the same condition builder as list filters. The condition is stored as an encoded query string internally.
// When you configure a Business Rule with condition: // priority = 1 AND state changes to Resolved // The platform stores it as: // priority=1^stateVALCHANGES6 // VALCHANGES is a special operator meaning "value changed to" // It only evaluates to true during the transaction where the change happens. // Other special operators: // VALCHANGES — value changed to // ANYTHING — field was modified (any change) // SAMEAS — same as another field's value // NSAMEAS — not same as another field's value
Quick reference table
| I want to | UI operator | Encoded query | addQuery equivalent |
|---|---|---|---|
| Equals | is | priority=1 | addQuery('priority', 1) |
| Not equals | is not | state!=6 | addQuery('state', '!=', 6) |
| Greater than | greater than | priority>2 | addQuery('priority', '>', 2) |
| Contains | contains | short_descriptionLIKEvpn | addQuery('short_description', 'CONTAINS', 'vpn') |
| Starts with | starts with | numberSTARTSWITHINC | addQuery('number', 'STARTSWITH', 'INC') |
| Is empty | is empty | assigned_toISEMPTY | addNullQuery('assigned_to') or encoded assigned_toISEMPTY |
| In list | is one of | stateIN1,2,3 | addQuery('state', 'IN', '1,2,3') |
| Changed to | changes to | stateVALCHANGES6 | current.state.changesTo(6) (in BR) |