t0mmy

2020年12月30日に参加

学習履歴詳細

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のようなもの)に従った文字列で同じ意図を伝えるべき
  • フィルター文字列は、評価用のコンテキストとして単一のリソースだけを受け取るようにすべき
    • 実行時間が無制限に増加するのを防ぐため
  • フィルターは、繰り返されるフィールド(たとえば、配列)の位置やインデックスに基づいてリソース上の項目を比較する方法を提供すべきではない
  • フィルターの評価中に発見されたエラーは、すぐにわかるようにすべき
    • 隠したり無視したりすべきではない
  • 基本的な比較機能では利用者にとって十分ではない場合は、フィルタリング中に解釈・実行可能な関数群とそのドキュメントを提供する必要がある
WebAPI

2023年11月19日(日)

1.5時間