db-query について
db-queryは、Datex Studioでの`$db`アクセスを扱うリファレンススキルです。function-creatorなどを使ってストレージアクセスを実装する開発者向けに、述語DSL、操作方法、スキーマ変更監査を説明しますが、コンポーネントの作成やプッシュは行いません。
主な機能
- 01`$db`述語DSLの説明
- 02読み取り・書き込み・更新操作の解説
- 03フィールドと必須列の制約確認
- 04ストレージスキーマ変更監査の案内
- 05フローデータソースのアクセスパターン
活用シーン
- 01関数用の`$db`クエリ作成
- 02パッチに必須フィールドが含まれるか確認
- 03ストレージのフィールド名変更による影響追跡
- 04グリッドやセレクター向けのフローデータソース設定
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ datex/ skills --skill db-queryインストール後、Claude Code で/db-queryを入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
evals/evals.jsonreferences/db.mdflow-db-datasources.md詳細
$db クエリガイド
$db は、クラウドに永続化された Mongo ベースのストレージコンポーネントを読み書きするための、関数層のランタイムハンドルです。述語には、サーバー側クエリにコンパイルされる流暢な演算子 DSLを使用します。生の JavaScript 評価ではありません。 $db のコードを書く前に理解しておくべき最も重要な点です。ネイティブ演算子はエラーなくコンパイルされ、実行時に誤った動作をしても警告されないためです。
このスキルは参照専用です。$db の仕組み(述語 DSL、演算子、スキーマ変更の監査)と、$db を使ったフローデータソースのサブバリアントを担当します。関数、データソース、ストレージコンポーネントを単独で作成することはありません。これらの一連の操作は、このスキルを呼び出した親の作成スキル(通常は function-creator)が担当します。
関連項目:
function-creator—$db呼び出しを含むバックエンドフローの作成。db-query は述語 DSL とストレージアクセスの仕組みを担当し、function-creator は関数のライフサイクル(dxs function generate/validate/upsert)を担当します。
参考資料
- ../datex-studio-shared/branch-setup.md — ブランチ/接続の選択(各スキル共通)
- references/db.md —
$dbの正式なリファレンス:アクセス方法、API サーフェス、述語 DSL、結果型のルール、暗黙的なid列、読み取り後にパッチを適用するイディオム - references/flow-db-datasources.md — フロー型データソース(
type: "flows"、configurationTypeId: 6/19)。getListFlowが$dbから読み取り、グリッドやセレクターにデータを渡す場合のページネーション、動的なフィルター/並べ替え用ヘルパーフロー、4 か所での登録ミラーリング - ../datex-studio-conventions/naming-conventions.md — ストレージフィールドと動的フィルターパイプラインに適用される、すべてを snake_case にするルール
- ../datex-studio-runtime/runtime-globals.md —
$flow、$apis、$flows、$datasources、$testとともに$dbを記載 - ../datex-studio-runtime/calling-conventions.md — アクションから
$dbにアクセスできない理由を説明する階層マトリクス - ../storage-creator/references/storage.md — ストレージコンポーネントの構造、列記述子、
required: trueに関する落とし穴 - ../function-creator/references/functions.md — 呼び出し元の階層。
$db呼び出しをラップする関数本体に関する一連の操作のルール - ../datasource-creator/references/odata-datasources.md — OData データソースにおける、結果型を省略可能にする同様のルール
- ../datasource-creator/references/flow-datasources.md — フロー型データソースの構造とエンティティ定義の契約
依存関係
function-creatorスキル — 一般的な呼び出し元です。このスキルには、code文字列に$db呼び出しを含む関数に対する往復編集ルール(dxs configuration get -O envelope.json→jq .json→ 編集 →dxs configuration upsert)が記載されています。このスキルは単独ではなく、そのワークフロー内から参照されます。storage-creatorスキル — 述語またはパッチを適用する前にスキーマへの列の追加、削除、名前変更が必要な場合に呼び出されます。datasource-creatorスキル —$db呼び出しがフロー型データソースのgetListFlow/getByKeysFlow/getFlowスロット内にある場合に呼び出されます(references/flow-db-datasources.mdに記載されている、グリッド/セレクターのバックエンドパターンです)。grid-creator/selector-creatorスキル —$dbを使うフローデータソースをグリッドまたはセレクターに接続する場合に、datasource-creatorとともに呼び出されます(references/flow-db-datasources.md§ 8 の4か所にわたる登録ミラーは、両方のコンポーネント種別にまたがります)。impact-analysisスキル — ストレージスキーマの変更が進行中の場合は必ず呼び出されます。$db.<Package>.<storage_referenceName>を使ってすべての呼び出し元を逆追跡し、読み取り側と書き込み側のヒットに分類して、スキーマを反映する前に各呼び出し箇所を確認します。post-edit-verification/component-validatorスキル —$db呼び出しを含む関数/データソース/ストレージを親の作成スキルがプッシュした後に呼び出されます。このスキル自体は何もプッシュしません。
CLI のライフサイクル
$db はランタイム API であり、Studio の構成タイプではないため、dxs db サブコマンドはなく、$db 呼び出し自体を往復編集することもありません。変更対象はすべて、$db を呼び出す構成内に埋め込まれた code 文字列です。
| 呼び出し元 | 構成タイプ | 往復編集を担当するスキル |
|---|---|---|
| 関数本体 | function(configurationTypeId: 9、-flow.json) | function-creator |
| フロー型データソース内のフロースロット | datasource(configurationTypeId: 6 または 19、type: "flows" を含む -datasource.json) | datasource-creator |
| スキーマの監査/列名の変更 | storage(-storage.json) | storage-creator(呼び出し元については impact-analysis も併用) |
このスキルでは、述語/パッチを正しく記述する方法を説明します。往復編集ルール(get -O envelope.json → jq .json envelope.json > body.json → 編集 → update)は親の作成スキルが担当します。述語の作成が終わったら、そのスキルに戻ってプッシュしてください。
ワークフロー
[Phase 1: Setup + Context]
Follow branch-setup.md for branch/connection selection (if the
parent skill hasn't already)
|
Identify the storage:
- storage referenceName (matches *-storage.json filename stem)
- package the storage lives in ($db.<Package>.<storage>)
- caller tier (function-tier ONLY — never an action, never UI)
- schema (objectTypeDef[] field ids — predicates / patches
must match these exactly)
|
[Phase 2: Pick the $db operation]
Read vs mutate? Single-row vs list? Read-then-patch needed?
- read list -> .where(...).sort(...).take(n).toList()
- read single by id -> .where(r => r.id.equals(guid)).toList()
- insert one -> .add(record)
- insert batch -> .addMany(records)
- patch one -> .update(id, patch)
(read-then-patch if storage has
any `required: true` columns)
- delete matching -> .removeMany(predicate)
Consult references/db.md → API Surface before picking
|
[Phase 3: Author the predicate / patch]
Fluent DSL only — no native operators:
- .equals / .in / .isNull / .and / .or (NOT === / && / ||)
- .update(id, patch) takes id string DIRECTLY, not a callback
- Result rows are TypeScript-optional even for required columns
- Field ids must match storage objectTypeDef[].id exactly
Walk references/db.md → Pre-Flight Checklist before push
|
[Phase 4 (optional): Backing-datasource decision]
Is the $db call wrapping a grid or selector? If so:
- YES -> consult references/flow-db-datasources.md for the full
authoring rules (getQuery factory, dynamic filter /
order-by helper flows, snake_case-everywhere rule,
four-location registration mirror, $orderby / $filter
inParam declarations)
Invoke `datasource-creator` (+ `grid-creator` /
`selector-creator` for the consumer side) for the
round-trip
- NO -> the $db call lives directly inside a function body;
return to `function-creator` for the round-trip
|
Schema change in flight? -> invoke `impact-analysis` with
`$db.<Package>.<storage_referenceName>` BEFORE the parent
creator skill pushes the storage edit
フェーズの詳細
フェーズ 1:セットアップとコンテキスト
- 親スキル(通常は
function-creator)でブランチと接続がすでに設定されていない場合は、選択について ../datex-studio-shared/branch-setup.md に従ってください。ブランチ ID を決めつけないでください。必ずユーザーに確認してください。 - アクセス対象のストレージを特定します。 アクセスパスは
$db.<Package>.<storage_referenceName>です。<storage_referenceName>は-storage.jsonのファイル名の拡張子を除いた部分と一致します(例:widget_rule_storage)。<Package>はストレージコンポーネントのパッケージ配置を示します。通常は機能パッケージ(Acme)であり、デフォルトのUtilitiesではありません。ストレージファイルに記載されたパッケージ宣言を直接読み取ってください。機能フォルダー名から推測してはいけません。
- 呼び出し元の階層を確認します。
$dbは関数階層でのみ使用できます。呼び出し元の設定が関数(configurationTypeId: 9、-flow.json)か、フロータイプのデータソース(type: "flows"、configurationTypeId6または19を含む-datasource.json)内のフロースロット(getListFlow/getByKeysFlow/getFlow)であることを確認してください。アクション(-footprintFlow.json)内では使用できず、UI コンポーネントにも公開されていません。アクションでストレージへのアクセスが必要な場合は、読み取り/書き込みを関数でラップし、$apis.<Package>.FootprintApi.extendedActions.<function>経由で呼び出してください。 - ストレージスキーマを読み取ります。ストレージファイルの
objectTypeDef[]配列には、すべての列のid(snake_case)、type、isCollection、requiredフラグが記載されています。述語とパッチで参照するフィールド名は、これらのidと完全に一致している必要があります。スキーマの名前を変更すると、更新されていない呼び出し箇所はすべて動作しなくなります(フェーズ 4 のスキーマ変更監査を参照)。
フェーズ 2:$db の操作を選ぶ
references/db.md → API Surface を確認し、目的に合った操作を選んでください。
| 目的 | API 呼び出し |
|---|---|
| 条件に一致する行のリストを読み取る | .where(predicate).sort({...}).take(n).toList() |
| ID で単一の行を読み取る | .where(r => r.id.equals(guid)).toList()(または「すべて」の場合は .where を省略) |
| レコードを 1 件挿入する | .add(record) |
| レコードを一括挿入する | .addMany(records) |
| ID でレコードにパッチを適用する | .update(id, patch) — 第 1 引数には述語コールバックではなく、ID 文字列を直接指定します |
| 条件に一致するすべての行にパッチを適用する | .updateMany(predicate, patch) |
| 行を 1 件、アトミックに取得/テスト・アンド・セットする | .findOneAndModify(predicate, patch, { sort?, select?, returnDocument? }) — 唯一の CAS プリミティブです。upsert オプションはなく、存在しない場合の挿入はできません(references/db.md を参照) |
| ID で行を 1 件削除する | .remove(id) |
| 条件に一致する行を削除する | .removeMany(predicate) |
| 条件に一致する行を数える | .count()(終端操作です。getQuery() ファクトリールールについては flow-db-datasources を参照) |
.where(...) はカーソルを返します。.toList() や .count() などの終端操作が実行されるまで、ストアにはアクセスしません。複数の連結可能なステージ(.where(...).sort(...).take(10).toList())は、単一のサーバー側クエリとしてまとめられます。
ストレージに required: true の列がある場合にパッチを適用するなら、読み取り後にパッチを適用するフローを計画してください。まず行を取得してから、実際の変更内容とともに必須フィールドも含めたパッチを作成します。references/db.md → Read-then-patch for a storage with required: true columns を参照してください。
Phase 3:述語/パッチを作成する
references/db.md のルールに従って呼び出しを記述します。重要な不変条件は次のとおりです。違反すると、いずれもエラーが表面化しないまま失敗します。
- 述語 DSL は流暢な API であり、ネイティブ構文ではありません。 すべての比較には
.equals(...)/.in([...])/.isNull()を使い、===/!==は使いません。すべての論理演算には.and(...)/.or(...)を使い、&&/||は使いません。TypeScript は誤った形式も警告なしに受け入れるため(カラム式オブジェクトの型が緩いため)、実行時に翻訳エラーが発生するか、誤った結果セットがエラーもなく返されます。 .update(id, patch)には id 文字列を直接渡します。 述語コールバック形式ではありません。required: trueのカラムはパッチにも含める必要があります。 プラットフォームはパッチをカラムスキーマ全体に照らして検証します。既存レコードにそのフィールドが設定されていても、省略すると更新に失敗します。- フィールド id は
objectTypeDef[].idと完全に一致させます。 ストレージの snake_case は、すべての述語とパッチでそのまま使います。 - パッケージアクセスが正しいことを確認します。 呼び出し元のパッケージは、ストレージのパッケージにアクセスできる必要があります(同じパッケージであるか、依存関係として宣言されていること)。
- カーソルの終端処理を記述します。 すべての
.where(...)チェーンは.toList()(件数を取得する場合は.count()、またはミューテーション呼び出し)で終わらせます。終端処理のないカーソルは実行されません。 - 結果行のローカル型は、すべてのフィールドを完全にオプショナルにします(または型推論に任せます)。
$dbの結果では、required: trueのカラムも含め、すべてのフィールドがT | undefinedとして型付けされます。プラットフォームの型ジェネレーターは、呼び出し元側の行型にrequiredを反映しません。厳密なローカル型注釈を付けると、インポートに失敗します。これは OData およびフロー型データソースと同じ動作です。型レベルでは、3 種類すべての結果の形状を同一に扱ってください。
親のクリエータースキルに戻す前に、references/db.md → 事前確認チェックリスト を確認してください。
フェーズ 4(任意):データソースの決定
$db の呼び出しがグリッドまたはセレクターをラップしている場合、つまりフロー型データソース(type: "flows"、configurationTypeId: 6 または 19)の getListFlow/getByKeysFlow/getFlow スロット内にある場合は、上記の基本的な $db チェックリストに加えて、次のルールが適用されます。
getQuery()ファクトリー — カーソルは一度しか使用できません。クエリ本体をファクトリーでラップし、終端操作ごとに呼び出してください。.toList()と.count()の間でカーソルを再利用してはいけません。- 単一の
.where(r => …)コールバックを使い、predicate = predicate.and(...)で条件を合成してください。.where(...).where(...)のチェーンは使用しないでください。 - ページング処理は
$dbに渡してください:.skip($skip).take($top).toList()を使用し、Promise.allで.count()を並列実行します。 - 大文字と小文字を区別しない全文検索には
r.<field>.includes(\(?i)${needle}`)を使用してください。.toLowerCase().includes(...)` は使わないでください(この JS メソッドは DSL に存在しません)。 - 動的フィルター/並べ替えには
apply_storage_datasource_dynamic_filters_flow/apply_storage_datasource_dynamic_order_by_flow(Utilities パッケージ)を使用します。ヘルパーのcolumn名はr[filter.column]としてそのまま渡されるため、グリッド内の列ごとのdynamicFilter/dynamicOrderByの値はすべて、ストレージの snake_case フィールド ID と一致している必要があります(集約後の形状の場合は、集約された行に引き継がれる同じ snake_case キーと一致している必要があります)。 - 内部識別子はすべて snake_case にしてください — フローの inParams、outParam の
objectTypeDef[].id、queryOptionsObjectTypeDef[].id、$orderby.columnと$filter.operands.*Filters.columnのoneOf.constantValueリテラル、グリッド列のid/source、動的登録のid/property、列ごとのdynamicOrderBy/dynamicFilter/dynamicFilterType.idが対象です。内部ブロック内で残る camelCase は、外部セレクターの契約(warehouse_dd→warehouseId、owners_dd→projectId)のみです。セレクター自身のinParams[0].idを読んで確認してください。 - 5 つのエンティティスキーマ宣言は同期させてください(
references/flow-db-datasources.md§ 11 と正規のグリッド配線ルールを参照)。一部の箇所だけでフィールド名を変更すると、型チェックは通っても実行時に「Property X not exists in entity definition」というエラーが発生します。 - 4 箇所の登録ミラー —
datasourceConfig.dynamicFilters / dynamicOrderBys≡datasources[0].dynamicFilters / dynamicOrderBysとし、datasources[0]ではallSelectedIsDynamicFilters/allSelectedIsDynamicOrderBysをtrueに設定してください。
詳しい手順とコードテンプレートについては、references/flow-db-datasources.md を参照してください。データソース自体(およびそれを利用するグリッド/セレクター)の往復処理は datasource-creator/grid-creator/selector-creator が担います。述語本体が正しくなったら、親のクリエータースキルに戻ってください。
スキーマ変更の監査(横断的な確認事項)
ストレージのスキーマが変更された場合(列の追加/削除/名前変更)、呼び出し元はすべて編集対象となる可能性があります。親のクリエータースキルがストレージの変更を反映する前に、次の手順を実施してください。
impact-analysisスキルを呼び出し、$db.<Package>.<storage_referenceName>を指定します。読み取り側と書き込み側の呼び出し箇所を個別に列挙し、複数ファイルのスキャンはこのスキルの対象外にします。- 該当箇所ごとに、述語とパッチ内のフィールド ID が
objectTypeDef[]のidと完全に一致していることを確認します。名前を変更しても未更新の箇所はすべて気付かれないまま壊れます(述語が未定義の列式にアクセスし、その結果クエリが実質的に空になります)。 required: trueの列を追加する場合は、既存の.update(id, patch)呼び出し元すべてで新しいフィールドも返す必要があります。読み取り後のパッチ適用が必須になります。これは最もリスクの高い不整合の方向なので、ストレージの変更を反映する前に、必ずユーザーに明示してください。
事前チェックリスト
references/db.md のチェックリストを最初から最後まで確認してください。簡易版は以下のとおりです。
- 呼び出し元の階層。 このコードは関数(
configurationTypeId: 9)内、またはフロータイプのデータソース内にあるフロースロットに置かれます。アクションではありません。UI でもありません。 - 述語 DSL はメソッドチェーン形式です。 すべての比較には
===/!==ではなく、.equals(...)/.in([...])/.isNull()を使います。すべての論理結合には&&/||ではなく、.and(...)/.or(...)を使います。 .update(id, patch)の第 1 引数には、述語コールバックではなく ID 文字列をそのまま渡します。required: trueの列は、読み取り後にパッチを適用して、パッチ内にも含めます。 必須列を省略しないでください。- フィールド名は、ストレージの
objectTypeDef[]の ID と完全に一致させます。 - パッケージへのアクセス権が正しいこと。 呼び出し元のパッケージから、ストレージのパッケージにアクセスできる必要があります。
- カーソルの終端メソッドがあること。 すべての
.where(...)チェーンの末尾に.toList()(または.count()、変更操作の呼び出し)を付けます。終端のないカーソルは実行されません。 - 結果行のローカル型は、すべてのフィールドをオプションにする(または型推論に任せる)必要があります。
id: stringを含む厳密な型注釈は、インポートに失敗します。これは$dbの結果では、OData やフロータイプのデータソースと同様、すべてのフィールドの型がT | undefinedになるためです。 - これが
$db上のフロータイプデータソース(グリッドまたはセレクターのバックエンド)の場合は、references/flow-db-datasources.md → 事前チェックリスト にある追加のチェックリストも確認してください。getQuery()ファクトリー、単一の.whereコールバック、$db側へのページング処理、全テキスト検索用の(?i)正規表現、ヘルパーフローの冒頭での呼び出し、getListFlow.inParamsでの$orderby/$filterの宣言、snake_case をそのまま使う規則、4 つの登録配列の同期、各列のdynamicFilterControlと型の一致、mapRowにおける結果の形状の一貫性の維持、行の形状を同期する 5 か所、$orderby/$filterの oneOf リテラルの同期、ハブのマウント時の inParams とグリッドの inParams の一致を確認してください。 - スキーマ変更が進行中ですか? 親の作成スキルがストレージの変更をプッシュする前に、
$db.<Package>.<storage_referenceName>を指定してimpact-analysisを実行し、該当箇所をすべて監査してください。
よくある間違い
| 間違い | 修正方法 |
|---|---|
$db の述語内で === / && / ` | |
.update(id, patch) の第 1 引数に述語コールバックを渡す | シグネチャでは id 文字列を直接渡します。.update(guid, { ... }) を使用してください。.update(r => r.id.equals(guid), { ... }) ではありません。 |
.update のパッチから required: true の列を省略する | プラットフォームはパッチを列の完全なスキーマに照らして検証するため、既存レコードにそのフィールドが設定されていても呼び出しは失敗します。読み取り後にパッチを適用し、すべての必須フィールドを引き継いでください。 |
列をクリアするために null を設定する | null 値のパッチキーは気付かないうちに破棄され、列の値は古いままになり、エラーも発生しません。型付きのセンチネル値(例:エポックミリ秒の列なら 0)を書き込み、読み取り時に null に変換してください。references/db.md を参照してください。 |
| 複数行の書き込みはアトミックだと想定する | $db にトランザクションはありません。クラッシュがどのタイミングで発生しても自己修復できるよう書き込み順序を設定してください(.findOneAndModify で勝者をアトミックに切り替える前に敗者をマークします)。また、行がすでに目標状態の場合、各書き込みが何もしないようにしてください。 |
結果行に厳密なローカル型(id: string)を注釈する | $db はすべてのフィールドを `T |
アクション(-footprintFlow.json)内で $db を使用する | 階層が一致しません。$db は関数階層でのみ使用できます。読み取り/書き込みを関数でラップし、アクションから $apis.<Package>.FootprintApi.extendedActions.<function> 経由で呼び出してください。 |
.where(...) チェーンの終端処理を忘れる | カーソルは終端処理を実行するまで実行されません。const cursor = $db.<...>.where(...); の行はカーソルを返します。引き続き .toList()(または .count()、あるいは変更処理)を呼び出す必要があります。 |
述語/パッチのフィールド ID がストレージの objectTypeDef[].id と一致しない | 何も通知されません。述語が未定義の列式にアクセスし、結果としてクエリは実質的に空になります。snake_case の ID を正確に一致させてください。名前を変更した後は impact-analysis を実行してください。 |
フロー・データソースのバック処理スロット内で、.toList() と .count() に同じカーソルを再利用する | 終端処理によってカーソルは消費されます。2 回目の呼び出しは、古いデータや誤ったデータを返すか、何も行いません。クエリ本体を getQuery() ファクトリーでラップし、終端処理ごとに呼び出してください。references/flow-db-datasources.md § 1 を参照してください。 |
フロー・データソースのバック処理スロット内で .where(...).where(...) とチェーンする | チェーンされた .where はそれぞれ新しいカーソルステージを作成するため、合成結果が予測できません。predicate = predicate.and(...) を使って、述語全体を 1 つのコールバック内で組み立ててください。references/flow-db-datasources.md § 2 を参照してください。 |
大文字と小文字を区別しない全文検索に .toLowerCase().includes(...) を使用する | この JavaScript メソッドは列式には存在しません。仮に存在しても、一致判定はフェッチ後に JavaScript で行ごとに評価されます。Mongo 側で大文字と小文字を区別せずに一致させるには、r.<field>.includes(\(?i)${needle}`)` を使用してください。 |
| フロー・データソースのバック処理スロット内で内部識別子をキャメルケースにする | 動的フィルターループは filter.column をそのまま r[filter.column] に渡します。キャメルケースの値では snake_case のストレージフィールドに一致せず、実行時に述語でエラーが発生します。外部セレクターの契約で必要なものを除き、内部ではすべて snake_case を使用してください。 |
大量データを含むファクトストレージに対し、個別値を選択する *_dd セレクターを構築する | ドロップダウンを開くたびに .toList() がトリガーされ、テーブル全体をスキャンします。読みやすい文字列フィールドでのフィルタリングには、既定で textBox を使用し、selectBox フィルターは値の範囲が限られた参照ストアに限定してください。references/flow-db-datasources.md § 8 を参照してください。 |
ストレージスキーマの変更前に impact-analysis を実行しない | 列名の変更や required: true の追加を行うと、未更新の呼び出し元すべてが気付かないうちに壊れます。ストレージの編集を反映する前に、$db.<Package>.<storage_referenceName> から逆方向に追跡してください。 |
$db を使用する関数またはデータソースを作成した後、(親の作成スキルを介して)post-edit-verification を実行してください。$db の検証エラーが発生した場合は、ここで該当するセクションを読み直してください。 |