0.19.1 • Published 9 months ago

@migiwa-ya/staticql v0.19.1

Weekly downloads
-
License
MIT
Repository
github
Last release
9 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

9 months ago

0.19.0

9 months ago

0.18.1

9 months ago

0.18.0

9 months ago

0.17.1

9 months ago

0.17.0

9 months ago

0.16.4

9 months ago

0.16.3

9 months ago

0.16.2

9 months ago

0.16.1

9 months ago

0.16.0

9 months ago

0.15.0

9 months ago

0.14.3

9 months ago

0.14.2

9 months ago

0.14.1

9 months ago

0.14.0

9 months ago

0.13.7

9 months ago

0.13.6

9 months ago

0.13.5

9 months ago

0.13.4

9 months ago

0.13.3

9 months ago

0.13.2

9 months ago

0.13.1

9 months ago

0.13.0

9 months ago

0.12.0

9 months ago

0.11.0

9 months ago

0.10.4

9 months ago

0.10.3

9 months ago

0.10.2

9 months ago

0.9.2

9 months ago

0.9.1

9 months ago

0.9.0

9 months ago

0.8.0

9 months ago

0.7.3

9 months ago

0.7.2

9 months ago

0.7.1

9 months ago

0.7.0

9 months ago

0.6.0

10 months ago

0.5.0

10 months ago

0.4.0

10 months ago

0.3.2

10 months ago

0.2.2

10 months ago

0.2.1

10 months ago

0.2.0

10 months ago

0.1.0

10 months ago