asopi tech
asopi techIndie Developer
構造化する技術(第3回)― 表現形式の選択と変換

【2026年8月版】

構造化する技術(第3回)― 表現形式の選択と変換

公開日: 2026/08/13
読了時間: 約 29分

第2部では、同じエンティティについて意味、関係、振る舞い、アクセス、通信、保存という関心を分け、ビジネス、データ、アプリケーション、テクノロジーの領域と、それらを横断する文脈を整理した。

第3部では、クエリに応じたデータ構造の選択から、情報損失、再構築、運用までを設計する。

1. クエリから論理表現を選ぶ

最初に、クエリで必要になる関係と演算を確認し、それを扱える論理表現とアクセスパスを選ぶ。

1.1 表現形式が残す関係

同じエンティティ集合から、残す関係に応じて別の表現を作れる。たとえば注文イベントをリストへ置けば発生順をたどれる。各イベントのevent_typeだけをセットへ写せば、重複を除いたイベント種別を得られるが、元の発生順と回数は残らない。組織をツリーへ写せば直属の上司と部分ツリーを計算しやすいが、兼務やプロジェクト参加のエッジは別のグラフへ置く必要がある。

リストは順序、セットは所属、ツリーは階層、グラフは任意の関係、マップはキーと値の対応を保持する。どの形式も同じ情報を別の形で表しているわけではなく、保持する関係と答えやすいクエリが異なる。

表現形式の選択では、エンティティのどの性質を残すかを先に決める。順序、所属、親子、依存、近接、類似のどれをクエリに使うかに応じて、リスト、ツリー、グラフなどの論理表現が決まる。実装上の計算量やメモリ配置は、その論理表現をどの物理配置で実現するかという次の判断になる。

1.2 検索条件とアクセスパス

元の商品レコードが同じでも、クエリに合うアクセスパスは異なる。一千万件の商品を検索する場合、商品ID、価格帯、説明文、位置、画像の類似度では、それぞれ別のアクセスパスが必要になる。

インデックスの種類は、検索に使う演算子に応じて選ぶ。PostgreSQLのインデックス種別の説明では、B-treeは順序付けできる値の等価比較と範囲クエリを扱い、ハッシュインデックスは単純な等価比較を扱う。GiST、SP-GiST、GIN、BRINは、演算子クラスとデータの性質に応じて別の検索戦略を提供する。

全文検索では、ドキュメントごとに本文を読み直す代わりに、語からドキュメントIDへ対応する逆引きインデクスを作る。Apache Luceneのインデックスパッケージの説明は、インデックスをドキュメント、フィールド、語から構成し、各語を含むドキュメントへポスティングリストから到達する内部表現を示している。

近傍検索に使うインデックスは、距離が表す意味に応じて選ぶ。空間検索では、点や領域の包含、交差、近接を効率よく扱う空間インデックスが候補になる。ベクトル検索では、埋め込まれた点の距離から近傍を探すインデックスを検討できるが、地理座標の距離と意味ベクトルの距離は区別する必要がある。

インデックスには更新コストがある。正本のレコードを変更するたびにアクセスパスも更新し、非同期更新なら鮮度の差を管理する。インデックスを正本として直接修正すると、再構築時に変更が失われる。何を保存するかと、どう到達するかを分けることで、用途ごとに複数のインデックスを持てる。

1.3 ベクトル検索で扱う類似性

埋め込みは、たとえばドキュメント、画像、商品をベクトルへ写し、距離や内積から類似する候補を検索する用途に使える。明示的なカテゴリや関係を定義しなくても、連続的な近さをアクセスパスに使える。

類似性が表すのは、ベクトル空間上の距離の近さである。会社名の埋め込みが近い二社を同じ法人と判断するには別の判定規則が要り、説明が似ている二つのサービスの依存関係を判断するには別のデータが要る。ベクトルインデックスは類似を検索する用途別の射影であり、同一性や関係を直接表すものではない。

ベクトルへ写すと、元のどの語や属性が距離に寄与したかを直接説明しにくくなる。検索結果の検証が必要な用途では、モデルのバージョンと入力、距離尺度、元のドキュメントやエンティティへ戻る識別子を記録する方法がある。モデルを更新した場合はベクトル空間自体が変わるため、新旧のベクトルを同じ距離で比較できるかも確認する必要がある。

2. 論理表現を物理配置へ落とし込む

論理表現が決まったら、読み書きの頻度、処理時間、データ量に応じて、メモリやストレージ上の配置と変換経路を設計する。

2.1 検索のハッシュ、配置のハッシュ

