ServiceNow GlideRecord Cheat Sheet for 2026
Every GlideRecord method you actually use, with operators, performance patterns, and copy-paste snippets you can drop straight into a Business Rule or Script Include.
The Core Loop
Every GlideRecord query follows the same five-step shape. Memorize it and the rest of this cheat sheet is just variations on the theme.
var gr = new GlideRecord('incident'); // 1. choose the table
gr.addQuery('active', true); // 2. filter
gr.addQuery('priority', '<=', 2);
gr.orderByDesc('sys_created_on'); // 3. order
gr.setLimit(100); // 4. limit
gr.query();
while (gr.next()) { // 5. iterate
gs.info(gr.getValue('number'));
}The pattern works the same in Business Rules, Script Includes, Scheduled Jobs, and Flow Designer Script steps. Keep the shape, change the table.
The Method Table
The methods you use 95% of the time, with one-liner examples. Bookmark this section.
| Method | Purpose | Example |
|---|---|---|
| addQuery(field, op, val) | Filter rows. | gr.addQuery('state', '!=', 7) |
| addEncodedQuery(str) | Apply a sysparm_query string. | gr.addEncodedQuery('active=true^priority=1') |
| addOrCondition(field, op, val) | OR clause off the last addQuery. | q.addOrCondition('state', 2) |
| addNullQuery(field) | Field IS NULL. | gr.addNullQuery('assigned_to') |
| addNotNullQuery(field) | Field IS NOT NULL. | gr.addNotNullQuery('assigned_to') |
| orderBy / orderByDesc | Sort. | gr.orderByDesc('sys_created_on') |
| setLimit(n) | Cap the row count. | gr.setLimit(100) |
| chooseWindow(first, last) | Server-side pagination window. | gr.chooseWindow(0, 50) |
| query() / _query() | Execute. _query bypasses ACLs. | gr.query() |
| next() / hasNext() | Iterate / lookahead. | while (gr.next()) {...} |
| get(sys_id) / get(field, val) | Fetch a single record. | gr.get('number', 'INC0010001') |
| getValue(field) | Raw string value. | gr.getValue('state') |
| getDisplayValue(field) | Display value. | gr.getDisplayValue('assigned_to') |
| setValue(field, val) | Set without dot notation. | gr.setValue('state', 7) |
| insert() / update() | Write changes. | gr.update() |
| deleteRecord() / deleteMultiple() | Delete one or all matched rows. | gr.deleteMultiple() |
| getRowCount() | Count after query (post-execute). | gr.getRowCount() |
| setWorkflow(false) | Suppress Business Rules. | gr.setWorkflow(false) |
| autoSysFields(false) | Suppress sys_updated_on bumps. | gr.autoSysFields(false) |
| initialize() | Fresh row, ready for insert. | gr.initialize() |
Query Operators
The operator argument in addQuery() supports the full set you would expect from a SQL-ish DSL. The unfamiliar ones are usually the ones that bite you.
gr.addQuery('field', '=', 'val'); // equals (default)
gr.addQuery('field', '!=', 'val'); // not equal
gr.addQuery('field', '>', 100); // greater than
gr.addQuery('field', '<', 100); // less than
gr.addQuery('field', '>=', 100);
gr.addQuery('field', '<=', 100);
gr.addQuery('field', 'IN', '1,2,3'); // value in list (string CSV)
gr.addQuery('field', 'NOT IN', '1,2,3');
gr.addQuery('field', 'STARTSWITH', 'abc');
gr.addQuery('field', 'ENDSWITH', 'xyz');
gr.addQuery('field', 'CONTAINS', 'foo');
gr.addQuery('field', 'DOES NOT CONTAIN', 'foo');
gr.addQuery('field', 'LIKE', '%abc%');
gr.addQuery('field', 'BETWEEN', '5@10'); // numeric rangeFor complex conditions, lean on addEncodedQuery. Build the filter visually in a list view, copy the URL's sysparm_query, and paste it into the script. It is the fastest path from requirement to working query.
Aggregations with GlideAggregate
When you need a count, sum, or group-by, use GlideAggregate instead of looping. It runs the aggregation in the database, not in your script.
var agg = new GlideAggregate('incident');
agg.addQuery('active', true);
agg.addAggregate('COUNT');
agg.addAggregate('AVG', 'reopen_count');
agg.groupBy('assignment_group');
agg.orderByAggregate('COUNT');
agg.query();
while (agg.next()) {
gs.info(agg.getDisplayValue('assignment_group') +
' count=' + agg.getAggregate('COUNT') +
' avg_reopens=' + agg.getAggregate('AVG', 'reopen_count'));
}GlideRecordSecure vs GlideRecord
GlideRecord bypasses ACLs in server-side script execution by default. GlideRecordSecure enforces them. Use GlideRecordSecure any time the requesting user's permissions are relevant - typically inside scoped apps, REST APIs, and Service Portal widgets.
var sec = new GlideRecordSecure('incident');
sec.addQuery('active', true);
sec.query();
while (sec.next()) {
// Only rows the running user can actually read.
}A common audit finding is a Script Include that uses plain GlideRecord and exposes data outside the requester's role. The 500+ checkpoints in Instance Audit flag this pattern automatically.
Performance Patterns
Use setLimit on exploratory queries
var gr = new GlideRecord('sys_audit');
gr.setLimit(10); // never iterate the whole audit table
gr.query();Use updateMultiple for bulk writes
var gr = new GlideRecord('incident');
gr.addQuery('assignment_group', oldGroup);
gr.setValue('assignment_group', newGroup);
gr.updateMultiple(); // one DB op instead of NAvoid N+1 lookups inside loops
// Bad: one query per incident
while (incidents.next()) {
var user = new GlideRecord('sys_user');
user.get(incidents.getValue('caller_id'));
gs.info(user.getValue('email'));
}
// Better: dot-walk the reference
while (incidents.next()) {
gs.info(incidents.caller_id.email + '');
}Use orderBy plus setLimit, not chooseWindow alone
chooseWindow without an explicit orderBy returns rows in undefined order. For pagination always pair them.
Five Copy-Paste Recipes
1. Most recent record matching a condition
var gr = new GlideRecord('incident');
gr.addQuery('caller_id', user.sys_id);
gr.orderByDesc('sys_created_on');
gr.setLimit(1);
gr.query();
if (gr.next()) { /* use gr */ }2. Count by group
var agg = new GlideAggregate('incident');
agg.addAggregate('COUNT');
agg.groupBy('priority');
agg.query();
while (agg.next()) {
gs.info('P' + agg.getValue('priority') + ': ' + agg.getAggregate('COUNT'));
}3. Bulk close stale incidents
var gr = new GlideRecord('incident');
gr.addEncodedQuery('active=true^sys_updated_on<javascript:gs.daysAgo(30)');
gr.setValue('state', 7);
gr.setValue('close_notes', 'Auto-closed - 30+ days inactive');
gr.updateMultiple();4. Dedupe a reference cache
var seen = {};
var gr = new GlideRecord('incident');
gr.addQuery('active', true);
gr.query();
while (gr.next()) {
var caller = gr.getValue('caller_id');
if (seen[caller]) continue;
seen[caller] = true;
// do work once per unique caller
}5. Safe single-record fetch
var gr = new GlideRecord('cmdb_ci');
if (gr.get(ciSysId)) {
gs.info('Found ' + gr.getDisplayValue());
} else {
gs.warn('CI ' + ciSysId + ' not found');
}When AI Helps More Than the Docs
Most GlideRecord bugs are not about the API. They are about the data: a missing field, a deactivated user, a circular reference. Yeti AI Chat is most useful when you paste an error and ask it to explain what your query is actually returning given your instance's real schema.

SnowCoder's 60% accuracy advantage over generic LLMs across 120+ ServiceNow benchmarks comes from grounding in a 100,000+ vector ServiceNow knowledge base, plus live MCP context. Read more about the benchmark methodology or the MCP integration.
Related Reading
Stop guessing GlideRecord syntax
Describe the query you need. SnowCoder writes it against your real schema, with the right operators and ACLs.