Skip to content

crsearch.jsonフォーマット

Akira Takahashi edited this page Jun 11, 2019 · 147 revisions

crsearch.json (v3-beta)

crsearch.json のルートオブジェクトは Database 型である。

ℹ v1 のフォーマットが決まった経緯は、 cpprefjp/crsearch#1 を参照。

ℹ v2 以降の変更については、このドキュメントの変更履歴を参照。

ℹ v3 はこの行が挿入されたリビジョンから策定開始。v3の目的としては、大きな機能追加は入れない。v2の仕様をベースに、仕様として欠陥があるものや現実の実装都合における問題を仕様レベルで現状に合わせる。


Database

サイト全体を示すオブジェクト。2つ以上含めてはならない。

Key Type Example Description
base_url String "https://cpprefjp.github.io" ベースURL(末尾スラッシュ無し)
database_name String "cpprefjp" crsearchデータベースの名前
namespaces Array<Namespace> (必須) サイトのトップレベルカテゴリ
ids Array<IndexID> (必須) データベースで持つ全てのIndexID。この配列の頭から0-basedなインデックス(サロゲートキー、オートインクリメント)が振られる

ユニーク性

  • ユニークである(サイトごとに1つしか存在しない)
  • crsearch.js を使っている検索プロバイダが Database::database_name によってタグ付けすることが期待できる

ℹ 現実的には、自分自身を容易に識別できないような不親切な名前をデータ内で使っている crsearch.json をわざわざ参照して使うようなことはしないだろう。そのため、信頼できるソースからの crsearch.json を読み込む際には、 Database::database_name をそのままプログラム内で利用しても問題は起こらない。


Namespace

サイトのトップレベルカテゴリを示すオブジェクト。

Key Type Example Description
namespace Array<String> ["lang"] グループ化のための名前(左結合)
indexes Array<Index> (必須) インデックスデータ
(optional) path_prefixes Array<String> ["lang", "cpp14"] Database::base_url に後続するパスプレフィックス(左結合)。省略した場合は namespace と同一になる。バージョニングの際の実際のURLとの対応付けが目的
(optional) cpp_version String "14" 対応するC++のバージョン。C++XXのXX部分

ユニーク性

  • namespacecpp_version が両方とも同じオブジェクトが、同一 Database オブジェクト内に複数存在してはならない。

ℹ 2つ以上の Namespace オブジェクトの namespace が同じ場合、それらは同一のカテゴリとして視覚的にまとめた上で、 cpp_version によってタグ付けをするべきである。

namespace における予約済み文字

  • /

IndexID

C++のトークンや、解説記事のタイトルなど、サイトで使われるあらゆる識別子を示すオブジェクト。

IndexID の目的は、同じトークンのグルーピングである。記事のユニーク性の保証は、 Index::page_id を参照。

Key Type Example Description
type String (必須) (後述)
key Array<String> ["duration", "operator+"] typeに応じたユニークインデックス(左結合)
(optional) cpp_namespace Array<String> ["std", "chrono"] 対象が存在するC++の名前空間。名前空間がある物でこれを省略した場合は、 ["std"] として扱う。 type"namespace" の場合は、 cpp_namespace は暗黙のうちに key と同一として扱う

ユニーク性

  • ユニーク性は無い。

C++言語の意味的に同名異義の識別子 についての解説ページが 別個 の記事として存在する場合は、 IndexID は重複させても良い。そのような場合は、 keycpp_namespace が共に文字列レベルで完全一致しなければならない。これは、例えばオーバーロードやtemplateの特殊化などが相当する(実例は後述)。

⚠ 上記以外のケースで IndexID が重複した場合、検索結果やナビゲーションのUX上正しくグルーピングすることが不可能になるので、そのような曖昧さは非推奨である。

例:

  • std::operator==(basic_string, basic_string)
    std 名前空間内の関数)
  • std::bitset::operator==(bitset)
    std::bitset 同士の等値比較用の、 std::bitset のメンバ関数)

この2つについて IndexID を考えた場合、crsearchの仕様では IndexID::key が完全に同一なものになる。

  • IndexID::cpp_namespace = ["std"]
    (これは省略可能だが、説明のため明示した)
  • IndexID::key = ["operator=="]
    "std::operator==" ではない点に注意!)

上記のどちらの関数も、名前としては std::operator== ではなく operator== が自身を識別するために必要十分な表現であるので、 IndexID::key は上記のようになる。

一方で、 std::basic_string のための std::operator== も、 std::bitset 同士のための std::bitset::operator== も、どちらも名前空間は std:: である。

そのため、このままでは両者について IndexID::cpp_namespaceIndexID::key はどちらも完全に同一となり、この2つのデータだけでは区別が不可能となる。

ここでは、両者は type で区別が可能である。

  • std::operator==(basic_string, basic_string)
    --> IndexID::type = function
  • std::bitset::operator==(bitset)
    --> IndexID::type = mem_fun

しかしながら、オーバーロード等では type も同一になるため、ここにユニーク性を仮定してはならない。まとめたい場合は、視覚的にグルーピングするべきである。

(参考)同じ名前の対象に関する記事が1つだけとは限らない

IndexID が示す名前が同じでも、表示上の参照先(記事)が1つだけとは限らない。そのような場面としては、幾つかのケースが想定されている。この点は crsearch.json の仕様のバグではなく、想定範囲内である。