ハッシュは、入力を固定長の値へ写し、検索、配置、内容の識別に使う操作である。たとえばcustomer_id = C-001をハッシュテーブルで検索するときは、ハッシュ値からバケットを選ぶ。同じ顧客IDを分散ストレージへ保存するときは、ハッシュ値からパーティションを選べる。内容アドレス方式では、ファイルの内容からデータを識別するアドレスを作る。

同じハッシュ関数でも、衝突の扱いと不変条件は用途で違う。ハッシュテーブルでは衝突したキーを比較して区別し、パーティショニングでは各キーを一つのシャードへ割り当てる。コンテンツハッシュを同一性の判定に使う場合は、内容が変われば識別子も変わる。設計時には、入力、ハッシュ値の用途、衝突の処理、不変条件を用途ごとに定める必要がある。

パーティショニングへ使う場合は、配置変更の影響も加わる。ノード数の変更で多くのキーが別のパーティションへ移れば、データ移動とキャッシュミスが増える。検索用ハッシュでは目立たなかった配置の問題が、テクノロジー領域では中心になる。

2.2 行指向と列指向で変わるI/O

顧客ID、日時、商品ID、金額を持つ取引表を行単位に置くと、一件の取引をまとめて読み書きしやすい。列単位に置くと、金額だけを連続して読み、集計と圧縮を適用しやすい。論理スキーマが同じでも、物理配置によってI/Oとキャッシュの局所性が変わる。

列指向は、同じ属性を近接して配置する物理的な射影である。Apache Arrowの列指向形式は、型ごとにバッファを配置し、有効性ビットマップやオフセットバッファを含むメモリ配置を定義している。複数の実装が同じ列指向データを共有し、ベクトル化処理へ渡せる。

取引更新用の行指向ストレージから分析用の列指向ファイルを作る構成では、同じ論理データに対して二つの物理的な射影を持つ。更新頻度と鮮度、再構築の経路を管理すれば、行と列を用途に合わせて共存させられる。

同じ分離はグラフにもある。概念モデルで関係をグラフとして捉え、論理モデルでノードとエッジとして表現した場合でも、物理配置には隣接リスト、隣接行列、CSRなどから用途に合う形式を選べる。疎なグラフの探索と密なグラフの行列演算では、適した配置が違う。

2.3 注文データとソースコードの多段階変換

注文受付を例にすると、注文を業務領域のエンティティとして識別し、受付操作をコマンドとしてアプリケーションの仕様へ落とし込む。入力データを通信仕様へ落とし込むときは、メッセージ、セグメント、パケットという単位を設計する。保存側では注文データのリレーション、タプル、ページ、ブロックを設計し、分析側では注文の記録をイベント、パーティション、行グループ、列、ベクトルとして設計する。注文という業務上のエンティティは、各層の操作に合う管理単位、メッセージ、レコードへ段階的に変換される。

ソースコードも、解析と実行の用途に応じて段階的に変換される。たとえばコンパイラでは、ソースコードをトークン、抽象構文木、中間表現、機械語命令へ変換する構成がある。処理系によって使う段階と表現は異なる。LLVM言語リファレンスが定義するLLVM IRは、型を持つSSA形式の命令、基本ブロック、関数、モジュールなどから構成される。LLVM IRは、解析と最適化に必要な性質を表す中間表現である。

この例の各変換は、新しい操作を可能にする。ASTは構文上の親子関係を表し、IRは制御フローとデータフローの解析に使われ、機械語命令はプロセッサが実行できる。各表現は、変換の目的に必要な情報を残す。

一方で、ソース上の位置をデバッグ情報へ残さなければ、機械語命令で起きた問題を元の行へ対応付けられない。異なる抽象レベルを往復する用途があるとき、変換後の命令やレコードへ元の同一性と位置を引き継ぐ必要がある。

3. 変換後の再構築と運用を設計する

用途別の表現を作るときは、変換によって失う情報を把握し、分割したデータの再構築、派生データの更新、障害時の回復までを設計する。

3.1 分割したデータをどう戻すか

大きなログファイルを並列処理する場合は、入力範囲を複数のパーティションへ分ける。ジョブやファイル、メッセージにも、処理、保存、転送に適した分割単位がある。

データセット → パーティション
ジョブ → タスク
ファイル → ブロック
メッセージ → フラグメント
ストリーム → セグメント
画像 → タイル

分割後の単位へ付けるメタデータは、再構築と障害回復の要件から選ぶ。順番を戻すなら順序番号、欠落を検出するなら総数やチェックサム、部分的に再試行するならパーティションの入力範囲と試行番号が候補になる。元の入力データとの対応が必要な場合は、親ID、オフセット、長さ、スキーマのバージョンなども検討する。

