我正在寻找一种在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服务调用,......
这两个例子很简单,但我还需要更复杂的搜索功能,如子字符串搜索,日期之前/之后搜索,NOT,...
这是我认为的常见问题。是否有我可以使用的库(或模式):
亲切的问候,
格伦
答案 0 :(得分:5)
每当我遇到这些问题时,我会问自己“如果我创建一个传统的网页,我会如何向用户展示”?简单的答案是我不会在一个页面中提供这些选项。界面太复杂了;但是,我能做的是提供一个界面,允许用户在许多页面上构建越来越复杂的查询,这是我认为在这种情况下你应该采用的解决方案。
The HATEOAS constraint指定我们必须在响应中包含超媒体控件(链接和表单)。所以我们假设我们在/cars
处有一个带有搜索选项的分段汽车集合,这样当你得到/cars
它会返回类似的东西(BTW我在这里使用自定义媒体类型,但是表格和链接应该非常明显。如果不是,请告诉我们:
<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>
所以,只要说我们搜索白色汽车,我们就会获得/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&amp; color2 = off-white&amp; 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个左右的城市,那么枚举它们会非常有用。
是否有图书馆会为您执行此操作?我不知道。我见过的大多数人甚至不支持超媒体控件(即you've got to add the links and forms yourself)。你有可以使用的模式吗?是的,我相信以上是解决此类问题的有效模式。
答案 1 :(得分:4)
解决此问题的最简单方法是将查询作为String接受,然后使用AST或您想要的任何内容对域特定语言进行验证。这使您可以在接受查询时保持灵活性,同时仍然可以验证查询的正确性。您失去的是能够通过URL以更有意义的方式描述查询。
看看Restful食谱食谱的chapter 8。它强调了我提出的解决方案之一,并建议采用其他方法。根据客户所需的灵活性与查询的表现力选择一个。你可以在某个地方找到平衡点。
答案 2 :(得分:2)
@GlennV
我不确定是否存在与此直接相关的模式/规则,如果这是一个常见问题(我的意思是查询字符串中的逻辑运算符),但在设计这样的事情时我会问自己以下问题:
问题在于URI(作为一个整体,包括查询字符串部分)必须完全由服务提供者控制,并且客户端不能直接耦合到特定于您的问题域的URI构造逻辑。
客户端如何使用URI以及如何构建它们有多种方式:
引导URI构建过程的几种众所周知的方法:
根据上述信息,您可以选择现有方法或设计自己的方法,但请记住,服务必须协调客户如何构建查询(即定义您自己的规范,自定义媒体类型或扩展现有的)。
另一件事是你如何将URI映射到底层实现,并且在REST方面有两个非常重要的事情:
答案 3 :(得分:1)
您是否搜索像ODATA(Link)这样的库?
这将解决您的查询解析问题,您只需将查询转发到您选择的数据库。
答案 4 :(得分:1)
如前所述,仍然没有通用的解决方案来做到这一点。可能最好的选择(至少发现最好的ive)是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 )让所有客户都拥有像#34; Island&#34; (以SQL方式)。希望它有所帮助。