mcpField
Q
query-generation

query-generation

github.com/happy-technologies-llcデータ・データベース4142 回インストール2 日前に更新
リポジトリ

query-generation について

query-generation は、自然言語のデータ要件をServiceNowのエンコードクエリ、GlideRecord/GlideAggregateスクリプト、レポート条件に変換するスキルです。レコード検索やレポート設定を行うユーザー向けで、日付条件やAND/OR、関連テーブル項目を扱います。

主な機能

  1. 01自然言語からのエンコードクエリ生成
  2. 02GlideRecordクエリスクリプト
  3. 03GlideAggregateによる集計
  4. 04レポート条件の設定
  5. 05日付と相対時刻のフィルター
  6. 06AND/ORとドットウォーククエリ

活用シーン

  1. 01自然言語の要件によるインシデント一覧の絞り込み
  2. 02カテゴリや優先度ごとのレコード集計
  3. 03ServiceNowレポート条件の設定
  4. 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
含むLIKEshort_descriptionLIKEnetwork
含まないNOT LIKEshort_descriptionNOT LIKEtest
で始まるSTARTSWITHnumberSTARTSWITHINC
で終わるENDSWITH[email protected]
空であるISEMPTYassignment_groupISEMPTY
空ではないISNOTEMPTYresolved_atISNOTEMPTY
より大きい>priority>2
より小さい<priority<3
以上>=sys_created_on>=2025-01-01
以下<=sys_created_on<=2025-12-31
リスト内INstate IN 1,2,3
リスト外NOT INpriority NOT IN 4,5
範囲内BETWEENsys_created_onBETWEEN2025-01-01@2025-06-30
インスタンスINSTANCEOFsys_class_nameINSTANCEOFtask
AND^active=true^priority=1
OR^ORpriority=1^ORpriority=2
新しいクエリ(ORグループ)^NQpriority=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-TableGET /api/now/table/{table}?sysparm_query={query}
テーブルスキーマを取得するSN-Get-Table-SchemaGET /api/now/table/sys_dictionary
レポートを作成するSN-Create-RecordPOST /api/now/table/sys_report
PA インジケーターを作成するSN-Create-RecordPOST /api/now/table/pa_indicators
フィールドを検証するSN-Query-TableGET /api/now/table/sys_dictionary

ベストプラクティス

  • 最初にフィールドを検証する: クエリを作成する前に、必ず sys_dictionary を確認してフィールド名を検証します
  • 段階的にテストする: 複雑なクエリは一度に 1 つの条件を追加し、追加するたびにテストします
  • レポートにはエンコードクエリを使用する: エンコードクエリは、スクリプト化されたフィルターよりも移植性が高く、URL でも安全に使用できます
  • カウントには GlideRecord より GlideAggregate を優先する: 集計には GlideAggregate のほうがはるかに効率的です
  • テストには setLimit を使用する: パフォーマンスの問題を避けるため、開発中は必ず結果数を制限します
  • ドットウォークは慎重に使用する: 複数の参照をまたいだドットウォークは、パフォーマンスに影響する場合があります
  • 複数の値には IN 演算子を使用する: priority IN 1,2,3 のほうが、OR 条件を連結するより簡潔です
  • 複雑なクエリを文書化する: 複数条件のクエリに含まれるビジネスロジックを説明するコメントを追加します
  • 日付には javascript: 関数を使用する: 相対日付関数を使うと、クエリを動的な状態に保てます
  • 動的クエリで eval() を避ける: 検証されていないユーザー入力からクエリを構築しないでください
  • 主要なフィルターフィールドにインデックスを設定する: 頻繁にフィルタリングするフィールドにデータベースインデックスがあることを確認します

トラブルシューティング

クエリで結果が返されない

症状: データが存在するはずなのに、エンコードクエリが空の結果セットを返します 原因:

  1. フィールド名のスペルミス、またはテーブルにそのフィールドが存在しない
  2. 値が一致しない(大文字と小文字の区別、表示値と内部値の違い)
  3. 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
クエリで結果が多すぎる

症状: クエリの範囲が広すぎて、過剰な数のレコードが返されます 原因:

  1. フィルター条件が不足している
  2. OR 条件の範囲が広すぎる
  3. 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
日付クエリで正しくフィルタリングされない

症状: 日付ベースのフィルターに予期しないレコードが含まれます 原因:

  1. 日付フィールドに日時で比較する、またはその逆を行っている
  2. タイムゾーンの違い
  3. 比較に時刻部分が含まれていない 解決策: 確実性を高めるために 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 で予期しない件数が返される

症状: 集計件数が手動で数えた件数と一致しません 原因:

  1. ACL によって集計対象のレコードがフィルタリングされている
  2. addQuery 条件が不足している
  3. 誤ったフィールドでグループ化している 解決策: 単純なカウントと比較します:
// 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 - クエリをリストビューとフィルターに適用する

コメント