再構築の要件は用途で変わる。パケットは上位のペイロードを復元できる必要がある一方、分析用の集計表では個々のイベントを復元しない構成も選べる。どこまで戻せる必要があるかを決めると、メタデータと正本の保存期間が決まる。

分割の境界によって障害の影響範囲も決まる。一つの巨大なファイルを一単位で検証すれば、一部の破損で全体を再取得することになる。ブロックごとにチェックサムを持つ構成なら破損した範囲を特定できる。タスクを細かくすれば再試行範囲を狭められるが、スケジューリングと状態管理の件数は増える。

3.2 集計で失われる情報

射影では、元の表現が持つ属性や関係の一部を失う。たとえば順序付きリストをセットへ変えると順序と重複回数を失い、イベント履歴から現在の状態を作ると途中の状態を失う。Money(100, JPY)を整数100へ変えると通貨を失い、グラフをエンティティ別の件数へ集計すると個々のエッジを失う。

損失を許容できるのは、射影が答えるクエリに不要で、必要なら正本から再生成できる場合である。現在の状態を高速に取得するビューでは途中の履歴を省けるが、履歴を破棄すると過去時点の状態や変更理由を答えられない。

変換の仕様を記述するときは、たとえば入力、出力、残す属性と関係、省略する属性と関係、不変条件を検討する。記録する項目と詳しさは、変換後のデータを検証し、必要に応じて再生成できる範囲に合わせる。金額集計なら、通貨をそろえる規則、取消し取引の扱い、端数処理、対象期間が不変条件の一部になる。同じSQLを再実行できても、規則と入力のバージョンが違えば同じ意味の結果にならない。

結果だけから分からない情報損失を検証するには、変換規則と出自を管理する必要がある。会社別売上テーブルでは、契約明細が含まれないことはスキーマから分かるが、除外した取引の条件や為替換算の時点は結果だけから判定できない。

3.3 インデックスと集計表を管理する

射影の更新経路では、正本との同期、古い射影の無効化、再構築、スキーマ変更、障害時の回復のうち、運用に必要な項目を検討する。同期方法が定まっていない場合は、商品価格を更新しても検索結果が古いまま残ったり、契約を修正しても月次集計へ反映されなかったりする。

設計項目検討内容の例
正本どの事実をどこが所有するか
変換規則入力から出力を作る手続きとバージョン
整合性同期更新か、どの遅延を許すか
無効化どの変更で派生データを無効にするか
再構築全件と部分の再構築をどう行うか
検証件数、ハッシュ、不変条件をどう照合するか
廃止使われない射影をどう停止するか

射影の回復条件は、停止できる時間と再生成方法に応じて決める。インデックスや集計表の再生成に数日かかる場合は、構築時間、入力の保持期間、停止中の代替手段を回復条件に含める必要がある。

同じ処理を繰り返しても結果を壊さない冪等性も、変換の責任になる。タスクの再試行で同じ注文を二重作成しないために、業務上の同一性と試行IDを分け、出力の重複判定を変換境界に置く方法がある。途中まで作られた射影を再実行で置き換えるか、差分を継続するかは、出力形式と回復方法に応じて決める。

検証では、レコード件数だけを比べても、必要な属性や関係の欠落を検出できない。照合項目の候補には金額の合計、外部キーの対応、グラフの到達可能性、時間範囲、情報源ごとの件数があり、その射影で残すと決めた項目を選ぶ。射影が答える代表クエリを回帰テストとして残せば、変換規則の変更時に用途が壊れていないかを確認できる。

4. 複数の表現を一つのシステムで運用する

最後に、クエリと更新条件から保存・検索方式を選び、同じエンティティから作った複数の表現を混同せずに併用する方法を整理する。

4.1 クエリと更新条件から保存・検索方式を選ぶ

保存・検索方式を選ぶには、利用者が何を知り、実行し、判断するかをクエリと操作として記述する。顧客IDから一件を取得して契約を更新する関連サービスを数段たどって障害影響を調べる大量のログを日単位で集計するでは、必要なアクセス方法と更新方法が異なる。

次にエンティティと粒度を決める。同一性、更新、転送、保存、整合性、再試行には、それぞれ別の単位を選べる。注文の場合は、一つの業務上のエンティティとして扱いながら、明細単位で在庫を引き当て、決済単位で再試行し、パケット単位で転送する構成が考えられる。

保存・検索方式は、クエリの種類、更新頻度、トランザクションの範囲、応答時間、データ量、正本から再構築できるかを確認して選ぶ。

