# データベース設計書

## 概要

本ドキュメントは、clojure-lspプロジェクトにおけるデータベース設計について記述します。clojure-lspは従来のリレーショナルデータベースではなく、Clojureのアトム（atom）をベースとしたインメモリデータストアを使用し、Transit JSON形式でディスクにキャッシュを永続化する設計を採用しています。

### データストアの特徴

- **インメモリストレージ**: Clojureの`atom`（`db*`）を使用したスレッドセーフなインメモリデータ管理
- **キャッシュ永続化**: Transit JSON形式による効率的なシリアライゼーション
- **バージョン管理**: キャッシュバージョン（現在v13）による互換性管理
- **階層的構造**: ネストされたマップ構造によるデータ表現

## テーブル一覧

本プロジェクトはリレーショナルデータベースを使用しないため、以下は論理的なデータ構造（「テーブル」に相当）の一覧です。

| データ構造名 | 対応エンティティ | 説明 |
| --- | --- | --- |
| db* (root) | データベースルート | グローバル状態を保持するアトム |
| documents | ドキュメント管理 | URIをキーとしたドキュメント情報のマップ |
| dep-graph | 依存関係グラフ | 名前空間間の依存関係を管理 |
| analysis | コード解析結果 | URIごとのコード解析データ |
| diagnostics | 診断情報 | 各種リンター・解析ツールからの診断結果 |

## 各テーブル定義

### 1. db* (データベースルート)

グローバルなアプリケーション状態を保持するClojureアトム。全てのデータはこのアトム内にネストされたマップとして格納されます。

| キー名 | データ型 | NULL | 説明 |
| --- | --- | --- | --- |
| :documents | Map<URI, DocumentItem> | 許可 | URIをキーとしたドキュメント情報 |
| :dep-graph | Map<Symbol, DepGraphItem> | 許可 | 名前空間をキーとした依存関係グラフ |
| :analysis | Map<URI, AnalysisBuckets> | 許可 | URIごとのコード解析結果 |
| :diagnostics | Map<Keyword, Map> | 必須 | 診断情報（初期値あり） |
| :project-root-uri | String | 許可 | プロジェクトルートのURI |
| :classpath | Vector<String> | 許可 | クラスパス情報 |
| :settings | Map | 許可 | ユーザー設定 |
| :client-capabilities | Map | 許可 | LSPクライアント機能 |
| :kondo-config | Map | 許可 | clj-kondo設定 |
| :analysis-checksums | Map | 許可 | 解析チェックサム |
| :project-hash | String | 許可 | プロジェクトハッシュ |
| :settings-hash | String | 許可 | 設定ハッシュ |
| :kondo-config-hash | String | 許可 | kondo設定ハッシュ |
| :dependency-scheme | Keyword | 許可 | 依存関係スキーム |

初期状態:
```clojure
{:documents {}
 :dep-graph {}
 :diagnostics {:clj-kondo nil
               :clj-depend nil
               :built-in nil
               :custom nil}}
```

### 2. documents (ドキュメント管理)

各ファイル（URI）に対応するドキュメント情報を保持します。

| キー名 | データ型 | NULL | 説明 |
| --- | --- | --- | --- |
| :namespaces | Set<Symbol> | 許可 | ドキュメントで定義された名前空間のセット |
| :internal? | Boolean | 必須 | プロジェクト内部のファイルかどうか |
| :langs | Set<Keyword> | 必須 | 使用言語（:clj, :cljs, :edn） |

### 3. dep-graph (依存関係グラフ)

名前空間間の依存関係を管理するグラフ構造です。

| キー名 | データ型 | NULL | 説明 |
| --- | --- | --- | --- |
| :dependencies | Multiset<Symbol> | 許可 | この名前空間が依存する名前空間（マルチセット） |
| :dependents | Multiset<Symbol> | 許可 | この名前空間に依存する名前空間（マルチセット） |
| :aliases | Multiset<Symbol/nil> | 許可 | この名前空間を参照するエイリアス |
| :aliases-breakdown | Map | 許可 | エイリアスの内部/外部分類 |
| :uris | Set<String> | 許可 | 名前空間が定義されているファイルのURI |
| :internal? | Boolean | 許可 | 内部ファイルで定義されているか |
| :dependents-internal? | Boolean | 許可 | 依存元に内部ファイルがあるか |
| :dependents-langs | Multiset<Keyword> | 許可 | 依存元ファイルの言語 |

**aliases-breakdown サブ構造:**

| キー名 | データ型 | NULL | 説明 |
| --- | --- | --- | --- |
| :internal | Multiset<Symbol/nil> | 許可 | 内部ファイルで見つかったエイリアス |
| :external | Multiset<Symbol/nil> | 許可 | 外部ファイルで見つかったエイリアス |

### 4. analysis (コード解析結果)

各URIに対するコード解析結果をバケット単位で格納します。

