ServiceNow Fundamentals for the Average Joe ‐ GlideRecords 101 - jcmings/sn GitHub Wiki

Previous post in the series: Business Rules


What are GlideRecords?

GlideRecords are a window into another record. GlideRecords are how we can use the same filters we'd apply to a list view, but in code form. We can use GlideRecords to do more than just peek through the window to a record -- we can use them to open the door.

GlideRecords are a slightly more technical topic than the rest of this course, but I think they are important to understand. They are often used in Business Rules, which we just covered in the previous post of this series. At some point, you may need to "read" through a business rule's script -- by the end of this post, you should be comfortable enough with reading and understanding what GlideRecord code does. Eventually, you may even find yourself writing simple GlideRecord queries.

Opening the window

In this section we'll talk about how GlideRecords can be used to find records. In the next section, Opening the door, we'll talk about how we can take actions on those records.

Initializing a GlideRecord, with examples

GlideRecords are initialized in code. This can be in a business rule, fix script, script include, widget, or something else. GlideRecords run on the server (rather than in the user's browser, which would be on the client), which means they have direct access to the database and can perform operations on the database. This is perfect, since we really only use GlideRecords to look up and modify records in the database.

To initialize a GlideRecord, we start with a simple line of code:

var gr = new GlideRecord('table_id_here');

This line tells ServiceNow that we want to open a window into the table table_id_here. We are using var gr to allow us to use gr to access this window in subsequent lines of code. This will all make more sense as you read more.

I want to quickly call out that I'm using gr as my variable name. You may see code or docs online that do the same. I typically only use gr when I'm doing a super simple GlideRecord query, since gr isn't a descriptive variable name. If my query were more complex, or if I were querying into multiple tables, I would use a more descriptive variable name, (e.g. user in var user = new GlideRecord('sys_user'). It is best practice to use descriptive names. For the remainder of the post and explanation, I'll follow best practice and actually use those descriptive names.

Let's write a few more lines of code, and then break down what they all do. For the purposes of this example, I want to find all users in the system with the name "Jackson Cummings" and output what departments they occupy:

[1] var user = new GlideRecord('sys_user');
[2] user.addQuery('first_name', 'Jackson');
[3] user.addEncodedQuery('last_name=Cummings');
[4] user.query();
[5] while (user.next()) {
[6] gs.info(user.department);
[7] }
  1. So as mentioned, we initialized our variable user and specified that any time we user user. we are pointing towards that sys_user GlideRecord.
  2. Next, we've used addQuery('first_name', 'Jackson') to apply our first filter. This is going to add an "equal to" (or "is") filter; we only want users with the first_name of Jackson.
  3. We've added another query. This one is an encoded query. It's doing the same thing as our query in line #2, but just in a different way, and for last_name this time.
  4. This is a super important line of code that actually makes the query work. You need to tell the system to actually apply the filter. This line does that.
  5. We're now going to iterate through all of the User [sys_user] records that are returned after our filter was applied. If we were only looking for one record, we'd use if (user.next()) { instead of while. But in this example, I want to find all Jackson Cummings', and not just the first one that the system returns.
  6. We log to the system the department field of the User [sys_user] record. We know that the Department of the user is stored in the field with the value department, just as we know their First Name is stored in first_name and Last in last_name. So we're directly accessing that field to list all of the departments associated with any Jackson Cummings.
  7. Closing out our while loop here.

A note on addEncodedQuery vs addQuery (lines 2 and 3 of the code)

Both addEncodedQuery and addQuery achieve the same thing: they add a "filter" onto our search. My preference is to use addEncodedQuery for a few reasons:

  • It's more visually intuitive to me... I can see the types of comparison's I'm doing (like =, >, etc) in front of me. While I could add these to the addQuery, I don't remember all the operators off the top of my head
  • Encoded queries can store multiple filters at once; alternatively, you would have to do multiple addQuerys for each filter you wanted to append
  • You can build the query right on your table view, and then right-click the breadcrumbs to copy it:

image

So if we wanted to make our code above more efficient, using an encoded query, we could just update it to this:

[1] var user = new GlideRecord('sys_user');
[2] user.addEncodedQuery('first_name=Jackson^last_name=Cummings');
[3] user.query();
[4] while (user.next()) {
[5] gs.info(user.department);
[6] }

Now, line 2 says first_name is Jackson AND (^) last_name is Cummings. All in one line.

A note on while vs if (line 5 of the code)

Sometimes, you only need to return one record in your GlideRecord. If this is the case, you can use if. But sometimes, you want to loop through all of the records that are returned and take action on them. In this case, you would use while.

An example of only returning one record would be if you know exactly what record you're trying to look up. For example, if I knew I wanted to find the Jackson Cummings in the Sales department, I could add a query (either encoded or normal) with Jackson's User [sys_user] sys_id and just use an if statement. Or -- I could add another query to add in the Sales department (and still use the if statement).

But in the example above, I don't know how many Jackson Cummings' there are, and I don't know which departments they occupy. So I'm just listing out all of their departments before I move on.

If this is confusing, it will all make more sense when we get into the Opening the door section below.

Before we move on

There are a ton of different and more complex functions that are supported with GlideRecords. Check out the ServiceNow docs for a longer list.

Opening the door

We found our Jackson Cummings' above in the Opening the window section. Up there, we just took a peek at the records that were returned. We didn't actually take any action. In this section, we will. But first...

We've realized that we only want the Jackson in the Sales department, so we've updated our code to look like this (note that the sys_id of the department field is passed through after I copied my encoded query from the breadcrumbs):

[1] var user = new GlideRecord('sys_user');
[2] user.addEncodedQuery('first_name=Jackson^last_name=Cummings^department=221db0edc611228401760aec06c9d929');
[3] user.query();
[4] while (user.next()) {
[5] gs.info(user.department);
[6] }

But now what? We've opened the window, and it's time to open the door. We want to make changes to this record. So let's hop into a real-world use case below.

Real-world use case and example

For this use case, we want to update the Title field (on the User [sys_user] table) for any Jackson Cummings' in the Sales department. We'll set the Title of these users to "Sales Analyst." So let's update our code:

[1] var user = new GlideRecord('sys_user');
[2] user.addEncodedQuery('first_name=Jackson^last_name=Cummings^department=221db0edc611228401760aec06c9d929');
[3] user.query();
[4] while (user.next()) {
[5] user.setValue('title', 'Sales Analyst');
[6] user.update();
[7] }

Instead of doing a gs.info to log the department of our Jackson's, we're now using a setValue function to change data stored in the title field on line 5. And then on line 6, we're calling a function to actually save our changes: update(). This, like the query() function in line 3, is often the first place to look if you're GlideRecord query isn't working. If you can't find any records, or your records aren't updating, chances are you missed one of these lines (or your queries are broken).

At this point, you're probably reading this thinking... well why would I code this if I could just do it manually? Wouldn't it be quicker to update Jackson Cummings in the Sales department's Title manually?

Well, the answer is yes. For that particular use case. But what if we removed the last name? What if we wanted to update the titles of all Jackson's in the Sales department? Or what if we wanted to update the titles of all users of the Sales department? Or what if we wanted to update the titles of all users, dependent on what department they are in? Hopefully now, you can see the use case for a GlideRecord query. It allows you to access a ton of data and take action on it, all with a few simple lines of code.

Another real-world use case and example

In this example, we'll paint a real-world scenario: your client comes to you and says something along the lines of...

My Sales staff are all locked out of their email. Everyone has opened tickets to get access restored, but the help desk hasn't responded. Many of them have been waiting 3 or more days for help. We need to flag these cases for the help desk so our Sales staff can check their emails.

Ok, so... the first thing we'll do is take a look at the tickets. For some reason, they've been set to Low priority. Since the help desk is busy, they have to knock out cases in priority order. So let's update all of these cases set them to a Critical priority, so they appear at the top of the help desk queue.

So how can we do this in a GlideRecord? Let's write some code and find out:

[1] var inc = new GlideRecord('incident');
[2] inc.addEncodedQuery('caller_id.department=221db0edc611228401760aec06c9d929^problem_id=051e8f6cc0a8016600cdf7fd19e10414');
[3] inc.query();
[4] while (inc.next()) {
[5] inc.setValue('priority', '1');
[6] inc.work_notes = 'Priority updated per request from The Boss';
[7] inc.update();
[8] }

This should look pretty familiar to you by now. We've initialized our GlideRecord, added our query, and queried. Inside of our while loop, we're setting the priority field's value to 1 (critical) and also posting a work note to explain the priority change. Then, we update the cases.

But what aren't we considering here? Well, for starters, we aren't considering cases that are already closed or cancelled... meaning that those cases will be impacted by this change too. Since we didn't add a filter to our query to prevent closed/cancelled cases (or in-progress cases, for that matter) from being updated, they will get these priority + work note updates as well. I bring this up because I want to highlight the importance of validating that your GlideRecord query actually meets your business need.

Using variables in your queries

You can use variables to make your code super dynamic. This is useful when you're using a business rule and want to plug in a relevant input. Or maybe you want to swap in-and-out a condition.

How I like to approach writing my GlideRecord queries

I like to use pseudo-code (basically just outline what I want my code to do in comments) before I write my code. So for the example above, I may start with this:

// look up all incidents associated with Problem ABC with a caller in Sales dept
// for each of those records, update the priority to 1
// and post a work note for auditability
// and then update those incidents

And it eventually becomes this:

// look up all incidents associated with Problem ABC with a caller in Sales dept
var inc = new GlideRecord('incident');
inc.addEncodedQuery('caller_id.department=221db0edc611228401760aec06c9d929^problem_id=051e8f6cc0a8016600cdf7fd19e10414');
inc.query();
// for each of those records, update the priority to 1
while (inc.next()) {
inc.setValue('priority', '1');
// and post a work note for auditability
inc.work_notes = 'Priority updated per request from The Boss';
// and then update those incidents
inc.update();
}

This way, not only are you helping yourself "check off" all the things that need to be done, but a future developer can easily trace your steps.


Next post in series: Update Sets