
t0mmy
学習履歴詳細
API デザインパターン 22章 フィルタリング 読了
やったこと
- API デザインパターン 22章 フィルタリング 読了
学んだこと
ポイント
- 二種類あるフィルターの指定方法は何か
- また、どちらで実装する方が良いか
- フィルターの実装でやってはいけないこと3つ
- フィルター関数の引数についてやってはいけないことは?
- 配列フィールドをもつリソースに対してやってはいけないことは?
- フィルタリング中に発生したエラーの対処でやってはいけないことは?
より複雑なフィルター機能を提供したい場合、最善の手段は何か
学び
フィルターの指定方法には二種類ある
- 構造化されていない文字列
- 構造化されたフィルター
フィルタリング関数は、外部リソースの影響を受けないようにする
- そのためにも、フィルタリング関数の引数は "フィルタリング用文字列" と "フィルタリング対象のリソース"の二つに絞る
配列の要素を指定できるような機能を提供しないこと
- 配列の順序が変わるだけでフィルタリング結果が変わってしまう
- 一貫性が保てず、利用者側も結果を予測できなくなる
フィルタリング式のエラーは、利用者に知らせる
- 言い換えると、無視したり、隠したりしない
- 勝手な推測で意図しないデータを返却する方が、利用者にとってマイナス
基本機能では達成が困難な、複雑なフィルターが必要な場合、目的に特化したヘルパー関数を提供する
- 安易にワイルドカードを開放してはならない ## メモ
対象とする問題
ある特定の条件を満たすリソース群を抽出したい。
しかし、フロントエンド側で "全件取得 & フィルタリング" を行うのでは非効率。
どうするか?
概要
「特定の条件を満たすリソース群を返却する」APIを実装する。
{ filter: any; maxPageSize: number; pageToken: string; }
これは、考えることが多い。
- フィルター条件の書式はどうする?
- どういったフィルターをサポートする?
- 対象のフィールド、完全一致、部分一致など
全てのフィルタリングが、実装に値するわけではない。
まずは、以下を明確にする。
- 重要な懸念事項(フィルターの指定方法など)
- サポートする価値のある機能は何か
実装
フィルタリングを提供したいリソース全部に対して、共通のフィルタリングインターフェースを決めること(一貫性の担保)。
構造
主に、以下のフィルタリング技術がある。
- 構造化されていない文字列
- 構造化されたフィルター
どちらも、フィルタリングという目的は達成可能。
しかし、構造化されていない文字列によるフィルターの方が利点が多い。
- 文字列は自由度が高い
- フィルターの評価はAPIサーバー側で完結
- 新フィルター導入の際も、文字列中に新フィルター構文を記述するだけですぐ利用可能
- 言い換えると、構造化されたフィルターは、新フィルターを扱えるSDKへのアップデートが必要
- SQLに近く、RDB経験者は理解しやすい
- 言い換えると、構造化されたフィルターは、フィルターを使いこなすための独自機構を学習する必要がある
- 複雑なクエリを処理する場合、構造化されたフィルターと比較して理解しやすい実装になる
フィルター構文と動作
◆実行時間
実行時間短縮の観点から、フィルター評価関数が扱うのは以下の二つのみ。
- フィルター文字列
- フィルタリング対象のリソース(一つだけ)
比較変数を増やすことで、フィルタリングの柔軟性は向上するが、共通のフィルタリングインターフェースを提供できなくなる(≒一貫性を担保できなくなる)。
このため、フィルター関数は、フィルター文字列とフィルタリング対象のリソースのみ扱う。
また、フィルター関数は、外部システムと通信する能力を一切持たせるべきではない。
- 言い換えると、外部リソースの影響を受けないようにする。
これにより、フィルターの責務を大幅に減らし、機能を単純化できる。
その結果、「実行時間が極端に長くなる」や「計算コストが高くなる」といった問題に直面する機会が減る。
◆配列のインデックスによるアドレス指定
配列フィールドは、「順序が決まっていない」または「順序が不定である」という前提でフィルターを実装する。
- 言い換えると、配列フィールドの特定のインデックスを参照できるようなフィルターは提供するべきではない
- 別の問題(例えば、配列の要素が一つ以上存在するかなど)を解決したいことが多い
配列フィールドのインデックス参照は、配列の順序が変わるだけで結果も変化する。
結果として一貫性の担保が難しくなり、API利用者が結果を予測しにくくなることにつながる。
これは、API利用体験の低下につながるため、避けるべき。
◆厳密性
フィルターに柔軟性を持たせるか。
結論として、意図の推測は避け、意図が明確な場合にのみ柔軟性を持たせる。
- 柔軟性を持たせると、利用者が意図しないフィルター式を入力し、意図しない結果が返ってくる可能性が上がる
- 柔軟性を下げると、意図しない結果をエラーとしてすぐに知ることが出来るが、エラーばかり発生し、利用者をイライラさせる可能性が上がる
無効なフィールド参照した場合はエラーを投げること。
エラーを投げず、勝手な推測で意図しないデータを返却する方が、ずっと利用者をイラつかせる。
◆カスタム関数
共通のフィルタリング機能として提供したくはないが、利用者が希望する複雑な処理を提供したい場合はどうするか。
- 集計やパターンマッチなど
複雑な処理に特化した、ヘルパー関数を提供する。
endsWith(...) = "xxx"
,substring(...) = "xxx"
など
通常の文字列フィルタリングと同様、文字列中にヘルパー関数を入力して使用する。
トレードオフ
多くのユースケースにおいて、フィルタリングはサポートした方が良い。
- クライアントでフィルタリング処理を実装する必要がない
- データ転送量を削減できる
- APIサーバー側の計算量を削減できる
フィルターの実装に、 "構造化フィルター" と "構造化されていないフィルター文字列" のどちらを選択するか。
扱うこと
- なぜ標準の list メソッドがリソースのフィルタリングをサポートするべきなのか?
- 標準のlistリクエストの一部としてフィルターを伝える方法
- リソースのフィルタリングを行う際の動作のガイドライン
- フィルタリングの条件においてサポートすべき機能、サポートすべきでない機能
まとめ
- 標準のlistメソッドでフィルタリングを提供することで、利用者は、興味のあるデータだけ取り出すことが出来る
- フィルタリングの仕様は、一般に、構造化されたインターフェースではなく、特定の構文(SQLのようなもの)に従った文字列で同じ意図を伝えるべき
- フィルター文字列は、評価用のコンテキストとして単一のリソースだけを受け取るようにすべき
- 実行時間が無制限に増加するのを防ぐため
- フィルターは、繰り返されるフィールド(たとえば、配列)の位置やインデックスに基づいてリソース上の項目を比較する方法を提供すべきではない
- フィルターの評価中に発見されたエラーは、すぐにわかるようにすべき
- 隠したり無視したりすべきではない
- 基本的な比較機能では利用者にとって十分ではない場合は、フィルタリング中に解釈・実行可能な関数群とそのドキュメントを提供する必要がある
2023年11月19日(日)
1.5時間