drjohnchun
Tera Guru

GlideFilter is used to determine if a GlideRecord meets the conditions specified in an encoded query string. For instance, it's used in the business rule condition builder to determine under which conditions the business rule must run. It's not well documented and its usage is slightly different between global and scoped apps, so there seems to be some confusion, especially on how it's used with regular expressions. I'll share what I know here and update this based on your feedback/additions/corrections.

The only official documentation available is Scoped GlideFilter API Reference. However, its variation is available in the global scope as well. Let's take a look at how they're used and how they're different (as of the Helsinki release).

SCOPED GlideFilter

Scoped GlideFilter is an object that is used without having to instantiate. It has only one method:

boolean checkRecord(GlideRecord gr, string filter, boolean matchAll)   // returns true if gr meets the conditions specified in filter

gr: a single GlideRecord

filter: an encoded query string

matchAll: (Optional) if true (default), all conditions in filter must match to return true; if false, any single true condition returns true (see below)

Here's an example:

var gr = new GlideRecord('incident');

gr.query();

var filter = 'active=true^state=2';   // active && state == 'In Progress'

while (gr.next()) {   // iterate through records

  if (GlideFilter.checkRecord(gr, filter)) gs.info(gr.number);   // test each record for the filter conditions

}

This returns all incident records that are active and the state is 'In Progress'. Some points to note are:

  1. GlideFilter is not instantiated (no need for new GlideFilter()).
  2. GlideFiter.checkRecord() is applied to each record after gr.query() is executed. In other words, GlideFilter is not used by gr.query().

USING MATCH_RGX FOR REGULAR EXPRESSION MATCH

GlideFilter also allows the use of regular expressions in the filter using the MATCH_RGX operator. Here's an example, modified from the previous one:

var gr = new GlideRecord('incident');

gr.query();

var filter = 'active=true^state=2^numberMATCH_RGXINC.*';   // active && state == 'In Progress' && /^INC.*$/m.test(number)

while (gr.next()) {   // iterate through records

  if (GlideFilter.checkRecord(gr, filter)) gs.info(gr.number);   // test each record for the filter conditions

}

This returns all incident records that are active, the state is 'In Progress' and the number starts with 'INC' (since all incident numbers start with 'INC', this doesn't really do anything). What's not well known and often a source of confusion is that MATCH_RGX already includes /^ and $/m, the start and end of string in multiline mode; the regular expression filter condition specified, therefore, is what comes between /^ and $/m. So

fieldMATCH_RGXcondition

is equivalent to, in JavaScript,

/^condition$/m.test(field)

Also, the test is case sensitive (we'll see below that there's a way to make this case insensitive in the global scope but not in scoped GlideFilter). Some examples and their JavaScript equivalents are

fieldMATCH_RGXabc === /^abc$/m.test(field)   // field exactly matches "abc"

fieldMATCH_RGXabc.* === /^abc.*$/m.test(field)   // field starts with "abc"

fieldMATCH_RGX.*abc === /^.*abc$/m.test(field)   // field ends with "abc"

fieldMATCH_RGX.*abc.* === /^.*abc.*$/m.test(field)   // field contains "abc"

It's worthwhile noting that MATCH_RGX is not available in GlideRecord's .addEncodedQuery() method (I wish it was); this indicates there are different flavors of encoded queries in ServiceNow.

GLOBAL GlideFilter

The examples used above for Scoped GlideFilter also work in the global scope. In addition, Global GlideFilter provides the following features:

new GlideFilter(string filter, string title)

filter: an encoded query string

title: title of the filter

boolean match(GlideRecord gr, boolean matchAll)   // returns true if gr meets the conditions specified in filter from GlideFilter instance

gr: a single GlideRecord

matchAll: if true, all conditions in filter must match to return true; if false, any single true condition returns true (see below). This is not optional, unlike in .checkRecord().

void setCaseSensitive(boolean caseSensitivity)   // sets whether .match() is case sensitive (does not apply to .checkRecord())

caseSensitivity: if true, .match() is case sensitive (does not apply to .checkRecord()).

Here are some additional properties and methods:

boolean caseSensitive   // true if .match() is case sensitive

string filter   // encoded query string of the filter

string getFilter()   // returns encoded query string of the filter

string title   // title of the filter

string getTitle()   // returns the title of the filter

void setDisplayTitle(String displayTitle)   // sets the display title of the filter

string getDisplayTitle()   // returns the display title of the filter (returns title if display title not set)

string script   // a string representing the source code of the JavaScript function for evaluating the filter conditions

Here's an example:

var gr = new GlideRecord('incident');

gr.query();

var filter = 'active=true^state=2^numberMATCH_RGXinc.*';   // active && state == 'In Progress' && /^inc.*$/i.test(number)

var gf = new GlideFilter(filter, '');

gf.setCaseSensitive(false);

while (gr.next()) {   // iterate through records

  if (gf.match(gr, true)) gs.info(gr.number);   // test each record for the filter conditions

}

Just like what we saw earlier, this returns all incident records that are active, the state is 'In Progress' and the number starts with 'inc' (case insensitive). .match() works similarly to .checkRecord(), but with an added benefit of evaluating case-sensitive or insensitive matches.

matchAll FLAG

The .script property, which shows the JavaScript function that evaluates the filter conditions, gives a clue on how the matchAll flag works. Using the above example:

matchAll = true

function trecord() {return !!(current.active == true && current.state.toString().toLowerCase() == 2 && RegExp('^inc.*$','mi').test(current.number.toString()));}trecord();

matchAll = false (effectively all conditions become OR conditions)

function trecord() {if (current.active == true ) return true;if (current.state == 2 ) return true;if (RegExp('^inc.*$','m').test(current.number.toString()) ) return true; return false;}trecord();

SUMMARY

Here's a summary of how GlideFilter is used in both global and scoped apps:

  1. GlideFilter is used to test encoded query filter conditions one record at a time; it can't be used to filter a bulk record set.
  2. GlideFilter supports regular expression matches using the MATCH_RGX operator: fieldMATCH_RGXcondition is equivalent to /^condition$/m.test(field) in JavaScript, with the /^ and $/m automatically added.
  3. In scoped apps,
    • GlideFilter is an object that is used without having to instantiate.
    • GlideFilter has only one method: .checkRecord().
  4. In global apps,
    • GlideFilter can be used the same way as in scoped apps.
    • GlideFilter can be instantiated, which gives two main methods: .match(), .setCaseSensitive().
  5. Encoded queries in GlideFilter support MATCH_RGX but those used in GlideRecord's .addEncodedQuery() don't.

HELPFUL LINKS

UPDATES

2016-11-21 corrected matchAll parameter and added .script example output; corrected title parameter; added multiline flag to regular expressions; added additional properties and methods for GlideFilter().

Please feel free to connect, follow, post feedback / questions / comments, share, like, bookmark, endorse.

John Chun, PhD PMP see John's LinkedIn profile

visit snowaid

ServiceNow Advocate
4 Comments