15

REST API で検索クエリをモデル化する堅牢な方法を探しています。

私の API では、クエリ パラメーターを使用して、リソースの URI で検索条件を指定できます。

例えば:

/cars?search=color,blue;AND;doors,4 --> Returns a list of blue cars with 4 doors

/cars?search=color,blue;OR;doors,4 --> Returns a list of cars that are blue or have 4 doors

サーバー側では、検索文字列が目的の基盤となるテクノロジーにマップされます。残りのリソースに応じて、これは SQL クエリ、Hibernate Criteria API、別の Web サービス呼び出しなどになります。

2 つの例はサポートするのに十分単純ですが、部分文字列検索、前後の日付の検索、NOT などのより複雑な検索機能も必要です。

これはよくある問題だと思います。私が使用できるライブラリ(またはパターン)はありますか:

  • 文字列として指定された検索クエリを汎用 Criteria モデルにマップします。検索形式は、上に挙げたものと同じである必要はありません。
  • その基準モデルを、使用する必要のあるテクノロジーにマッピングできます。
  • Hibernate/JPA/SQL のマッピング サポートを提供しますが、これはおまけです ;)

敬具、

グレン

4

5 に答える 5

6

これらの問題に直面するたびに、「従来の Web ページを作成しているとしたら、これをユーザーにどのように提示するか」と自問します。簡単な答えは、この種のオプションを 1 つのページに表示しないということです。インターフェースが複雑すぎます。しかし、私ができることは、ユーザーが多数のページにわたってますます複雑なクエリを作成できるようにするインターフェイスを提供することです。

HATEOAS 制約は、応答にハイパーメディア コントロール (リンクとフォーム) を含める必要があることを指定します。/carsで、検索オプション付きのページ分割された車のコレクションがあるとしましょう。取得すると、/cars次のようなものが返されます (ところで、ここではカスタム メディア タイプを使用していますが、フォームとリンクはかなり明白なはずです。そうでないかどうかを確認してください):

<cars href="/cars">
    <car href="/cars/alpha">...</car>
    <car href="/cars/beta">...</car>
    <car href="/cars/gamma">...</car>
    <car href="/cars/delta">...</car>
    ...
    <next href="/cars?page=2"/>
    <search-color href="/cars" method="GET">
        <color type="string" cardinality="required"/>
        <color-match type="enumeration" cardinality="optional" default="substring">
            <option name="exact"/>
            <option name="substring"/>
            <option name="regexp"/>
        </color-match>
        <color-logic type="enumeration" cardinality="optional" default="and">
            <option name="and"/>
            <option name="or"/>
            <option name="not"/>
        </color-logic>
    </search>
    <search-doors href="/cars" method="GET">
        <doors type="integer" cardinality="required"/>
        <door-logic type="enumeration" cardinality="required" default="and">
            <option name="and"/>
            <option name="or"/>
            <option name="not"/>
        </door-logic>
    </search>
</cars>

たとえば、白い車を検索すると、GET を実行する/cars?color=whiteと、次のような結果が返される可能性があります。

<cars href="/cars?color=white">
    <car href="/cars/beta">...</car>
    <car href="/cars/delta">...</car>
    ...
    <next href="/cars?color=white&page=2"/>
    <search-color href="/cars?color=white" method="GET">
        <color2 type="string" cardinality="required"/>
        <color2-match type="enumeration" cardinality="optional" default="substring">
            <option name="exact"/>
            <option name="substring"/>
            <option name="regexp"/>
        </color2-match>
        <color2-logic type="enumeration" cardinality="optional" default="and">
            <option name="and"/>
            <option name="or"/>
            <option name="not"/>
        </color2-logic>
    </search>
    <search-doors href="/cars?color=white" method="GET">
        <doors type="integer" cardinality="required"/>
        <door-logic type="enumeration" cardinality="required" default="and">
            <option name="and"/>
            <option name="or"/>
            <option name="not"/>
        </door-logic>
    </search>
</cars>

