query-generation について
query-generation は、自然言語のデータ要件をServiceNowのエンコードクエリ、GlideRecord/GlideAggregateスクリプト、レポート条件に変換するスキルです。レコード検索やレポート設定を行うユーザー向けで、日付条件やAND/OR、関連テーブル項目を扱います。
主な機能
- 01自然言語からのエンコードクエリ生成
- 02GlideRecordクエリスクリプト
- 03GlideAggregateによる集計
- 04レポート条件の設定
- 05日付と相対時刻のフィルター
- 06AND/ORとドットウォーククエリ
活用シーン
- 01自然言語の要件によるインシデント一覧の絞り込み
- 02カテゴリや優先度ごとのレコード集計
- 03ServiceNowレポート条件の設定
- 04日付範囲と関連項目を使ったクエリ作成
MCP 対応
MCPツールのSN-Create-RecordでServiceNowレポートを作成する方法を説明しています。MCP経由でServiceNowデータを検索する例も含みます。
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ happy-technologies-llc/ happy-platform-skills --skill query-generationインストール後、Claude Code で/query-generationを入力して読み込まれたか確認します。
詳細
自然言語からServiceNowクエリを生成する
概要
このスキルでは、自然言語による説明をServiceNowのクエリ、スクリプト、レポート設定に変換します。
- 平易な英語の要件をエンコードクエリに変換する
- 適切なフィルター演算子を使ってGlideRecordクエリを作成する
- データ集計用のGlideAggregateスクリプトを生成する
- ダッシュボード用のレポートフィルター条件を作成する
- AND/ORロジックを使った複雑な複数条件クエリを変換する
- 日付ベース、相対時間、動的クエリを生成する
使用するタイミング: ユーザーが自然言語でデータ要件を説明するとき、レポートフィルターを作成するとき、スクリプトによるデータ取得を実装するとき、またはリストやモジュール向けの複雑なエンコードクエリを構築するときに使用します。
前提条件
- ロール:
itil、report_user、admin、または対象テーブルへの読み取りアクセス権を持つ任意のロール - アクセス: 対象データテーブル、フィールド検証用のsys_dictionary、レポート作成用のsys_report
- 知識: ServiceNowのエンコードクエリ構文、GlideRecord API、GlideAggregate API、データ型
- 関連スキル: ダッシュボードを作成するには、
reporting/analytics-generationを完了してください
手順
ステップ1:エンコードクエリの構文を理解する
ServiceNowのエンコードクエリでは、レコードのフィルタリングに特定の構文を使用します。
演算子のリファレンス:
| 演算子 | 構文 | 例 |
|---|---|---|
| 等しい | = | priority=1 |
| 等しくない | != | state!=6 |
| 含む | LIKE | short_descriptionLIKEnetwork |
| 含まない | NOT LIKE | short_descriptionNOT LIKEtest |
| で始まる | STARTSWITH | numberSTARTSWITHINC |
| で終わる | ENDSWITH | [email protected] |
| 空である | ISEMPTY | assignment_groupISEMPTY |
| 空ではない | ISNOTEMPTY | resolved_atISNOTEMPTY |
| より大きい | > | priority>2 |
| より小さい | < | priority<3 |
| 以上 | >= | sys_created_on>=2025-01-01 |
| 以下 | <= | sys_created_on<=2025-12-31 |
| リスト内 | IN | state IN 1,2,3 |
| リスト外 | NOT IN | priority NOT IN 4,5 |
| 範囲内 | BETWEEN | sys_created_onBETWEEN2025-01-01@2025-06-30 |
| インスタンス | INSTANCEOF | sys_class_nameINSTANCEOFtask |
| AND | ^ | active=true^priority=1 |
| OR | ^OR | priority=1^ORpriority=2 |
| 新しいクエリ(ORグループ) | ^NQ | priority=1^NQstate=6 |
日付演算子:
| 関数 | 構文 | 意味 |
|---|---|---|
| 今日 | javascript:gs.daysAgo(0) | 今日 |
| 過去7日間 | javascript:gs.daysAgo(7) | 過去1週間 |
| 過去30日間 | javascript:gs.daysAgo(30) | 過去1か月 |
| 今月の開始日 | javascript:gs.beginningOfThisMonth() | 今月の初日 |
| 今四半期の開始日 | javascript:gs.beginningOfThisQuarter() | 今四半期の初日 |
| 今年の開始日 | javascript:gs.beginningOfThisYear() | 今年の初日 |
| 先月の開始日 | javascript:gs.beginningOfLastMonth() | 前月の初日 |
| 先月の最終日 | javascript:gs.endOfLastMonth() | 前月の最終日 |
ステップ2:自然言語をエンコードクエリに変換する
自然言語からクエリへの変換例:
| 自然言語 | エンコードクエリ |
|---|---|
| 「すべての未解決の重大インシデント」 | active=true^priority=1 |
| 「今週オープンした、サービスデスクに割り当てられているインシデント」 | assignment_group.name=Service Desk^sys_created_on>=javascript:gs.daysAgo(7) |
| 「未解決のP1またはP2インシデント」 | active=true^priority=1^ORpriority=2 |
| 「承認済みだが未実装の変更」 | state=authorized^ORstate=scheduled |
| 「マネージャーがいないユーザー」 | managerISEMPTY^active=true |
| 「2025年第1四半期に作成されたインシデント」 | sys_created_on>=2025-01-01^sys_created_on<2025-04-01 |
| 「3日間更新されていない高優先度インシデント」 | priority IN 1,2^sys_updated_on<javascript:gs.daysAgo(3)^active=true |
ステップ3:テーブルスキーマに照らしてクエリを検証する
クエリを使用する前に、対象テーブルにフィールドが存在することを確認してください。
MCPを使う方法:
Tool: SN-Get-Table-Schema
Parameters:
table_name: incident
RESTを使う方法:
curl -u "$SN_USER:$SN_PASS" \
"$SN_INSTANCE/api/now/table/sys_dictionary?sysparm_query=name=incident^elementISNOTEMPTY^active=true&sysparm_fields=element,column_label,internal_type&sysparm_limit=200" \
-H "Accept: application/json"
ステップ 4:生成したクエリをテストする
MCP を使用する方法:
Tool: SN-Query-Table
Parameters:
table_name: incident
query: active=true^priority=1^assignment_groupISEMPTY
fields: sys_id,number,short_description,priority,state,sys_created_on
limit: 10
REST を使用する方法:
curl -u "$SN_USER:$SN_PASS" \
"$SN_INSTANCE/api/now/table/incident?sysparm_query=active=true^priority=1^assignment_groupISEMPTY&sysparm_fields=sys_id,number,short_description,priority,state&sysparm_limit=10" \
-H "Accept: application/json"
ステップ 5:GlideRecord スクリプトを生成する
基本的な GlideRecord クエリ:
// Natural Language: "Get all active incidents assigned to the Network team"
var gr = new GlideRecord('incident');
gr.addQuery('active', true);
gr.addQuery('assignment_group.name', 'Network');
gr.orderByDesc('sys_created_on');
gr.setLimit(100);
gr.query();
while (gr.next()) {
gs.info('Incident: {0} - {1} (Priority: {2})',
gr.getValue('number'),
gr.getValue('short_description'),
gr.getDisplayValue('priority'));
}
複雑な条件を使った GlideRecord:
// Natural Language: "Find incidents that are either P1 or P2,
// created in the last 7 days, and not yet assigned"
var gr = new GlideRecord('incident');
var qc = gr.addQuery('priority', '1');
qc.addOrCondition('priority', '2');
gr.addQuery('sys_created_on', '>=', gs.daysAgo(7));
gr.addQuery('assigned_to', '');
gr.addActiveQuery();
gr.query();
gs.info('Found {0} unassigned high-priority incidents', gr.getRowCount());
エンコードクエリを使った GlideRecord:
// Natural Language: "All resolved incidents from this month
// where the resolution was a workaround"
var gr = new GlideRecord('incident');
gr.addEncodedQuery('state=6^sys_resolved_on>=javascript:gs.beginningOfThisMonth()^close_notesLIKEworkaround');
gr.query();
while (gr.next()) {
gs.info('{0}: {1}', gr.getValue('number'), gr.getValue('close_notes'));
}
ステップ 6:GlideAggregate スクリプトを生成する
グループ化してカウントする:
// Natural Language: "How many incidents are open per priority?"
var ga = new GlideAggregate('incident');
ga.addQuery('active', true);
ga.addAggregate('COUNT');
ga.groupBy('priority');
ga.orderBy('priority');
ga.query();
while (ga.next()) {
gs.info('Priority {0}: {1} incidents',
ga.getDisplayValue('priority'),
ga.getAggregate('COUNT'));
}
平均値を計算する:
// Natural Language: "What is the average resolution time by category?"
var ga = new GlideAggregate('incident');
ga.addQuery('state', 6); // Resolved
ga.addQuery('sys_resolved_on', '>=', gs.daysAgo(90));
ga.addAggregate('AVG', 'business_duration');
ga.groupBy('category');
ga.query();
while (ga.next()) {
var avgDuration = ga.getAggregate('AVG', 'business_duration');
var hours = (parseInt(avgDuration, 10) / 3600).toFixed(1);
gs.info('Category: {0} - Avg Resolution: {1} hours',
ga.getDisplayValue('category'), hours);
}
フィルタリングして合計する:
// Natural Language: "Total cost of all hardware assets purchased this year"
var ga = new GlideAggregate('alm_hardware');
ga.addQuery('purchase_date', '>=', gs.beginningOfThisYear());
ga.addAggregate('SUM', 'cost');
ga.query();
if (ga.next()) {
gs.info('Total hardware cost this year: ${0}',
ga.getAggregate('SUM', 'cost'));
}
複数の集計を行う:
// Natural Language: "For each assignment group, show count,
// average priority, and oldest open incident"
var ga = new GlideAggregate('incident');
ga.addQuery('active', true);
ga.addAggregate('COUNT');
ga.addAggregate('AVG', 'priority');
ga.addAggregate('MIN', 'sys_created_on');
ga.groupBy('assignment_group');
ga.orderByAggregate('COUNT');
ga.query();
while (ga.next()) {
gs.info('Group: {0} | Count: {1} | Avg Priority: {2} | Oldest: {3}',
ga.getDisplayValue('assignment_group'),
ga.getAggregate('COUNT'),
parseFloat(ga.getAggregate('AVG', 'priority')).toFixed(1),
ga.getAggregate('MIN', 'sys_created_on'));
}
ステップ 7:レポートフィルターを生成する
生成したクエリでレポートを作成する:
MCP を使用する方法:
Tool: SN-Create-Record
Parameters:
table_name: sys_report
data:
title: "Unassigned Critical Incidents"
table: "incident"
type: "list"
filter: "active=true^priority=1^assigned_toISEMPTY"
is_published: true
active: true
クエリを使ってグラフレポートを作成する:
MCP を使用する方法:
Tool: SN-Create-Record
Parameters:
table_name: sys_report
data:
title: "Monthly Incident Trend by Priority"
table: "incident"
type: "line"
field: "priority"
filter: "sys_created_on>=javascript:gs.monthsAgo(12)"
aggregate: "COUNT"
trend_field: "sys_created_on"
interval: "month"
is_published: true
active: true
REST を使用する方法:
curl -u "$SN_USER:$SN_PASS" \
"$SN_INSTANCE/api/now/table/sys_report" \
-H "Content-Type: application/json" \
-X POST \
-d '{
"title": "Monthly Incident Trend by Priority",
"table": "incident",
"type": "line",
"field": "priority",
"filter": "sys_created_on>=javascript:gs.monthsAgo(12)",
"aggregate": "COUNT",
"trend_field": "sys_created_on",
"interval": "month",
"is_published": true
}'
ステップ 8:高度なクエリパターン
ドットウォーク(関連テーブルのフィールド):
// Natural Language: "Incidents where the caller's department is IT"
query: caller_id.department.name=IT^active=true
動的参照修飾子:
// Natural Language: "Incidents assigned to the current user's group"
query: assignment_group=javascript:getMyGroups()^active=true
相対日付クエリ:
// Natural Language: "Changes scheduled for next week"
query: start_date>=javascript:gs.daysAgo(-1)^start_date<javascript:gs.daysAgo(-8)
階層クエリでの INSTANCEOF:
// Natural Language: "All tasks including incidents, changes, and problems"
query: sys_class_nameINSTANCEOFtask^active=true
ツールの使用方法
| 操作 | MCP ツール | REST エンドポイント |
|---|---|---|
| クエリをテストする | SN-Query-Table | GET /api/now/table/{table}?sysparm_query={query} |
| テーブルスキーマを取得する | SN-Get-Table-Schema | GET /api/now/table/sys_dictionary |
| レポートを作成する | SN-Create-Record | POST /api/now/table/sys_report |
| PA インジケーターを作成する | SN-Create-Record | POST /api/now/table/pa_indicators |
| フィールドを検証する | SN-Query-Table | GET /api/now/table/sys_dictionary |
ベストプラクティス
- 最初にフィールドを検証する: クエリを作成する前に、必ず sys_dictionary を確認してフィールド名を検証します
- 段階的にテストする: 複雑なクエリは一度に 1 つの条件を追加し、追加するたびにテストします
- レポートにはエンコードクエリを使用する: エンコードクエリは、スクリプト化されたフィルターよりも移植性が高く、URL でも安全に使用できます
- カウントには GlideRecord より GlideAggregate を優先する: 集計には GlideAggregate のほうがはるかに効率的です
- テストには setLimit を使用する: パフォーマンスの問題を避けるため、開発中は必ず結果数を制限します
- ドットウォークは慎重に使用する: 複数の参照をまたいだドットウォークは、パフォーマンスに影響する場合があります
- 複数の値には IN 演算子を使用する:
priority IN 1,2,3のほうが、OR 条件を連結するより簡潔です - 複雑なクエリを文書化する: 複数条件のクエリに含まれるビジネスロジックを説明するコメントを追加します
- 日付には javascript: 関数を使用する: 相対日付関数を使うと、クエリを動的な状態に保てます
- 動的クエリで eval() を避ける: 検証されていないユーザー入力からクエリを構築しないでください
- 主要なフィルターフィールドにインデックスを設定する: 頻繁にフィルタリングするフィールドにデータベースインデックスがあることを確認します
トラブルシューティング
クエリで結果が返されない
症状: データが存在するはずなのに、エンコードクエリが空の結果セットを返します 原因:
- フィールド名のスペルミス、またはテーブルにそのフィールドが存在しない
- 値が一致しない(大文字と小文字の区別、表示値と内部値の違い)
- ACL によって結果がフィルタリングされている 解決策: フィールド名と値を検証します:
Tool: SN-Get-Table-Schema
Parameters:
table_name: [target_table]
次に、クエリを簡略化してテストします:
Tool: SN-Query-Table
Parameters:
table_name: [target_table]
query: [single_condition_only]
fields: sys_id
limit: 1
クエリで結果が多すぎる
症状: クエリの範囲が広すぎて、過剰な数のレコードが返されます 原因:
- フィルター条件が不足している
- OR 条件の範囲が広すぎる
- active=true フィルターがない 解決策: 制約を段階的に追加します:
Tool: SN-Query-Table
Parameters:
table_name: incident
query: active=true^priority=1^sys_created_on>=javascript:gs.daysAgo(30)
fields: sys_id
limit: 1
日付クエリで正しくフィルタリングされない
症状: 日付ベースのフィルターに予期しないレコードが含まれます 原因:
- 日付フィールドに日時で比較する、またはその逆を行っている
- タイムゾーンの違い
- 比較に時刻部分が含まれていない 解決策: 確実性を高めるために ServiceNow の日付関数を使用します:
// Use gs.daysAgo() instead of hardcoded dates
query: sys_created_on>=javascript:gs.daysAgo(30)
// For specific date ranges, include full datetime
query: sys_created_on>=2025-01-01 00:00:00^sys_created_on<2025-02-01 00:00:00
GlideAggregate で予期しない件数が返される
症状: 集計件数が手動で数えた件数と一致しません 原因:
- ACL によって集計対象のレコードがフィルタリングされている
- addQuery 条件が不足している
- 誤ったフィールドでグループ化している 解決策: 単純なカウントと比較します:
// Verify with GlideRecord count
var gr = new GlideRecord('incident');
gr.addEncodedQuery('active=true^priority=1');
gr.query();
gs.info('GlideRecord count: ' + gr.getRowCount());
ドットウォーククエリのパフォーマンス
症状: ドットウォークを使ったクエリの動作が非常に遅い 原因: ドットウォークの階層が多いと、高コストな JOIN が発生します 解決策: ドットウォークの深さを制限し、最初にインデックス付きの条件を追加します:
// BAD: Deep dot-walk as primary filter
query: caller_id.department.company.name=Acme
// GOOD: Add indexed conditions first, limit dot-walk depth
query: active=true^priority IN 1,2^caller_id.department.name=IT
例
例 1:複雑なインシデントクエリを作成する
Natural Language: "Show me all P1 and P2 incidents from the last 30 days
that are still open, assigned to either the Network or Server team,
and have not been updated in the last 48 hours"
Encoded Query:
active=true^priority IN 1,2^sys_created_on>=javascript:gs.daysAgo(30)^assignment_group.nameLIKENetwork^ORassignment_group.nameLIKEServer^sys_updated_on<javascript:gs.daysAgo(2)
Test:
Tool: SN-Query-Table
Parameters:
table_name: incident
query: active=true^priority IN 1,2^sys_created_on>=javascript:gs.daysAgo(30)^sys_updated_on<javascript:gs.daysAgo(2)
fields: sys_id,number,priority,assignment_group,sys_updated_on
limit: 20
例 2:集計レポートスクリプトを生成する
Natural Language: "Create a summary showing the number of incidents
created each month this year, broken down by category"
GlideAggregate Script:
var ga = new GlideAggregate('incident');
ga.addQuery('sys_created_on', '>=', gs.beginningOfThisYear());
ga.addAggregate('COUNT');
ga.groupBy('category');
ga.addTrend('sys_created_on', 'month');
ga.query();
while (ga.next()) {
gs.info('Month: {0} | Category: {1} | Count: {2}',
ga.getValue('timeref'),
ga.getDisplayValue('category'),
ga.getAggregate('COUNT'));
}
例 3:自然言語からレポートを作成する
Natural Language: "I need a pie chart showing the distribution
of change requests by risk level for the current quarter"
Tool: SN-Create-Record
Parameters:
table_name: sys_report
data:
title: "Change Requests by Risk Level (Current Quarter)"
table: "change_request"
type: "pie"
field: "risk"
filter: "sys_created_on>=javascript:gs.beginningOfThisQuarter()"
aggregate: "COUNT"
is_published: true
active: true
関連スキル
reporting/analytics-generation- PA 指標を含む完全なダッシュボードを作成するdevelopment/code-assist- クエリロジックを含むスクリプトを生成するitsm/incident-lifecycle- インシデントのデータモデルとフィールドリファレンスadmin/schema-discovery- クエリをリストビューとフィルターに適用する