2023-03-15
/icons/pass.iconやったこと
第一マイルストーンから通しの記録に関する記述を一旦削除。
おそらく色々とややこしい
LevelRecordとSingleSegmentRecordの関係
第二マイルストーンでやる。
Scrapboxに背景をつける。
タグの定義見直し
タグ, 完備タグを名札, 完備な名札という名称も許容することにする。
タグというと、どんなエンティティも一意に指すことができるイメージが強い。
名前が同じエンティティを果たして同一のタグとして扱っていいのか....
+ メモリ容量を大幅に節約できる。メモリ空間上で無駄がない
+ 同一のデータをいくつも保持する必要がない。
- システムを理解する側の人の負担が高い
- 付加情報をつける際に、個別の作品ごとに付加情報をつけるのが難しい。
出来たとしても
- エンティティの管理IDとタグの管理IDが異なるため、混乱の元。バグが発生するかも。
....もしかしてあんまり利点がない?
やっぱりタグとエンティティは一対一対応するように扱おうか。
GitHubに色々進捗をまとめる。
やっぱり草生えてる方が満足度があるよな....。
次
タグの定義見直し
目標 - 17:00
(1) 第一マイルストーンに対するデータベースインタフェースを実装する論理モデルを構築する。
Firestoreに依存した設計を行う。
/icons/pass.icon(1-1) LevelRecordDocument
/icons/pass.icon(1-2) SingleSegmentRecordDocument
~~~ #2023-03-15 ここまで ~~~
interrupt :: タグの定義見直し
(1-3) GameTItleDocument
(1-4) AbilityDocument
(1-5) TagDocument
(1-6) LevelDocument
(1-7) SingleSegmentDocument
(1-8) MediaOwnerDocument
開発日誌上だと、今後の変化を心配せずに実装内容を考えることができるので良い。
シングルセグメント上の記録とレベル上の記録の分別について
Firestoreで記録につけられたタグを表現する。
KSSRsで要求される仕様を実装しようとすると、少しばかり困難がある。
愚直にタグ配列(Tag[])を記録ドキュメント中ののプロパティに持たせると、検索をするときに制約に引っかかる。
array-contains
1 つのクエリで使用できる array-contains 句は 1 つだけです。
二つ以上のタグ検索が常となるこのシステムではあまりにも強い制約。
array-contains-any
渡された配列の要素を少なくとも1つ持つドキュメントを取り出してくる。
OR検索のみができる
オブジェクトのハッシュで集合を表現する。
悩んでいたところ、以下のような記事を発見した。
/appbirdNotebook-public/firestore で配列の検索・追加・削除がサポートされたので試してみた - Qiita
従来推奨されていたパターンの、オブジェクトのハッシュを配列として表現するパターンが有効かもしれない。
この表現方法が有効なのは以下の条件が満たされる場合
要素が文字列のみ
順序が重要でない。
重複要素が存在しない
タグの集まりは集合とも捉えられるため、このいずれも満たす。
アクセスも、ハッシュの性質上O(1)またはO(logn)で済む。
赤黒木実装であればO(logn)である。
nも高々10程度だろうから、線形探索をしてもそこまでコストにならないと判断した。
よって、オブジェクトのハッシュでタグを表現する。
先んじて、以下のように定義する。
ID<T>を、データ構造Tのプロパティidの型とする。
code:ts
type ID<T> = T extends { id: infer V } ? V : never;
https://gyazo.com/52db356ce117886f7aad4a868bc02e4c
code:ts
type Set_of_Tag = { key:ID<Tag>: TagKind };
(1-1) LevelRecordDocument
エンティティ(実体; Entity)の定義#64109d08d3fe61000045d55fにある性質により、
記録の基礎情報はタグの集合だけで表現できる。
また、配列にはオブジェクトのハッシュを使えば良い。
2023-03-15#64115aa2d3fe6100001a4689
よって、以下の通りに策定する。
本来の推奨パターンで値にtrueを入れていたが、ここではタグ種別の情報を入れ込むことにした。
こうすることで、タグのID-日本語翻訳などを高速化できる。
score_typeはゲームタイトルから得ることができる。
code:ts
interface LevelRecordDocument{
id: string;
tags: { key:ID<Tag>: TagKind };
score: number;
docinfo: DocumentInfo;
parent_single_segment_record?:ID<LevelRecord>[];
}
(1-2) SingleSegmentRecordDocument
関連するレベル上の記録をどう保存するかが問題となる。
シングルセグメントの定義上、記録の保存順序が重要となるため配列を採用。
しかも、このプロパティにおいては複数のarray_containsをAND検索する機会はないため、この形式で保存する。
code:ts
interface SingleSegmentRecordDocument{
id: string;
tags: Set_of_Tag;
score: number;
level_records:
(ID<LevelRecord> | ID<SingleSegmentRecordDocument>)[];
scores: number[]
docinfo: DocumentInfo;
}
(1-3) GameTitleDocument
ゲームタイトル(エンティティ)を一意に指すことができる。
特別なエンティティのデータ表現を参照。
完備な名札としての情報も持つ。
code:ts
interface GameTitleDocument{
id: string; //エンティティに対して与えるID
tagID: ID<Tag>; // 対応するタグのID
rule: string;
record_count: number;
runners_list: Set_of_Tag;
score_type: ScoreType;
}
(1-4) AbilityDocument
他に必要な情報がないため、現状は用意しない。
しかし、このデータは
(1-5) TagDocument
全ての名前情報をここで管理する。
code:ts
interface TagDocument{
id: TagID
kind: TagKind;
default_name: string;
game_title?:ID<GameTitleEntity>;
actual_name?: ActualName;
}
(1-6) LevelDocument
code:ts
(1-7) SingleSegmentDocument
(1-8) MediaOwnerDocument
なんかそもそものエンティティの定義がまずい気がしてきた
エンティティとタグの対応関係がややこしい
場合わけが必要。
ただ単純にAbility#Bombとした場合、それは複数のエンティティを指している。
トリデラのボムを指しているのか
スタアラのボムを指しているのか
はたまたディスカバのボムを指しているのか.....。
曖昧性がある。