主な用途と更新条件判断するときに確認すること候補になる保存・検索方式
注文や決済を高頻度に読み書きし、複数レコードの整合性を保つトランザクションの範囲、制約、同時更新、IDや条件による検索性能RDBMS
注文書や設定一式を一つの文書として読み書きし、文書ごとに項目が変わる文書の境界、部分更新、重複する値の更新方法ドキュメントDB
組織やサービスの関係を数段たどり、経路や到達可能性を調べるノードとエッジの粒度、探索方向、関係の更新頻度グラフDB
会社名や商品説明を全文検索し、条件で絞り込み、関連度順に並べる対象フィールド、逆引きインデクスの更新間隔、正本からの再構築方法検索エンジン
文書や画像を類似度で検索する埋め込みモデル、距離尺度、検索精度、再計算にかかる時間ベクトル検索基盤
ログ、JSON、画像などをファイル単位で保存し、後続のバッチでまとめて読む書き込み頻度、分割単位、ファイル形式、走査時間、保持期間オブジェクトストレージ

一つのシステムでは、正本、検索用、分析用の役割ごとに複数の方式を組み合わせる。たとえば注文の正本をRDBMSに置き、商品説明の全文検索に検索エンジン、ログの保管と集計入力にオブジェクトストレージを使う構成が考えられる。

MySQL、Oracle Database、PostgreSQLなどの製品比較は、RDBMSを候補に選んだ後で、必要な機能、運用体制、既存環境に応じて行う。先に保存・検索方式の役割を定めることで、同種製品の比較と、異なる方式の使い分けを分けて検討できる。

4.2 RDB、JSON、グラフ、ベクトルの併用

同じエンティティを複数の形式で扱う場合は、正本と用途別の表現の役割を分け、ID、変換規則、更新経路で紐付ける。たとえば顧客情報をRDBのレコードで管理し、APIの応答にJSON、関係の探索にグラフ、類似検索にベクトルを使う構成がある。各表現には、実行するクエリ、利用者、使用する属性と関係、変換元、再生成方法を定める。

役割を分けておけば、検索インデックス、キャッシュ、マテリアライズドビュー、集計値を正本から再生成できる。APIのJSONを変更しても業務上のエンティティの定義を保ち、埋め込みによる類似検索の結果には別の同一性判定を適用できる。

物理的な識別子を業務上の同一性へ流用すると、同じエンティティの履歴をつなげられない場合がある。DBの連番、プロセスID、IPアドレス、ファイルパスは、再作成や移動で変わり得るため、概念上の同一性にはそのまま使えない。

用途が定まらない共通語彙、メタデータ、インデックスは、更新経路とバージョン管理だけを増やす。作成時には利用するクエリと処理を定め、運用中は利用状況と再生成経路を確認し、不要になった表現を廃止する必要がある。

RDB、JSON、グラフ、イベント、ベクトルは、リレーション、メッセージ、関係、変化、類似性という異なる関心を扱う。各表現の役割とライフサイクルを分ければ、一つの形式へ統一せず、ユースケースに応じて同じシステム内で併用できる。

4.3 表現形式の妥当性と再生成

表現形式の妥当性はリスト、ツリー、グラフなどの名前だけでは決まらない。エンティティのどの属性と関係を残し、どのクエリへ使い、何を失い、どこから再生成するかが定まったとき、その設計を評価できる。実装では、ユースケースに必要なデータ表現を複数の射影として設計し、正本から再生成できるかを確かめる。検討、設計、検証を繰り返しながら、各射影と再生成経路を実装へ落とし込んでいく。

5. まとめ

構造化において、まず何を一つの管理対象として扱うかが問われる。境界、同一性、属性、関係、制約を検討しながら、用途別の論理仕様と物理配置へ落とし込み、実現できるかを確かめていく。各表現を後から解釈するには、参照した管理対象、時点、情報源、規則も文脈として残しておく必要がある。

顧客や注文のような業務上の単位に加え、ファイル、メッセージ、レコード、タスク、ログイベントも管理対象になりうる。ビジネス、データ、アプリケーション、テクノロジーの各領域では、同じ管理対象でも一件の単位やIDが変わる。保持する属性、関係、制約も、その用途に合わせて選ばれる。

たとえば名寄せでは、各システムのレコードを共通の顧客IDへ紐付ける。注文処理では、同じ注文がコマンド、メッセージ、レコード、ページとして扱われる。成果物と抽象度は異なり、それぞれで何を一つの管理対象とするか、どの属性と関係を使うか、変換後の表現をどう検証するかが設計事項になる。

構造化する技術は、この判断を業務、データ、アプリケーション、テクノロジーの各領域へ適用し、システム全体を変更、検証、再構築できる形で設計する技術である。