{
  "name": "Recipe Search API",
  "version": "20260804",
  "endpoints": {
    "/search": {
      "method": "GET",
      "description": "レシピを検索（キーワードマッチング）",
      "parameters": {
        "q": "キーワード（全文検索、スペース区切りでAND検索）",
        "name": "レシピ名",
        "cuisine": "料理ジャンル（5区分: 和食/洋食/中華/韓国料理/エスニック。表記揺れ・サブジャンルを自動展開。他の値は完全一致。繰り返し指定/カンマ区切りで複数=OR）",
        "maxCalories": "最大カロリー（99999kcal超の非現実値はカロリーとして扱わない）",
        "maxTime": "最大調理時間（分、「◯分以内」フィルタ）",
        "minServings": "人数の下限（範囲指定。単一指定はminServings=maxServings、「5人分以上」はminServingsのみ。レシピ側の「N〜M人分」レンジとは範囲の重なりで判定〔「2〜3人分」はminServings=3でもmaxServings=2でもヒット〕、recipeYieldの人数以外の数字〔個数/寸法/重量/容量〕・999人超の非現実値は人数として拾わない。minServings>maxServingsは400、整数以外は400）",
        "maxServings": "人数の上限（範囲指定。整数のみ）",
        "author": "著者名（部分一致フィルタ）",
        "ingredient": "材料",
        "source": "レシピソース（ajinomoto, kikkoman, delishkitchen, erecipe, orangepage, kyounoryouri, lettuceclub, kewpie。繰り返し指定/カンマ区切りで複数=OR）",
        "sort": "並び替え: relevance（既定。qありは名前一致先行の2段方式〔第1段=レシピ名に全キーワード一致、第2段=説明・材料のみ一致、各段の中は公開日降順〕、qなしは公開日降順）, time_asc, time_desc, calories_asc, calories_desc, published_desc（time_asc/descは調理時間0分・未設定を末尾に配置）",
        "limit": "表示件数（デフォルト: 10）"
      },
      "example": "/search?q=トマト チーズ&source=kikkoman,ajinomoto&cuisine=洋食&maxCalories=300&limit=5"
    },
    "/search/vector": {
      "method": "GET",
      "description": "レシピを意味的類似性で検索（PLaMo-Embedding-1B使用）",
      "parameters": {
        "q": "検索クエリ（必須、スペース区切りも可）",
        "cuisine": "料理ジャンル（5区分対応、/searchと同じ。繰り返し指定/カンマ区切りで複数=OR）",
        "maxCalories": "最大カロリー（99999kcal超の非現実値はカロリーとして扱わない）",
        "maxTime": "最大調理時間（分）",
        "minServings": "人数の下限（範囲指定。レシピ側の「N〜M人分」レンジとは範囲の重なりで判定、人数以外の数字・999人超の非現実値は拾わない。minServings>maxServingsは400、整数以外は400。詳細は/searchと同じ）",
        "maxServings": "人数の上限（範囲指定。整数のみ）",
        "author": "著者名（部分一致）",
        "ingredient": "材料",
        "source": "レシピソース（8サイト、/searchと同じ。繰り返し指定/カンマ区切りで複数=OR）",
        "limit": "表示件数（デフォルト: 10）"
      },
      "note": "「さんま」「サンマ」「秋刀魚」などの表記揺れに対応。並び順は類似度（コサイン距離）固定のためsortは非対応",
      "example": "/search/vector?q=秋刀魚 大根&maxTime=30&limit=5"
    },
    "/search/hybrid": {
      "method": "GET",
      "description": "ハイブリッド検索（RRF: キーワード検索とベクトル検索を統合）",
      "parameters": {
        "q": "検索クエリ（必須、スペース区切りでAND検索）",
        "limit": "表示件数（デフォルト: 10）",
        "k": "RRFパラメータ（デフォルト: 60）",
        "candidates": "B-2方式の候補絞り込み件数N（デフォルト: 100、relevance以外のソート時のみ有効）",
        "weight": "RRF合成の字句（キーワード）側の重み（0〜1、デフォルト: 0.6。意味側は1-weight。weight=0.5で従来の対等合成を再現。範囲外・数値以外は400）",
        "cuisine": "料理ジャンル（5区分: 和食/洋食/中華/韓国料理/エスニック。表記揺れ・サブジャンルを自動展開。生のcuisine値も後方互換で完全一致。繰り返し指定/カンマ区切りで複数=OR）",
        "ingredient": "材料（フィルタ）",
        "source": "レシピソース（ajinomoto, kikkoman, delishkitchen, erecipe, orangepage, kyounoryouri, lettuceclub, kewpie。繰り返し指定/カンマ区切りで複数=OR）",
        "author": "著者名（部分一致フィルタ）",
        "maxCalories": "最大カロリー（フィルタ。99999kcal超の非現実値はカロリーとして扱わない）",
        "maxTime": "最大調理時間（分、「◯分以内」フィルタ）",
        "minServings": "人数の下限（範囲指定。単一指定はminServings=maxServings、「5人分以上」はminServingsのみ。レシピ側の「N〜M人分」レンジとは範囲の重なりで判定〔「2〜3人分」はminServings=3でもmaxServings=2でもヒット〕、recipeYieldの人数以外の数字〔個数/寸法/重量/容量〕・999人超の非現実値は人数として拾わない。minServings>maxServingsは400、整数以外は400）",
        "maxServings": "人数の上限（範囲指定。整数のみ）",
        "sort": "並び替え: relevance（既定/重み付きRRFスコア順）, time_asc, time_desc, calories_asc, calories_desc, published_desc（time_asc/descは調理時間0分・未設定を末尾に配置）"
      },
      "note": "完全一致と意味的類似性の両方を考慮した最適な検索。フィルタはベース2検索（keyword/vector）両方に適用される。sort指定（relevance以外）時はB-2方式：RRF上位N件（candidates、既定100）に絞ってからDB側で対象フィールドを並び替えるため関連性が保たれる。このときitemsのrrfScore/keywordRank/vectorRankは含まれない",
      "example": "/search/hybrid?q=トマト&cuisine=和風&maxTime=30&sort=calories_asc&limit=5"
    },
    "/stats": {
      "method": "GET",
      "description": "レシピ総数と料理ジャンル別集計を取得",
      "returns": {
        "recipes": "レシピ総数",
        "cuisines": "料理ジャンル別集計"
      },
      "example": "/stats"
    },
    "/health": {
      "method": "GET",
      "description": "ヘルスチェック"
    }
  }
}