メインコンテンツへスキップ
見出し画像

ドット・ウォーク:参照先の項目をコードで辿る


    開発ハンズオンシリーズの3回目です。①のGlideRecordで検索と更新を、②で件数の集計を動かしてきました。今回のテーマは ドット・ウォーク(dot-walk)。インシデントの「起票者の部署」や「担当グループの名前」のように、別のテーブルにある値をコードでするりと辿る書き方です。

    ServiceNow を触っていると、インシデントの caller(起票者)や assignment_group(担当グループ)のような「別のレコードを指す項目」に何度も出会います。これが参照(reference)フィールドです。この参照の先にある名前やメールを、コードで簡単に取り出せると、書けるスクリプトの幅がぐっと広がります。今回も前回までと同じ Scripts - Background で、実際に辿ってみましょう。

    この記事は、ServiceNow の PDI(Personal Developer Instance、無料の個人検証環境)Zurich リリース・OOTB(初期設定)で実機実行した記録です。コードの出力はすべて Scripts - Background で流した結果で、参照先の値は REST でも確認しました(確認日 2026-07-19)。本番環境では実行しないでください。

    参照フィールドの正体:中身は sys_id

    まず、参照フィールドが中に何を持っているのかを見ます。インシデント INC0000001 の caller_id(起票者)を、2通りの書き方で出してみましょう。

    var gr = new GlideRecord('incident');
    if (gr.get('number', 'INC0000001')) {
      gs.info('caller_id の生値(getValue) : ' + gr.getValue('caller_id'));
      gs.info('caller_id の表示(ドット)   : ' + gr.caller_id.getDisplayValue());
    }


    画像

    getValue('caller_id') が返したのは、5137…2005 という32桁の文字列でした。これが sys_id、つまり相手レコードを一意に指す内部IDです。参照フィールドが DB の中に抱えているのは、相手の名前そのものではありません。相手を一意に指す sys_id、ただそれだけ。

    一方、gr.caller_id.getDisplayValue() は Fred Luddy と、人が読める名前を返しました。同じ項目でも、生値は sys_id、表示は名前。前回 state で見た「生値と表示値の違い」が、参照フィールドでも同じように起きています。ここを押さえると、次のドット・ウォークがすっと理解できます。

    sys_id で持つ理由

    参照フィールドが名前ではなく sys_id を抱えているのには、ちゃんとした理由があります。名前は変わることがあるからです。たとえば起票者の氏名が結婚や表記ゆれで書き換わったとしても、その人の sys_id は最初に払い出されたまま動きません。もし incident 側に「Fred Luddy」という文字をそのまま持たせていたら、ユーザー側の名前を直すたびに、あちこちのインシデントを一斉に書き換えるはめになります。sys_id という不変の背番号を1つ持っておけば、名前はいつでも相手のレコードから引き直せる。この設計のおかげで、表示は常に最新、しかも重複もありません。

    だから参照フィールドを読むときは、「この項目は相手の背番号を1個握っているだけ」とイメージすると腑に落ちます。背番号から相手の名簿を引くのがドット・ウォーク。名簿そのものは sys_user 側にあり、incident はそこへの参照を1本つないでいるだけ。この関係が分かると、なぜ getValue が謎の32桁を返すのかも自然に納得できますね。

    この「不変のIDで指す」考え方は、ServiceNow のあちこちで顔を出します。ユーザーもグループも構成アイテムも、みな自分の sys_id を持っています。他のテーブルからは、その sys_id を通して指し示されるわけです。だから参照フィールドの仕組みをひとつ覚えると、incident 以外のテーブルでも同じ読み方が通用する。caller_id で通じたことが、assigned_to でも company でも、そっくり同じ手順で通じます。ひとつの型を、そのまま横に広げられるわけですね。

    ドット・ウォーク:.項目 で相手を読む

    sys_id を持っているということは、その sys_id が指す相手レコードの項目も辿れる、ということです。書き方はとてもシンプルで、gr.caller_id のうしろに .項目名 をつなぐだけ。これがドット・ウォークです。

    var gr = new GlideRecord('incident');
    if (gr.get('number', 'INC0000001')) {
      gs.info('起票者の名前     caller_id.name      : ' + gr.caller_id.name);
      gs.info('起票者のメール   caller_id.email     : ' + gr.caller_id.email);
      gs.info('起票者のユーザー名 caller_id.user_name : ' + gr.caller_id.user_name);
    }


    画像

    gr.caller_id.name が Fred Luddy、gr.caller_id.email が fred.luddy@example.com、gr.caller_id.user_name が fred.luddy。caller_id が指す先は sys_user(ユーザー)テーブルなので、そのユーザーの name・email・user_name を、まるで自分の項目のように読めました。図にすると、こういうつながりです。


    画像

    incident の caller_id は sys_id を1つ持つだけ。その sys_id が sys_user の1行を指していて、.name や .email でその行の項目に届く。「別のテーブルへ、点(ドット)でひとまたぎする」から dot-walk(ドット・ウォーク=点で歩く)と呼ばれます。わざわざ別に GlideRecord を作って sys_user を検索しなくていい。この手軽さが、ドット・ウォークのいちばんの魅力です。

    別の GlideRecord を立てずに済むのは、コード量が減るだけの話ではありません。検索を1本増やすたび、書き手はミスの余地も抱えます。条件の付け忘れ、値の取り違え、next() の書き忘れ。ドット・ウォークなら、その一連をまるごと省けます。読む人にとっても、caller_id.name の一行は「起票者の名前」と素直に読めますね。短く書けて、意図もすっと伝わる。この読みやすさが、ドット・ウォークが好まれる理由のひとつです。

    2段でも辿れる

    ドット・ウォークは1回だけとは限りません。「起票者の部署」のように、参照の先の、さらに参照まで辿れます。ユーザー(sys_user)は department(部署)という参照フィールドを持っているので、そこもつなげてみましょう。

    var gr = new GlideRecord('incident');
    if (gr.get('number', 'INC0000001')) {
      gs.info('起票者の部署 caller_id.department.name : ' + gr.caller_id.department.name);
      gs.info('担当グループ assignment_group.name     : ' + gr.assignment_group.name);
      gs.info('構成アイテム cmdb_ci.name              : ' + gr.cmdb_ci.name);
    }


    画像

    gr.caller_id.department.name が Development と出ました。incident →(caller_id)→ user →(department)→ 部署、と2回ジャンプして、その部署の名前まで届いています。ついでに assignment_group.name(担当グループ=Service Desk)や cmdb_ci.name(構成アイテム=MailServerUS)も、同じ書き方で取れました。参照フィールドなら、相手が何のテーブルでも同じ要領です。

    便利ですが、辿る段数が増えるほど、裏では相手レコードを1つずつ読みにいく回数も増えます。2段・3段とつなぐのは可読性も落ちるので、必要な範囲にとどめておくのが実務では無難です。深く辿りたくなったら、いったん相手を GlideRecord で取り直す、という選択肢もあります。

    段数が増えると内部の読み込みが増える、というのは仕組みを考えれば腑に落ちます。1段目で caller_id の sys_id から sys_user を1件読み、2段目でその department から部署を1件読む。辿るたびに、相手テーブルへの読み取りが1回ずつ足されていきます。1件を表示するだけなら気になりませんが、何千件ものループで3段・4段を多用すると、読み取りの総数がじわじわ効いてきます。深く辿りたいときは、いったん相手を GlideRecord で取り直し、そこで必要な項目をまとめて読む。この判断ができると、コードが速さの面でも素直になります。

    参照フィールドかどうかを見分ける

    ドット・ウォークが効くのは、相手が参照フィールドのときだけです。ではその項目が参照かどうかは、どこで分かるのでしょうか。いちばん手軽なのは、フォーム上での見た目です。参照フィールドは入力欄の右に虫めがねのアイコンが付き、値がリンク(下線付き)で表示されます。ふつうのテキスト項目には、このアイコンが出ません。担当者や起票者の欄を眺めると、たしかにリンクになっているのが分かります。

    もう少し確実に確かめたいなら、辞書(Dictionary)が頼りになります。System Definition の Dictionary で項目を開くと、Type という欄が目に入ります。ここが Reference なら、その項目は参照フィールド。すぐ下に参照先のテーブル名も添えられています。caller_id の参照先は sys_user、cmdb_ci の参照先は cmdb_ci(構成アイテム)テーブルです。この参照先が、ドット・ウォークで辿り着く相手そのもの。記事末尾に、参照フィールドだけを一覧するURLも置いておきました。

    見分けがつくと、コードを書く前に「この先へ辿れるか」を判断できます。テキスト項目に .name をつないでも、相手がいないので何も返りません。逆に、リンクになっている項目を見つけたら、その先の名前やメールへ手が届くサインです。ここを押さえておくと、初めて触るテーブルでも「どこまで辿れるか」の見当が早くつきますよ。

    ループの中でも使える

    ドット・ウォークは、1件取得のときだけでなく、複数件を回すループの中でもそのまま使えます。未解決のインシデントを数件、起票者と担当グループつきで並べてみましょう。

    var gr = new GlideRecord('incident');
    gr.addQuery('active', true);
    gr.orderBy('number');
    gr.setLimit(5);
    gr.query();
    while (gr.next()) {
      gs.info(gr.getValue('number') + ' | 起票: ' + gr.caller_id.name + ' | 担当G: ' + gr.assignment_group.name);
    }


    画像

    1件ずつ、番号と起票者名と担当グループが並びました。


    画像

    INC0000007 の担当グループが空になっているのに気づいたでしょうか。担当グループがまだ設定されていないインシデントで、参照先がないので空文字が返っています。ドット・ウォークは、相手がいなければ静かに空を返す。エラーで止まらないのは楽ですが、「なぜか名前が空」というときは、そもそも参照先が設定されているかを疑うのが第一歩になります。

    ループとドット・ウォークの相性がいいのは、1件ずつの処理に「相手の情報」を自然に添えられるからです。番号だけの一覧は味気ないものですが、起票者や担当グループの名前が並ぶと、ログを眺めるだけで状況が読み取れます。実務でも、夜間バッチの処理ログに「どのレコードの、誰の分を触ったか」を書き添えておくと、あとで追いかけるのがぐっと楽になります。参照先の名前をログに1つ足しておく。その小さな工夫が、後日の自分を助けてくれますよ。

    他の参照でも辿ってみる

    今回は caller_id を主役にしましたが、incident には他にも参照フィールドがいくつもあります。今日のコードで一緒に触った assignment_group.name(担当グループ=Service Desk)や cmdb_ci.name(構成アイテム=MailServerUS)も、その仲間です。やることは同じ。書き方はまったく同じで、.項目名 をつなぐだけ。相手が sys_user だろうと、グループのテーブルだろうと、構成アイテムのテーブルだろうと、辿り方は変わりません。

    自分の PDI で遊ぶなら、assigned_to(担当者)や company(会社)あたりが手ごろです。assigned_to.name で担当者の名前、assigned_to.email でそのメール。caller_id でやったことを、項目名だけ差し替えて試せます。うまく名前が出れば、その項目は参照フィールドだったという確認になりますね。空が返ってきたら、そのレコードではまだ相手が設定されていない、というサイン。手を動かすほど、参照フィールドの手ざわりが自分のものになっていきます。

    3つの読み方を並べて整理

    参照フィールドの読み方が何通りか出てきたので、いちど並べて整理しておきます。


    画像

    生値がほしいなら getValue(sys_id が返る)、表示名だけでいいなら getDisplayValue、相手の特定の項目がほしいならドット・ウォーク。目的に合わせて選びます。とくに「他システムへ sys_id を渡したい」ときは getValue、「画面やログに名前を出したい」ときはドット・ウォークか getDisplayValue、という使い分けを覚えておくと迷いません。

    迷ったら、こう考えると早いです。sys_id そのものが要るのか、名前が要るのか。名前でいいなら、特定の項目まで欲しいのか、表示名だけで足りるのか。この2段階で分ければ、getValue・getDisplayValue・ドット・ウォークのどれを選ぶかは、ほぼ自動的に決まります。3つを丸暗記するより、目的から逆にたどるほうが、実際のコードでは迷いませんよ。

    sys_id を渡すか、名前を出すか

    読み方が3通りあると、「で、結局どれを使えばいいの?」と迷いますよね。判断の軸はひとつです。その値を人が読むのか、機械が使うのか。ここで自然と分かれます。

    画面やログに出して人に見せるなら、名前がほしい。ここは名前の出番。だからドット・ウォークか getDisplayValue を選びます。別のシステムへ連携したり、あとで同じレコードを取り直したりするなら、機械が扱う sys_id のほうが確実。ここは getValue の出番です。名前は重複することもあり得ますが、sys_id は世界にひとつ。連携のキーとして渡すなら、ぶれない sys_id が向いています。

    具体例で言えば、通知メールの本文に「起票者:Fred Luddy」と載せたいなら、caller_id.getDisplayValue() で名前を取ります。外部の勤怠システムへ「この起票者のレコード」を引き渡すなら、getValue('caller_id') の sys_id をキーにする。同じ caller_id でも、渡す先が人か機械かで取り方が変わります。この線引きさえ持っておけば、3つの読み方で迷う時間はぐっと減りますよ。

    実務での使いどころ

    ドット・ウォークが手になじむと、日々のスクリプトが一段書きやすくなります。実務でよく出会う形を、いくつか挙げてみましょう。

    ひとつは通知です。「担当グループのマネージャーにメールを送る」とき、assignment_group.manager.email と辿れば、そのマネージャーのアドレスに一発で届きます。手軽ですね。わざわざグループを検索し、そこからマネージャーを検索し直す手間は要りません。参照の鎖をそのまま辿れるのが、ドット・ウォークの強みですね。

    もうひとつは絞り込みです。addQuery でも caller_id.department=xxxx のようにドット・ウォークが使えて、「起票者の部署が Development のインシデントだけ」といった条件で絞れます。前回の GlideAggregate と重ねれば、「部署ごとの件数」を出すのも同じ発想。参照の先を条件にできると、レポートの幅がぐっと広がります。

    表示に使う場面も多いです。一覧画面のカラムやレポートの軸に「起票者の部署」を出したいとき、裏で効いているのはやはりドット・ウォーク。画面のリスト設定で caller_id.department のように項目をたどると、別テーブルの値をそのまま列に並べられます。コードで書く辿り方と、画面で選ぶ辿り方は地続きです。片方が分かると、もう片方も自然と読めますよ。

    つまずきやすいところ

    ドット・ウォークで最初にとまどう点を、先回りして挙げておきます。どれも一度は通る道なので、知っておくと落ち着いて対処できます。身構えなくて平気です。

    • テキスト項目に辿ろうとする:.name などが効くのは参照フィールドだけです。ふつうの文字列項目に . をつないでも、相手がいないので何も返りません。まず「これは参照か」を確かめるのが先です。

    • 参照先が空なのを忘れる:担当グループ未設定の INC0000007 のように、参照先がなければ静かに空文字が返ります。エラーで止まらないぶん、「名前が空なのはなぜ」と悩んだら、参照そのものが入っているかを疑いましょう。

    • 段数を深くしすぎる:2段・3段と辿るほど、裏では相手レコードを読む回数が積み上がります。可読性も落ちるので、必要な段数にとどめるのが無難です。

    • sys_id と表示名を取り違える:getValue は sys_id、ドット・ウォークや getDisplayValue は名前を返します。人に見せるのか、機械に渡すのかで選び分けましょう。

    値を二重に確かめた

    本文の値は、Scripts - Background の出力だけでなく、REST でも読み直して突き合わせました。caller_id.name が Fred Luddy、caller_id.email が fred.luddy@example.com、caller_id.department.name が Development。この3つは、sys_user 側を直接引いた結果ともぴったり一致しています。ドット・ウォークが指す先が同じレコードだと確かめたうえで載せているので、安心して手元の PDI と見比べてください。

    コードの結果を、ひとつの経路だけで信じないようにしています。地味ですが、大事な習慣です。手は抜きません。ドット・ウォークは「辿った先の値」を返すので、途中の参照が正しくつながっているかを、別の入口からも確かめると心強い。今回は REST で同じ値が返ったので、辿り方も参照先も間違っていないと言い切れます。実機で二度確かめる。この一手間が、記事の数字を裏づけています。

    まとめ

    参照フィールドの中身は、相手の名前ではなく sys_id でした。その sys_id をたどって相手の項目を読むのが ドット・ウォークで、gr.caller_id.name のように .項目 をつなぐだけ。caller_id.department.name のように2段でも辿れて、ループの中でも同じように使えます。参照先が無ければ空が返る、という素直な挙動も確認できました。

    このドット・ウォークを覚えると、「起票者の部署ごとに集計する」「担当グループのマネージャーに通知する」といった、テーブルをまたぐ処理がぐっと書きやすくなります。前回の GlideAggregate と組み合わせれば、「部署ごとの件数」だって出せます。ぜひ自分の PDI で、caller_id 以外の参照(assigned_to や company など)も辿って遊んでみてください。

    次回は、②でも少し触れた エンコードクエリをもっと掘り下げます。^OR(または)・LIKE(部分一致)・ISEMPTY(空)といった演算子を、実機で件数を出しながら読み解いていきます。画面のフィルタとコードをつなぐ、実用度の高い回になる予定です。

    その他、実機で確認した記録

    用語

    • 参照(reference)フィールド … 別テーブルのレコードを指す項目(中身は相手の sys_id)

    • sys_id … レコードを一意に識別する32桁のID

    • ドット・ウォーク … gr.caller_id.name のように .項目 で参照先を辿る書き方

    • getValue(参照) … 生値=相手の sys_id

    • 参照.getDisplayValue() … 参照先の表示名

    • 2段ドット・ウォーク … caller_id.department.name のように参照の先の参照まで辿る

    実行に使ったスクリプト

    本文のコードは、そのまま Scripts - Background に貼れば動きます(実行は必ず PDI で)。出力はすべて筆者の PDI(dev / Zurich / OOTB)で流した実画面です。

    調べるときの入口URL(先頭に自分のインスタンスを付ける)

    • /sys.scripts.do … Scripts - Background(今回コードを流した画面)

    • /incident.do?sysparm_query=number=INC0000001 … 今回辿ったインシデント

    • /sys_user.do?sysparm_query=user_name=fred.luddy … 参照先の起票者レコード

    • /sys_dictionary_list.do?sysparm_query=name=incident^internal_type=reference … incident の参照フィールド一覧

    実測の要点(再掲)

    • caller_id の生値 = sys_id(5137…2005) / 表示 = Fred Luddy

    • caller_id.name = Fred Luddy / .email = fred.luddy@example.com / .user_name = fred.luddy

    • caller_id.department.name = Development(2段) / assignment_group.name = Service Desk / cmdb_ci.name = MailServerUS

    • ループ5件で起票者・担当グループを取得(INC0000007 は担当グループ空)

    検証範囲とおことわり

    • 実行は PDI(Zurich / OOTB)の Scripts - Background・scope=global のみ。値は OOTB のデモデータに基づきます。

    • ドット・ウォークは辿るたびに相手レコードを読むため、巨大なループでの多用は性能に影響します(詳細は範囲外)。

    • 参照先が未設定のときは空文字が返る点のみ確認し、null 挙動の細部は範囲外とします。

    参考リンク

     
     
    ServiceNowのPDI(Personal Developer Instance)を実機で観察して、Noteに記事を書いています 実装・運用・教育の現場視点で淡々と記録中です リクエストがあればその記事も書きます 個人の観察日記です

    あなたへのおすすめ