⚠ 以下の内容は全ての可能性を網羅していないので注意すること

  1. 完全に同一の対象を扱った解説記事でも、C++のバージョンごとにページが丸ごと分けられている場合
    例: std::unexpected はC++11で非推奨となり、C++17で削除された。これを説明するために、 std::unexpected に関するサイトの記事が2つ以上存在する可能性がある。
  2. 同一のclassについて、複数の側面から解説する記事が2つ以上ある場合
    例:
    (i) そのclassを含むヘッダファイルの解説記事
    (ii) そのclass自体の解説記事
    (iii) classが追加されたC++の言語規格のバージョンの仕様をまとめるページ
  3. スタブ
    例: スタブの都合上既存のエンティティを参照しているが、その対象の記事が既に存在しているなど

ℹ このような複数への参照を解決するためには、後述の Index::related_to を適切に設定する。

ℹ (実装依存の挙動) ユーザの検索でこれら同一の名前を持つ複数の存在が同時にマッチした場合は、単純に全てが表示される。その上で、ユーザが検索クエリを適切に絞り込むことでおおよそ想定通りに動く。これを簡単にするため、フロントエンドの実装はトークンをグルーピング化してもよい(便宜上、IndexIDを連想配列のキーなどにしてもよい)。

type

type
header 標準ライブラリのヘッダ
namespace 対応するC++の物
class 同上
function 同上
mem_fun 同上
enum 同上
variable 同上
type-alias 標準規格における既存の型のエイリアス
macro マクロ、あるいはプリプロセッサの文脈で扱われるキーワード全て
concept 標準ライブラリのコンセプト
article サイトの記事
meta サイトの記事をまとめるために使われるメタページや、サイト内の大きなカテゴリのトップページ

key における予約済み文字

どの type ? key における予約済み文字
header ", <, >,
/ (サブディレクトリはsplitすること)
namespace :
class :
function :
mem_fun :
enum :
variable :
type-alias :
macro 空白文字
concept :
article 改行文字
meta 改行文字

Index

サイトの記事や、記事をまとめるためのメタページを示すオブジェクト。

Key Type Example Description
id Integer (省略) IndexIDのサロゲートキー
page_id Array<String> (v2から必須) 絶対URLの自動生成で使われるページID。サービス内の全ページについてユニークなID(後述)
related_to Array<Integer> (意味的に存在する場合は必須。v2から必須) IndexIDのサロゲートキーの配列。自分自身を「検索結果の関連」として表示する対象(後述)
(optional) nojump Boolean (省略) クイックジャンプ先として表示してほしくない場合にtrueを指定する。執筆者向け資料ページや、スタブなどが対象
(optional) attributes Array<String> ["cpp14deprecated", "cpp17removed"] ページの属性情報。後述

ユニーク性

  • 全ての Index オブジェクトは、同じ Database オブジェクト内でグローバルにユニークでなければならない。
  • 意味的なユニーク性は、 id によってトークンレベルで保証される。
  • 論理的なユニーク性は、 page_idNamespace レベルで保証する。

IndexNamespace レベルでユニークになり、 NamespaceDatabase レベルでユニークなので、 Index は常に Database レベルでもユニークとなる。

参考:特定のC++バージョンに関連した Index を扱う方法

対象についての仕様追加や仕様変更が行われたC++の言語規格のバージョンを Index / IndexID のスコープで解決する方法は無い(後述のように、 crsearch.json は元々そのような論理構造を採用していないため、別の正しい解決方法がある)。

例として、 std::vector を挙げる。

  • std::vector::push_back() :メンバ関数、C++03以前から
  • std::vector::emplace_back() :メンバ関数、C++11以降から

C++11で追加された対象についての Index は、 Namespace::cpp_version = "11"Namespace オブジェクトの子要素に含めればよい。このため、 std::vector のメンバを扱うための Namespace オブジェクトは同じ crsearch.json 内に 少なくとも2つ以上 存在するはずである。

page_id

page_id は、v2から必須となった。

内容には、本番環境のURLのパス構造を見た上で、ルート以下からファイルへの相対URLを パス区切り文字 / で split した配列を格納する。その際、ファイル名の拡張子(つまり .md )は削除する。

ℹ 元々、v1から既に存在していた 「IndexのURLを解決する処理」 は、本来は IndexID::key などを使って機械的に自動生成することが十分可能なものだった。一方で、現実的には記事自体をプログラム内で連想配列に格納する際のキーとして利用する場合など、実装上の様々な応用場面が浮上したことから v2 で必須となった。

Index のURLの解決

crsearch は Index::page_id に応じて絶対URLを自動生成して解決する。

// 擬似コード

let db = new CRS.Database

let ns  = new CRS.Namespace
db.addNamespace(ns)

let idx = new CRS.Index
ns.addIndex(idx)

const url = new URL(
  `${db.base_url}/${ns.path_prefixes.join('/')}/${idx.page_id.join('/')}.html`
)

related_to の扱い

related_to は、v2から必須。

例として、 「あるメンバ関数 mem_funcpprefjp の動的部分全てにおいて正しく認識する」 ためには、自身が所属する classheaderIndexID サロゲートキーがここに含まれている必要がある。

⚠ 実装のバグによってこの検出が失敗した場合には、その対象が名前空間スコープのフリー関数として表示(フォールバック)される可能性がある(ナビゲーションの問題なので、記事本体には影響しない)

attributes

ページの属性情報。

  • cppXXdeprecated: C++バージョンXXで非推奨
  • cppXXremoved: C++バージョンXXで削除
  • deprecated_in_latest: 現行のC++バージョンで非推奨
  • removed_in_latest: 現行のC++バージョンで削除

*_in_latest は互いに素であるため、どちらか片方しかつかない。

*_in_latest 系の属性は絶対に矛盾してはならない。

ℹ 上記の特別な属性がついていない場合は、全ての属性を集合レベルで加算して自動で解決されるので、つけなくてもよい。この最終的な属性の正しさに依存する細かい挙動は、実装中に数多く存在するので、特別な事情がある場合を除いては省略することが推奨される。