0.19.1 • Published 7 months ago

@migiwa-ya/staticql v0.19.1

Weekly downloads
-
License
MIT
Repository
github
Last release
7 months ago

staticql

staticql は、Markdown / YAML / JSON ファイルを型安全に読み込み、検索・結合できるシンプルなデータクエリエンジンです。小規模な構造化コンテンツを扱う Jamstack / SSG を想定しています。

特長

  • Zod によるスキーマバリデーション
  • SQL ライクなクエリ(where / join)
  • CLI でインデックファイル、API 用メタファイルを出力可能

インストール

npm install @migiwa-ya/staticql

ディレクトリ例

project/
├── content/
│   ├── herbs/*.md
│   └── herbStates.yaml
├── public/
│   └── index/
├── staticql.config.ts
└── package.json

staticql.config.ts の例

import { defineStaticQL } from "@migiwa-ya/staticql";
import { z } from "zod";

export default defineStaticQL({
  storage: {
    type: "filesystem",
    output: "output",
  },
  sources: {
    herbs: {
      path: "tests/content-fixtures/herbs/*.md",
      type: "markdown",
      schema: z.array(
        z.object({
          slug: z.string(),
          name: z.string(),
          herbStateSlug: z.string(),
        })
      ),
      relations: {
        herbState: {
          to: "herbStates",
          localKey: "herbStateSlug",
          foreignKey: "slug",
          type: "hasOne",
        },
      },
      index: ["name", "herbState.name", "tags"],
    },

    herbStates: {
      path: "tests/content-fixtures/herbStates.yaml",
      type: "yaml",
      schema: z.array(
        z.object({
          slug: z.string(),
          name: z.string(),
        })
      ),
    },
  },
});

CLI の使い方

型定義(TypeScript)の生成

TypeScript 型定義ファイルを自動生成するには、次のコマンドを実行します。

npx statical-gen-types staticql.config.ts types
  • 第一引数: 設定ファイルのパス (例: staticql.config.ts)
  • 第二引数: 型定義ファイルの出力ディレクトリ (例: types)

生成結果: types/staticql-types.d.ts に型定義が出力されます。

インデックスファイルの生成

各 source のインデックスファイルを生成するには、次のコマンドを実行します。

npx staticql-gen-index staticql.config.ts
  • 第一引数: 設定ファイルのパス (例: staticql.config.ts)

このコマンドにより、各 source ごとに index-*.json などのファイルが staticql.config.tsstorage.output に出力されます。
出力例: public/index/herbs.index-name.json など。

Index File Structure

  • 各 source ごとに、index-{field}.json(インデックスファイル)が出力されます。
    例: herbs.index-name.json は name ごとの slug 配列を持ちます。

インデックスファイルの分割方式(splitIndexByKey)

データ量が多い場合、インデックスファイルを「キーごと」に分割して出力することができます。
staticql.config.ts の各 source ブロックで splitIndexByKey: true を指定すると、
インデックスファイルがキーごとにサブディレクトリ分割され、必要なキーのみを効率的に読み込めます。

設定例

sources: {
  reportGroups: {
    // ...
    index: ["processSlug"],
    splitIndexByKey: true, // ← 追加
  }
}

出力例

output/
└── reportGroups/
    └── index-processSlug/
        ├── 001.json
        ├── 002.json
        └── ...
  • それぞれのファイル(例: 001.json)には、そのキー値に該当する slug 配列が格納されます。
  • デフォルト(splitIndexByKey 未指定または false)は reportGroups.index-processSlug.json 1 ファイルに全件が格納されます。

どちらを選ぶべきか

  • データ量が少ない場合や全件一括ロードが許容される場合は従来方式で十分です。
  • データ量が多くインデックスファイルが巨大化する場合は、分割方式(splitIndexByKey: true)を推奨します。

Fast Querying with Index Files

QueryBuilder は defineStaticQLstorage.output ディレクトリの index ファイルを利用し、比較的高速に検索します。

Speed Comparison Example

import define from "../tests/staticql.config.ts";
import { HerbsRecord } from "../tests/types/staticql-types";

async function main() {
  const staticql = define();
  await staticql.saveIndexes();

  const result = await staticql
    .from<HerbsRecord>("herbs")
    .where("name", "eq", "mentha-piperita")
    .join("reports")
    .exec();

  console.log(result);
}

main().catch((err) => {
  console.error(err);
  process.exit(1);
});

Through Relations (hasOneThrough / hasManyThrough)

  • Through リレーションは、中間テーブル(モデル)を経由してリレーションを定義できます。
  • 例: herbs から processesreportGroups 経由で取得する場合など。

設定例

relations: {
  processThroughReportGroup: {
    to: "processes",
    through: "reportGroups",
    sourceLocalKey: "reportGroupSlug",
    throughForeignKey: "slug",
    throughLocalKey: "processSlug",
    targetForeignKey: "slug",
    type: "hasOneThrough",
  },
}
  • hasOneThrough も同様に type を指定できます。
  • QueryBuilder で .join("processThroughReportGroup") で利用可能です。

Cloudflare R2 ストレージ対応(Cloudflare Workers)

staticql はローカルファイルだけでなく、Cloudflare R2 ストレージもデータソース・出力先として利用できます(Cloudflare Workers での利用のみ)。
CLI・QueryBuilder・Indexer・型生成など全ての I/O が StorageProvider で抽象化されており、設定ファイルで storage.type を切り替えるだけでローカル/クラウド両対応となります。

設定例(Cloudflare Workers での R2 バケット利用)

# staticql.config.ts

import { defineStaticQL } from '@migiwa-ya/staticql/workerd';
import { z } from 'zod';

export default defineStaticQL({
	storage: {
		type: 'r2',
		output: '',
	},
	sources: {
  ...
  }
});
import define from "../staticql.config";
import { HerbsRecord } from "../types/staticql-types";

export default {
  async fetch(request, env: any, ctx): Promise<Response> {
    const staticql = define(env.MY_BUCKET);

    const herbs = await db.from<HerbsRecord>("herbs").exec();

    return new Response(herbs[0].name);
  },
} satisfies ExportedHandler<Env>;

ライセンス

MIT

0.19.1

7 months ago

0.19.0

7 months ago

0.18.1

7 months ago

0.18.0

7 months ago

0.17.1

7 months ago

0.17.0

7 months ago

0.16.4

7 months ago

0.16.3

7 months ago

0.16.2

7 months ago

0.16.1

7 months ago

0.16.0

7 months ago

0.15.0

7 months ago

0.14.3

7 months ago

0.14.2

7 months ago

0.14.1

7 months ago

0.14.0

7 months ago

0.13.7

7 months ago

0.13.6

7 months ago

0.13.5

7 months ago

0.13.4

7 months ago

0.13.3

7 months ago

0.13.2

7 months ago

0.13.1

7 months ago

0.13.0

7 months ago

0.12.0

7 months ago

0.11.0

7 months ago

0.10.4

7 months ago

0.10.3

7 months ago

0.10.2

7 months ago

0.9.2

7 months ago

0.9.1

7 months ago

0.9.0

7 months ago

0.8.0

7 months ago

0.7.3

7 months ago

0.7.2

8 months ago

0.7.1

8 months ago

0.7.0

8 months ago

0.6.0

8 months ago

0.5.0

8 months ago

0.4.0

8 months ago

0.3.2

8 months ago

0.2.2

8 months ago

0.2.1

8 months ago

0.2.0

8 months ago

0.1.0

8 months ago