この結果から、クエリを絞り込みましょう。したがって、「オフホワイト」の車ではなく白い車が必要だと言うだけで、GET '/cars?color=white&color2=off-white&color2-logic=not' が返される可能性があります。

<cars href="/cars?color=white&color2=off-white&color2-logic=not">
    <car href="/cars/beta">...</car>
    <car href="/cars/delta">...</car>
    ...
    <next href="/cars?color=white&color2=off-white&color2-logic=not&page=2"/>
    <search-color href="/cars?color=white&color2=off-white&color2-logic=not" method="GET">
        <color3 type="string" cardinality="required"/>
        <color3-match type="enumeration" cardinality="optional" default="substring">
            <option name="exact"/>
            <option name="substring"/>
            <option name="regexp"/>
        </color3-match>
        <color3-logic type="enumeration" cardinality="optional" default="and">
            <option name="and"/>
            <option name="or"/>
            <option name="not"/>
        </color3-logic>
    </search>
    <search-doors href="/cars?color=white&color2=off-white&color2-logic=not" method="GET">
        <doors type="integer" cardinality="required"/>
        <door-logic type="enumeration" cardinality="required" default="and">
            <option name="and"/>
            <option name="or"/>
            <option name="not"/>
        </door-logic>
    </search>
</cars>

その後、クエリをさらに絞り込むことができますが、ポイントは、途中の各ステップで、ハイパーメディア コントロールが可能なことを教えてくれることです。

ここで、車の検索オプションについて考えてみると、色、ドア、メーカー、およびモデルは無制限ではないため、列挙を提供することでオプションをより明確にすることができます。例えば

<cars href="/cars">
    ...
    <search-doors href="/cars" method="GET">
        <doors type="enumeration" cardinality="required">
            <option name="2"/>
            <option name="3"/>
            <option name="4"/>
            <option name="5"/>
        </doors>
        <door-logic type="enumeration" cardinality="required" default="and">
            <option name="and"/>
            <option name="or"/>
            <option name="not"/>
        </door-logic>
    </search>
</cars>

ただし、私たちが持っている唯一の白い車は 2 ドアと 4 ドアかもしれません。その場合、GETing/cars?color=whiteは私たちに与えるかもしれません

<cars href="/cars?color=white">
    ...
    <search-doors href="/cars?color=white" method="GET">
        <doors type="enumeration" cardinality="required">
            <option name="2"/>
            <option name="4"/>
        </doors>
        <door-logic type="enumeration" cardinality="required" default="and">
            <option name="and"/>
            <option name="or"/>
            <option name="not"/>
        </door-logic>
    </search>
</cars>

同様に、色を絞り込むと、それらのオプションがほんのわずかであることがわかる場合があります。その場合、文字列検索の提供から列挙型検索の提供に切り替えることができます。例えば、GETing/cars?color=whiteは私たちに与えるかもしれません

<cars href="/cars?color=white">
    ...
    <search-color href="/cars?color=white" method="GET">
        <color2 type="enumeration" cardinality="required">
            <option name="white"/>
            <option name="off-white"/>
            <option name="blue with white racing stripes"/>
        </color2>
        <color2-logic type="enumeration" cardinality="optional" default="and">
            <option name="and"/>
            <option name="or"/>
            <option name="not"/>
        </color2-logic>
    </search>
    ...
</cars>

他の検索カテゴリについても同じことができます。たとえば、最初はすべてのメーカーを列挙したくないので、何らかのテキスト検索を提供します。コレクションが洗練され、選択できるモデルがわずかしかなくなったら、列挙を提供することは理にかなっています。同じロジックが他のコレクションにも適用されます。たとえば、世界のすべての都市を列挙する必要はありませんが、地域を 10 程度の都市に絞り込むと、それらを列挙することは非常に役立ちます。

あなたのためにこれを行うライブラリはありますか?私が知っているものはありません。私が見たほとんどのものは、ハイパーメディア コントロールさえサポートしていません (つまり、リンクとフォームを自分で追加する必要があります)。使えるパターンはありますか?はい、上記はこの種の問題を解決するための有効なパターンだと思います。

于 2012-10-29T11:26:18.527 に答える
4

