‹ All essays

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.

WhereHowUsed by
List filter (UI)Condition builder — dropdown menusAdmins, end users
Encoded query stringText string like priority=1^active=trueURL params, scripts, reports
GlideRecord addQueryJavaScript method callsDevelopers 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 toUI operatorEncoded queryaddQuery equivalent
Equalsispriority=1addQuery('priority', 1)
Not equalsis notstate!=6addQuery('state', '!=', 6)
Greater thangreater thanpriority>2addQuery('priority', '>', 2)
Containscontainsshort_descriptionLIKEvpnaddQuery('short_description', 'CONTAINS', 'vpn')
Starts withstarts withnumberSTARTSWITHINCaddQuery('number', 'STARTSWITH', 'INC')
Is emptyis emptyassigned_toISEMPTYaddNullQuery('assigned_to') or encoded assigned_toISEMPTY
In listis one ofstateIN1,2,3addQuery('state', 'IN', '1,2,3')
Changed tochanges tostateVALCHANGES6current.state.changesTo(6) (in BR)