リンク項目の [[]] 形式と JSON 形式の使い分け(できること・実装・性能)
分類項目の「選択肢一覧」に書くリンクには [[200]] のような [[]] 形式と、[{"SiteId": 200}] のような JSON 形式があります。同じリンクを作れても、選択肢を作る経路が別のコードで、使える機能と SQL の発行のされ方が違います。このページは、書き方の対応表(リンク項目の [[]] 形式と JSON 形式)を前提に、次の 3 点を 1.5.8.1 のソースで整理します。
- できること・できないこと(
[[]]形式は絞り込み・表示文字列・Lookup などが使えない) - 実装の違い(
[[]]形式は 1 回の SELECT を共有し、ユーザー・グループ・組織はメモリのキャッシュ。JSON 形式は項目ごとに SQL) - 性能に効く条件(SQL の本数、
UseSearch、取得件数の上限と並べ替えの順序)
実測値はありません
性能の話は、ソースから読み取れる SQL の発行回数・取得元・条件の違いです。ミリ秒などの計測はしていません。テーブルの件数や DB によって差の大きさは変わるため、気になる画面は DB 側の実行ログで SELECT の本数を数えて確かめてください。
先に結論
| こうしたい | 選ぶ形式 | 理由 |
|---|---|---|
| マスタを全件、タイトル順に選ばせるだけ | [[]] 形式 | 同じサイトを指す項目で SELECT を共有できる |
| 状態や区分で絞る、並び順を変える、表示文字列を変える | JSON 形式 | View・SearchFormat は JSON 形式にしかない |
| リンク先の値を自分の項目へ転記(Lookup)、リンク先の同時コピー・削除(LinkActions) | JSON 形式 | Lookups・LinkActions は JSON 形式にしかない |
| 画面の値で選択肢を動的に絞る | JSON 形式 | ColumnFilterExpressions は JSON 形式にしかない |
| ユーザーを部署名付きで出す | [[]] 形式 | [[Users,ShowDeptName]] に相当する JSON のプロパティはない |
| Wiki の本文を選択肢にする | [[]] 形式 | JSON 形式は Wiki を選択肢の取得元にできない(ソースの読み。下記) |
| 1 つの項目に複数のサイトのレコードを並べる | [[]] 形式 | JSON 形式は、選択肢の生成に先頭の 1 件しか使われない |
2 つの形式の読み分け
形式を指定する設定はありません。SetLinks() が選択肢一覧の全体を JSON として読み、読めたかどうかで分かれます(SiteSettings.cs#L4649-L4690)。
図を読み込み中…
Deserialize<T>() は例外を握りつぶして null を返します(Jsons.cs#L22-L32)。このため、設定画面でも JSON の書式エラーは出ません。
- JSON が 1 か所でも壊れる(カンマ漏れ・引用符漏れなど)と、サイトリンクとして登録されません。 全体が
[[]]形式として 1 行ずつ読まれ、JSON の各行がそのまま普通の選択肢文字列になります。選択肢の追加はAddToChoiceHash(line)で行われます(Column.cs#L466-L520)。 [[]]形式は、リンク先のサイトが存在しないと登録されません。[[999]]のようにサイト ID を間違えると、new SiteModel(...)が見つからずLinksに入りません。リンクのない項目になるため、行は普通の選択肢として扱われ、[[999]]という文字列が選択肢に並びます(この動きはソースの読みで、実機では確認していません)。- JSON 形式は、サイトの存在を保存時に確かめません。 存在しない
SiteIdでもLinksに入りますが、選択肢の取得元が見つからず選択肢は空になります。
できること・できないこと
| 機能 | [[]] 形式 | JSON 形式 |
|---|---|---|
| サイトのレコードを選択肢にする | ○ | ○ |
新規作成ボタンを隠す(NoAddButton) | ○ | ○ |
リンク元のレコードも選択肢に加える(AddSource) | ○ | ○ |
保存後に親のレコードへ戻らない(NotReturnParentRecord) | ○ | ○ |
絞り込み・並べ替え(View) | × | ○ |
検索ダイアログの一覧の表示文字列(SearchFormat) | × | ○ |
表示の優先順位(Priority) | × | ○ |
新規作成後にそのレコードを自動選択(SelectNewLink) | × | ○ |
ログインユーザーを選択肢から除く(ExcludeMe) | × | ○ |
転記(Lookups) | × | ○ |
リンク先と一緒にコピー・削除(LinkActions) | × | ○ |
画面の値による動的な絞り込み(ColumnFilterExpressions) | × | ○ |
| 組織をサイトのメンバーだけに絞る | × | ○(MembersOnly) |
ユーザーを部署名付きで出す(ShowDeptName) | ○ | × |
| Wiki の本文を選択肢にする | ○ | ×(ソースの読み) |
| 複数のサイトを 1 つの項目の選択肢にする | ○ | △(選択肢は先頭 1 件) |
[[]] 形式の解析は、1 つ目の要素をサイト ID とし、2 つ目以降は NoAddButton・AddSource・NotReturnParentRecord の 3 つだけを見ます。これ以外の文字列は黙って無視されます(Link.cs#L51-L73)。
形式によらず同じこと
リンクの有無は Links のうち SiteId が 1 以上のものを見て決まり、JSON かどうかは見られません。次の動きは、どちらの形式でも変わりません。
- リンクテーブル(リンク元・リンク先の一覧)と作成ボタン、
NoAddButton・AddSource - 一覧のチルダ構文(
ClassA~200,Title)による JOIN(リンク先の列(チルダ構文)と JOIN の生成) - 一覧に出る名前(リンク先の Title)と閲覧権限の確認
- 項目連携の親子の対応(
Linksを見る。項目連携の候補取得とイベントの連鎖) - 選択肢の取得件数の既定(
DropDownSearchPageSizeの 500。General.json#L86) UseSearch(検索ダイアログで選ぶ設定)の対象リンクの判定(GetUseSearchLinks()。SiteSettings.cs#L4629-L4637)
JSON 形式の注意点
複数の要素を書いても、選択肢になるのは先頭の 1 件です。 選択肢の生成は、その項目の Links のうち JSON 形式の最初の 1 件だけを FirstOrDefault で選びます(SiteSettings.cs#L4871-L4926、検索ダイアログ側は DropDowns.cs#L512-L578)。2 件目以降は Links には登録されますが、選択肢には出ません。[[100]] と [[200]] を 2 行並べると両方のレコードが並ぶのとは違います。
SearchFormat が効くのは検索ダイアログの候補一覧です。 選択した後のドロップダウンの表示や項目連携による候補の差し替えは searchFormat: false で呼ばれ、通常のタイトル(ユーザーなら名前)になります(DropDowns.cs#L380-L478)。
Wiki は取得元にできません。 JSON 形式でサイトの選択肢を取るには、ss.Destinations に対象サイトがあることが前提です(Link.cs#L296-L324)。この Destinations はテナントキャッシュのリンク関係から作られ、そのリンク関係は Wiki を両端から除いています(SiteInfo.cs#L568-L594)。[[]] 形式は Wiki の本文を行ごとに読んで選択肢にする経路を持っています(SiteSettings.cs#L5126-L5152)。
実装の違い
選択肢を取る経路が違います。
| 対象 | [[]] 形式 | JSON 形式 |
|---|---|---|
| サイトのレコード | 同じサイトを指す項目をまとめた 1 回の DataSet(Items と Sites の JOIN、閲覧権限つき、Title 順、上位 500 件) | 項目ごとに、リンク先の実テーブル(Issues・Results)を引く SELECT と件数の COUNT(View の条件・並べ替え、既定は Title 順) |
| ユーザー | [[Users]] はサイトのメンバーのキャッシュ、[[Users*]] はテナントキャッシュ(SQL なし) | Users テーブルを SQL で検索(Depts を LEFT JOIN、Name 順) |
| グループ・組織 | キャッシュ(SQL なし) | SQL で検索 |
サイトのレコード
[[]] 形式は、編集画面で SetChoiceHash() が呼ばれるとき、JoinedSsHash(そのテーブルとリンク関係のあるテーブル)の [[]] 形式のリンク先サイト ID を重複なく集め、サイトごとの SELECT を 1 回の ExecuteDataSet にまとめて実行します。結果は [[200]] というキーで項目に配られるので、同じサイトを指す項目が何個あっても取得は 1 回です(SiteSettings.cs#L4928-L4984)。選択肢の文字は Items に保存されたタイトルです。
JSON 形式は、項目ごとに Link.SetChoiceHash() から Items() を呼びます。実テーブルを FROM にして View.Where() を組み、データ取得と COUNT の 2 文を 1 回の ExecuteDataSet で送ります(Link.cs#L636-L712)。View に絞り込みや並べ替えがあれば、そこで使う列のための JOIN も加わります。
選択肢一覧のテキストと UseSearch が同じ項目は、どちらの形式でも先頭の 1 項目の結果を共有します(ChoiceHashKey()。Column.cs#L309-L312)。JSON 形式では、同じサイトを指していても、View や SearchFormat が 1 文字でも違えば別の項目として SELECT が増えます。
ユーザー・グループ・組織
[[Depts]]・[[Groups]]・[[Groups*]]・[[Users*]] は、メモリ上のキャッシュ(TenantCaches)から読みます。[[Users]]・[[Groups]] はサイトごとのキャッシュ(SiteInfo.SiteUsers()・SiteGroups())です。JSON 形式の "TableName": "Users" などは、Link.Users()・Groups()・Depts() が DB へ SELECT を発行します(Link.cs#L460-L634)。
JSON 形式では、Disabled 列を持つテーブル(ユーザー・グループ・組織)に Disabled = false の条件が自動で付きます(GetView()。Link.cs#L768-L779)。[[]] 形式のキャッシュ側も無効なものを除きます。
件数の上限と並べ替えの順序
ユーザーが 500 人(DropDownSearchPageSize)を超えるテナントでは、両形式で結果が変わります。
[[Users]]・[[Users*]]: 先にTake(500)で先頭 500 件を切ってから名前順に並べます。500 人を超えると、名前順の先頭 500 人にはなりません(AddUsersToChoiceHash()。Column.cs#L530-L597)。- JSON 形式:
ORDER BY Nameの後に上位 500 件を取ります。 [[Depts]]・[[Groups]]・[[Groups*]]: 件数の上限を付けず全件を名前順に並べます。
500 件を超えるマスタは、どちらの形式でも、項目の UseSearch(検索ダイアログで選ぶ)を有効にして使うのが前提の設計です。
性能に効く条件
ソースから読み取れる条件を、効きやすい順に並べます。
- SQL の本数。
[[]]形式のサイトリンクは、同じサイトなら項目が増えても SELECT を共有します。JSON 形式は、View・SearchFormatが違う項目ごとに SELECT(と COUNT)が増えます。分類項目が多いテーブルで、どれも同じマスタを指すなら、[[]]形式のほうが SELECT の本数は少なくなります。 - 取得元。
[[]]形式のサイトリンクはItemsとSitesだけを引きます。JSON 形式は実テーブルを引き、Viewの条件が複雑になるほど JOIN が増えます。条件が不要なら JSON 形式にする理由は小さくなります。 - ユーザー・グループ・組織。
[[]]形式はメモリのキャッシュで SQL を発行しません。JSON 形式は毎回 SQL です。検索ダイアログで文字を入力して絞るときは、JSON 形式ではLIKEの SQL が走ります。 UseSearchの扱い。 JSON 形式は、UseSearchが有効な項目では、検索時・全件取得時・選択済みの値があるときにだけ選択肢を取ります(Link.cs#L296-L324)。[[]]形式のサイトリンクを集める経路(上のLinkHash)にはUseSearchで除く条件がなく、[[Users]]などの分岐にあるUseSearch != trueの判定も[[サイト ID]]の分岐にはありません(Column.cs#L449-L506)。この読みの通りなら、UseSearchを有効にした[[]]形式の項目でも、編集画面を開くたびに上位 500 件の SELECT が走ります。実機の SQL ログでは確認していないため、大きなマスタで使う前に確かめてください。- 取得件数の上限。 どちらも既定で 500 件です。これを超える分は、一覧の先頭だけが出る・検索で探す、という動きになります。
書き換えの例
[[200,NoAddButton]] と同じ意味の JSON 形式です。
[
{
"SiteId": 200,
"NoAddButton": true
}
]JSON 形式にすると、状態で絞って並び順を変え、検索ダイアログの表示を変えられます。ColumnFilterHash の値は、選択値の配列を文字列にしたものです。
[
{
"SiteId": 200,
"NoAddButton": true,
"SearchFormat": "[ClassA]:[Title]",
"View": {
"ColumnFilterHash": { "Status": "[\"100\",\"200\"]" },
"ColumnSorterHash": { "ClassA": "asc" }
}
}
]ユーザー・組織の置き換えは、リンク項目の [[]] 形式と JSON 形式 の対応表を参照してください。[[Users*]] を JSON 形式にすると、500 人を超えたときの切り方が変わります(上の「件数の上限と並べ替えの順序」)。