うーん...これはトリッキーな領域です。あなたの問題に合わせて調整されたフレームワークはありません。@p0wl が言及している ODATA でさえ、フィールドがドメイン モデルにマップされる方法について一定の制限があります。あるポイントの後、それは奇妙になります。

これを回避する最も簡単な方法は、クエリを文字列として受け入れ、AST などを使用してドメイン固有の言語に対して検証することです。これにより、クエリの正確性を検証しながら柔軟にクエリを受け入れることができます。失われるのは、URL を介してより意味のある方法でクエリを説明する機能です。

Restful レシピ クックブックの第 8 章をご覧ください。私が提案したソリューションの 1 つを強調し、他のアプローチも推奨します。クライアントが必要とする柔軟性とクエリの表現力に基づいて、いずれかを選択してください。どこかに打ち込めるバランスがあります。

于 2012-10-28T15:53:59.763 に答える
2

@GlennV

これに直接関連するパターン/ルールがあるかどうかはわかりませんが、これが一般的な問題(クエリ文字列の論理演算子を意味する)であるかどうかはわかりませんが、次のようなものを設計するときは、次の質問をします。

  • クライアントはそのようなクエリをどのように構築しますか?

問題は、URI(クエリ文字列部分を含む全体)をサービスプロバイダーが完全に制御する必要があり、クライアントを問題のドメインに固有のURI構築ロジックに直接結合してはならないことです。

クライアントがURIを使用する方法と、URIを構築する方法はいくつかあります。

  • クライアントは、「Link:<...>」ヘッダーを介してHTTPヘッダーに含まれているリンク、またはエンティティ本体(アトムやHTMLリンクなど)に含まれているリンクをたどることができます。
  • クライアントは、メディアタイプまたは他の仕様によって定義されたルールによってURIを構築できます。

ガイド付きURI構築プロセスのいくつかのよく知られたアプローチ:

  • フォームフィールドがRFC3986に従ってエンコードされ、フォームのアクション属性値(メディアタイプガイド)で提供されるURIに追加されるHTMLGETフォーム。
  • URIテンプレート(読む価値はありますが、かなり広いですが、仕様は本当に興味深い機能を提供します)RFC6570
  • 検索クエリを開く(メディアタイプガイド付き)

上記の情報に基づいて、既存のアプローチを選択するか、独自のアプローチを設計できますが、サービスはクライアントがクエリを構築する方法を調整する必要があることを念頭に置いてください(つまり、独自の仕様、カスタムメディアタイプを定義する、または既存を拡張する)。

もう1つは、URIを基盤となる実装にマップする方法であり、RESTに関して2つの非常に重要なことがあります。

  • コネクタ-HTTP+URI仕様が含まれ、実際にはコネクタのみがクライアントにとって重要です。
  • コンポーネント-基礎となる実装であり、あなた以外は誰も気にしません。この部分はクライアントから完全に隠す必要があり、ここで任意のバインディングアプローチを選択できます。正確な方法は誰にもわかりません。これは、特定の要件に完全に依存します。
于 2012-10-28T20:05:13.733 に答える
1

前述のように、これを行うための一般的な解決策はまだありません。おそらく、最良の選択肢 (少なくとも見つかった最良のもの) は、Spring DATA REST ( http://static.springsource.org/spring-data/rest/docs/1.1.0.M1/reference/htmlsingle/ ) です。CrudRepository を使用すると、findOne、findAll などのメソッドを取得できます。また、それを拡張すると、独自の汎用マッピングを追加できます。たとえば、アルゼンチンに属していない顧客を取得するために、customer?country=notArgentina のような一般的なクエリ オプションを持つように拡張しました。または、例: customer?country=like( Island ) は、"Island" のような国を持つすべての顧客を (SQL の方法で) 取得します。それが役に立てば幸い。

于 2013-03-25T00:15:07.803 に答える
1

ODATA ( Link )のようなライブラリを検索しますか?

これにより、クエリの解析の問題が解決され、選択したデータベースにクエリを転送することができます。

于 2012-10-28T15:42:10.110 に答える