| バケット名 | データ型 | NULL | 説明 |
| --- | --- | --- | --- |
| :namespace-definitions | Vector<Element> | 許可 | 名前空間定義 |
| :namespace-usages | Vector<Element> | 許可 | 名前空間使用 |
| :namespace-alias | Vector<Element> | 許可 | 名前空間エイリアス |
| :var-definitions | Vector<Element> | 許可 | 変数/関数定義 |
| :var-usages | Vector<Element> | 許可 | 変数/関数使用 |
| :locals | Vector<Element> | 許可 | ローカル変数定義 |
| :local-usages | Vector<Element> | 許可 | ローカル変数使用 |
| :keyword-definitions | Vector<Element> | 許可 | キーワード定義 |
| :keyword-usages | Vector<Element> | 許可 | キーワード使用 |
| :java-class-definitions | Vector<Element> | 許可 | Javaクラス定義 |
| :java-class-usages | Vector<Element> | 許可 | Javaクラス使用 |
| :java-member-definitions | Vector<Element> | 許可 | Javaメンバー定義 |
| :protocol-impls | Vector<Element> | 許可 | プロトコル実装 |
| :symbols | Vector<Element> | 許可 | シンボル |

**Element 共通フィールド:**

| フィールド名 | データ型 | NULL | 説明 |
| --- | --- | --- | --- |
| :uri | String | 必須 | 要素が存在するファイルのURI |
| :bucket | Keyword | 必須 | 要素のバケット種別 |
| :name | Symbol | 許可 | 要素名 |
| :name-row | Integer | 必須 | 名前開始行 |
| :name-col | Integer | 必須 | 名前開始列 |
| :name-end-row | Integer | 必須 | 名前終了行 |
| :name-end-col | Integer | 必須 | 名前終了列 |
| :row | Integer | 許可 | 要素開始行 |
| :col | Integer | 許可 | 要素開始列 |
| :end-row | Integer | 許可 | 要素終了行 |
| :end-col | Integer | 許可 | 要素終了列 |
| :external? | Boolean | 許可 | 外部ファイルの要素か |
| :lang | Keyword | 許可 | 言語（:clj, :cljs, :edn） |

### 5. diagnostics (診断情報)

各種リンター・解析ツールからの診断結果を格納します。

| キー名 | データ型 | NULL | 説明 |
| --- | --- | --- | --- |
| :clj-kondo | Map<URI, Vector<Finding>> | 許可 | clj-kondoからの診断結果 |
| :clj-depend | Map<URI, Vector<Finding>> | 許可 | clj-dependからの診断結果 |
| :built-in | Map<URI, Vector<Finding>> | 許可 | 組み込みリンターからの診断結果 |
| :custom | Map<URI, Vector<Finding>> | 許可 | カスタムリンターからの診断結果 |

**Finding 構造:**

| フィールド名 | データ型 | NULL | 説明 |
| --- | --- | --- | --- |
| :type | Keyword | 必須 | 診断種別 |
| :message | String | 許可 | 診断メッセージ |
| :row | Integer | 必須 | 開始行 |
| :col | Integer | 必須 | 開始列 |
| :end-row | Integer | 許可 | 終了行 |
| :end-col | Integer | 許可 | 終了列 |
| :level | Keyword | 許可 | 重要度（:error, :warning, :info） |

## キャッシュファイル構造

### ローカルキャッシュ

**ファイルパス:** `{project-root}/.lsp/.cache/db.transit.json`

| フィールド名 | データ型 | 説明 |
| --- | --- | --- |
| :version | Integer | キャッシュバージョン（現在13） |
| :project-root | String | プロジェクトルートパス |
| :project-hash | String | プロジェクト構成ハッシュ |
| :settings-hash | String | 設定ハッシュ |
| :kondo-config-hash | String | clj-kondo設定ハッシュ |
| :dependency-scheme | Keyword | 依存関係スキーム |
| :project-analysis-type | Keyword | プロジェクト解析タイプ |
| :classpath | Vector<String> | クラスパス |
| :analysis | Map | コード解析結果 |
| :analysis-checksums | Map | 解析チェックサム |
| :stubs-generation-namespaces | Set<String> | スタブ生成名前空間 |

### グローバルキャッシュ

**ファイルパス:** `~/.cache/clojure-lsp/db.transit.json` または `$XDG_CACHE_HOME/clojure-lsp/db.transit.json`

グローバル設定やユーザー全体で共有されるキャッシュデータを格納します。

## 備考

### マルチセット（Multiset）について

本データベースでは頻度カウントが必要な場合にマルチセットを使用しています。マルチセットは内部的には`{要素 頻度}`形式のマップとして実装されています。

```clojure
;; 例: 名前空間'aaa'が3つのファイルで'a'というエイリアスで参照されている場合
{:aliases {'a 3}}
```

### 依存関係グラフの使用例

```clojure
;; clojure-lsp.mainの依存関係を取得
(-> @db/db*
    :dep-graph
    (get 'clojure-lsp.main)
    :dependencies)
;; => {borkdude.dynaload 1, clojure-lsp.internal-api 1, ...}
```

### 廃止されたストレージ

以下のストレージ形式は廃止され、起動時に自動削除されます：

- **SQLite**: `{project-root}/.lsp/.cache/sqlite.db`
- **Datalevin**: `{project-root}/.lsp/.cache/data.mdb`, `lock.mdb`

### スレッドセーフティ

- `db*`はClojureの`atom`であり、`swap!`による原子的な更新が保証されています
- キャッシュの書き込みは非同期（`core.async/go`）で行われる場合があります
- 大規模キャッシュ書き込み前にGCが実行されます
