【ServiceNow】GlideRecordで取得した要素を配列に追加する際に気をつけたいこと

# 1. はじめに
## 1-1. GlideRecordとは？
ServiceNowはノーコード/ローコードプラットフォームです。

コードを書かずに様々な機能を実装できる一方、コードを書いてもできないことが沢山あります。

例えば具体的には、データベースの操作にSQLが使えません。

え、じゃあどうするの？というとGlideRecordという機能が用意されています。

GlideRecordというクラスからテーブルのオブジェクト（GlideRecord object）を生成して、GlideRecord APIと呼ばれるメソッド群を用いて操作します。

```javascript
// テーブルのオブジェクトを生成して
var gr = new GlideRecord('table_name');
// クエリを書いて
gr.addQuery('field_name', 'operator',  'value');
// クエリを実行して
gr.query();
// クエリの実行結果について
while(gr.next()){
    // 処理を書く
    gs.info(gr.field_name);
}
```

というような感じでスクリプトを記述します。

ServiceNowやGlideRecord API固有のメソッドが沢山出てきて、独特な感じですね。

:::note info
GlideRecordの公式Docは[こちら](https://docs.servicenow.com/bundle/tokyo-application-development/page/app-store/dev_portal/API_reference/GlideRecord/concept/c_GlideRecordAPI.html)
:::

:::note info
ServiceNowで使用できる標準的なスクリプトはJavaScript(ES5)です。
:::

## 1-2. GlideRecordで取得した要素を配列に入れたい
業務でServiceNowを触っていると、「GlideRecordを実行して取得した要素を配列に入れたい」という場面によく遭遇します。

特に意識せず`array.push(value)`と書いても、何も問題ないことも多いです。

が、時折うまくいかないことがあります。

GlideRecord objectを配列に入れる上でよく躓くポイントを、以下に実例を交えながら紹介します。

# 2. 実例
## 2-0. 結論
GlideRecord objectを配列に追加する目的でArray.push()メソッドを使用する際は、**String** として渡す必要があります。

## 2-1. 実行環境
ServiceNowで使用できるスクリプトは、Server sideとClient sideの2つに大別されます。

Admin権限を持っていると使えるScripts - Backgroundという機能で、簡単にServer sideのスクリプトを実行できます。

今回はこれを使って、うまくいく例、うまくいかない例と対処方法の具体例を見ていきましょう。

::: note info
Scripts - Backgroundの公式Docは[こちら](https://docs.servicenow.com/en-US/bundle/tokyo-application-development/page/script/server-scripting/concept/c_ScriptsBackground.html)
:::

:::note info
使用するServiceNowのバージョンはTokyoです
:::

## 2-2. うまくいく例
まずは配列を使わず、実行結果をそのまま出力してみましょう。

「incident」というテーブルからcategory=hardwareとなるレコードを取得し、取得したレコードのuser_nameフィールドの値を出力する例です。
```javascript
var gr = new GlideRecord('incident');
gr.addQuery('category', 'hardware');
gr.orderBy('number');
gr.query();
while(gr.next()){
    gs.info(gr.caller_id.user_name);
}
```
実行結果は…
```text
x_880928_my_cust_0: alejandro.mascall
x_880928_my_cust_0: bow.ruggeri
x_880928_my_cust_0: don.goodliffe
x_880928_my_cust_0: rick.berzle
x_880928_my_cust_0: rick.berzle
x_880928_my_cust_0: bow.ruggeri
x_880928_my_cust_0: jerrod.bennett
x_880928_my_cust_0: beth.anglin
x_880928_my_cust_0: David.Miller
x_880928_my_cust_0: David.Miller
```

ちゃんと出力されましたね。

次に、同じクエリを実行して、実行結果を配列に入れてから出力してみましょう。
```javascript
var gr = new GlideRecord('incident');
gr.addQuery('category', 'hardware');
gr.orderBy('number');
gr.query();
var userNames = [];
while(gr.next()){
    userNames.push(gr.caller_id.user_name);
}
for (i = 0; i < userNames.length; i++) {
    gs.info(userNames[i]);
}
```

実行結果は…
```text
x_880928_my_cust_0: alejandro.mascall
x_880928_my_cust_0: bow.ruggeri
x_880928_my_cust_0: don.goodliffe
x_880928_my_cust_0: rick.berzle
x_880928_my_cust_0: rick.berzle
x_880928_my_cust_0: bow.ruggeri
x_880928_my_cust_0: jerrod.bennett
x_880928_my_cust_0: beth.anglin
x_880928_my_cust_0: David.Miller
x_880928_my_cust_0: David.Miller
```
上記と同じですね。なんの問題もありません。

## 2-3. うまくいかない例
先程のスクリプトのクエリは変えず、取得するフィールドを変えてみましょう。

テーブルとクエリは同じで、priorityというフィールドの値を出力します。

まずは配列に入れない例です。
```javascript
var gr = new GlideRecord('incident');
gr.addQuery('category', 'hardware');
gr.orderBy('number');
gr.query();
while(gr.next()){
    gs.info(gr.priority);
}
```
以下が実行結果です。
```text
x_880928_my_cust_0: 1
x_880928_my_cust_0: 1
x_880928_my_cust_0: 1
x_880928_my_cust_0: 5
x_880928_my_cust_0: 5
x_880928_my_cust_0: 3
x_880928_my_cust_0: 1
x_880928_my_cust_0: 5
x_880928_my_cust_0: 1
x_880928_my_cust_0: 3
```

次に、配列に入れる例です。
```javascript
var gr = new GlideRecord('incident');
gr.addQuery('category', 'hardware');
gr.orderBy('number');
gr.query();
var priorities = [];
while(gr.next()){
    priorities.push(gr.priority);
}
for (i = 0; i < priorities.length; i++) {
    gs.info(priorities[i]);
}
```
以下が実行結果です。
```text
x_880928_my_cust_0: 3
x_880928_my_cust_0: 3
x_880928_my_cust_0: 3
x_880928_my_cust_0: 3
x_880928_my_cust_0: 3
x_880928_my_cust_0: 3
x_880928_my_cust_0: 3
x_880928_my_cust_0: 3
x_880928_my_cust_0: 3
x_880928_my_cust_0: 3
```

様子がおかしいですね…

配列に最後にpushされた要素に、他のすべての要素が上書きされています。

## 2-4. うまくいかない理由
「うまくいく例」で出力した`caller_id.user_name`はString型のフィールドです。

「うまくいかない例」で出力した`priority`はInteger型のフィールドです。

どうやらString型のフィールドでないとちゃんとpushされない…？

他の型も試してみましょう。

まずはBoolean型です。

配列に入れない場合
```text
x_880928_my_cust_0: false
x_880928_my_cust_0: true
x_880928_my_cust_0: true
x_880928_my_cust_0: false
x_880928_my_cust_0: false
x_880928_my_cust_0: true
x_880928_my_cust_0: true
x_880928_my_cust_0: false
x_880928_my_cust_0: true
x_880928_my_cust_0: false
```

配列に入れた場合
```text
x_880928_my_cust_0: false
x_880928_my_cust_0: false
x_880928_my_cust_0: false
x_880928_my_cust_0: false
x_880928_my_cust_0: false
x_880928_my_cust_0: false
x_880928_my_cust_0: false
x_880928_my_cust_0: false
x_880928_my_cust_0: false
x_880928_my_cust_0: false
```

ダメですね。

次にServiceNow固有の型も試してみます。

Date/Time型という、文字通り日付+時刻の型です。

配列に入れない場合
```text
x_880928_my_cust_0: 2021-01-19 15:14:01
x_880928_my_cust_0: 2022-06-18 23:41:15
x_880928_my_cust_0: 2022-06-18 23:55:07
x_880928_my_cust_0: 2022-06-29 23:56:03
x_880928_my_cust_0: 2022-06-30 00:02:01
x_880928_my_cust_0: 2022-06-19 00:45:27
x_880928_my_cust_0: 2022-08-26 18:05:40
x_880928_my_cust_0: 2022-09-12 09:43:10
x_880928_my_cust_0: 2018-10-17 05:47:45
x_880928_my_cust_0: 2018-09-16 12:50:05
```

配列に入れた場合
```text
x_880928_my_cust_0: 2018-09-16 12:50:05
x_880928_my_cust_0: 2018-09-16 12:50:05
x_880928_my_cust_0: 2018-09-16 12:50:05
x_880928_my_cust_0: 2018-09-16 12:50:05
x_880928_my_cust_0: 2018-09-16 12:50:05
x_880928_my_cust_0: 2018-09-16 12:50:05
x_880928_my_cust_0: 2018-09-16 12:50:05
x_880928_my_cust_0: 2018-09-16 12:50:05
x_880928_my_cust_0: 2018-09-16 12:50:05
x_880928_my_cust_0: 2018-09-16 12:50:05
```

やはりダメです。

公式のドキュメントでは記載を見つけられませんでしたが、GlideRecord objectをArray.push()で配列に入れたい場合、String型でないと失敗するようです。

# 3. 対処方法
String型はOKで他の型はNGだったので、String型に型変換してスクリプトを実行してみましょう。

## 3-1. toString()
まずは素直にJavaScriptのtoString()メソッドを使ってみましょう。

```javascript
var gr = new GlideRecord('incident');
gr.addQuery('category', 'hardware');
gr.orderBy('number');
gr.query();
var priorities = [];
while(gr.next()){
    priorities.push(gr.priority.toString());
}
for (i = 0; i < priorities.length; i++) {
    gs.info(priorities[i]);
}
```
以下が実行結果です。
```text
x_880928_my_cust_0: 1
x_880928_my_cust_0: 1
x_880928_my_cust_0: 1
x_880928_my_cust_0: 5
x_880928_my_cust_0: 5
x_880928_my_cust_0: 3
x_880928_my_cust_0: 1
x_880928_my_cust_0: 5
x_880928_my_cust_0: 1
x_880928_my_cust_0: 3
```
ちゃんと出力されました。

## 3-2. getValue()
GlideRecord APIのgetValue()というメソッドがあります。

戻り値がStringになるようなので、使ってみましょう。

```javascript
var gr = new GlideRecord('incident');
gr.addQuery('category', 'hardware');
gr.orderBy('number');
gr.query();
var priorities = [];
while(gr.next()){
    priorities.push(gr.getValue('priority'));
}
for (i = 0; i < priorities.length; i++) {
    gs.info(priorities[i]);
}
```
以下が実行結果です。
```text
x_880928_my_cust_0: 1
x_880928_my_cust_0: 1
x_880928_my_cust_0: 1
x_880928_my_cust_0: 5
x_880928_my_cust_0: 5
x_880928_my_cust_0: 3
x_880928_my_cust_0: 1
x_880928_my_cust_0: 5
x_880928_my_cust_0: 1
x_880928_my_cust_0: 3
```
こちらもちゃんと出力されました。

## 3-3. 空文字の結合
念の為、メソッドを使わずにString型に型変換する方法も試してみましょう。

空文字を結合してみます。
```javascript
var gr = new GlideRecord('incident');
gr.addQuery('category', 'hardware');
gr.orderBy('number');
gr.query();
var priorities = [];
while(gr.next()){
    priorities.push(gr.priority + '');
}
for (i = 0; i < priorities.length; i++) {
    gs.info(priorities[i]);
}
```
以下が実行結果です。
```text
x_880928_my_cust_0: 1
x_880928_my_cust_0: 1
x_880928_my_cust_0: 1
x_880928_my_cust_0: 5
x_880928_my_cust_0: 5
x_880928_my_cust_0: 3
x_880928_my_cust_0: 1
x_880928_my_cust_0: 5
x_880928_my_cust_0: 1
x_880928_my_cust_0: 3
```
大丈夫ですね。

# 4. まとめ
というわけで、GlideRecordで取得した要素を配列にpushする際はString型に型変換しよう！という記事でした。

ServiceNowを触り始めて、GlideRecord便利だなーと思って色々いじっていると、多くの人が遭遇する問題だと思います。

GlideRecordに限らず、ServiceNow側で用意されているスクリプト関連の機能は一癖あるものが多いです。

ただ上記のように簡単に解決できる場合も多いので、今後も「ServiceNowのコツ」的なトピックを見つけたら記事にしていきたいと思います。

皆様のお力になれましたら幸いです。