REST和复杂的搜索查询(REST and complex search queries)

2019-07-02 20:10发布

我在寻找一个可靠的方法在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的标准API,另一个Web服务调用,...

在两个例子是很简单的支持,但我也需要像字符串搜索更复杂的搜索功能,搜索前/后日期,NOT ...

这是一个常见的问题,我认为。 是否有一个库(或模式),我可以使用:

  • 地图搜索指定为字符串到一个通用的标准模型的查询。 搜索格式不必是一样的我上面列出。
  • 让我来映射的是标准模式,我需要使用任何技术
  • 提供了休眠/ JPA / SQL映射支持,但是这是一个奖金;)

亲切的问候,

格伦

Answer 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>

所以刚才说我们搜索的白色轿车,我们会得到/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>

这一结果则让我们改善我们的查询。 所以刚才说,我们希望白色汽车,但不是“灰白色”车,那么我们可以得到“/汽车?颜色=白色&颜色2 =灰白色&COLOR2逻辑=不”,这可能会返回

<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门,在这种情况下歌厅/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>

同样,作为我们改进的颜色,我们可能会发现他们的只有少数几个选项,在这种情况下,我们可以提供字符串搜索,提供一个枚举搜索切换。 例如,歌厅/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个左右的城市,然后枚举他们可以是非常有帮助的。

是否有会为你做这个图书馆吗? 没有,我知道的。 大多数我见过甚至不支持超媒体控件(即你要添加的联系,并形成自己 )。 有没有可以使用的模式? 是的,我相信上面是解决这类问题的有效方式。



Answer 2:

嗯...这是一个棘手的区域。 有在那里没有框架,这对于你的问题量身定制。 即使是ODATA @ p0wl提到有大约字段映射到域模型的方式一定的局限性。 这点之后会很奇怪。

要解决这个问题最简单的方法是接受查询作为一个字符串,然后你验证对使用AST或不管它是什么,你想要一个领域特定语言。 这可以让你在接受查询,同时还验证它的正确性是灵活的。 什么,你失去的是通过URL来描述一个更有意义的方式查询的能力。

看看第8章的宁静食谱菜谱。 它突出了我提出的解决方案之一,还建议其他方法。 选择一个基于您的客户对您的查询的表现所需的灵活性。 有你的地方可以取得平衡。



Answer 3:

@GlennV

我不知道有直接关系,这种模式/规则,如果这是一个常见的问题(我的意思是在查询字符串逻辑运算符),但我会问设计这样的事情时,以下问题对自己说:

  • 如何客户端将构建这样的查询?

的问题是,的URI(作为一个整体,包括查询字符串部分)必须由服务提供商完全控制,并且客户端不能直接地耦合到URI构造逻辑,其是特定于您的问题域。

有几种方法客户端如何使用URI,以及他们如何构建它们:

  • 客户端的可以通过跟随被包括或者在HTTP头中的链接“链接:<...>”页眉或实体主体(如原子或HTML链接或类似的东西)
  • 客户可以通过媒体类型或其他规范定义的规则构建的URI。

引导URI施工过程中的一些众所周知的方法:

  • HTML GET其中表单字段是根据编码形式RFC3986和附加到表单的action属性值所提供的URI(媒体类型的指导下);
  • URI模板(相当广泛,虽然实在值得阅读,规范提供了非常有趣的功能) RFC6570
  • 打开搜索查询(媒体类型的指导下)

根据上述信息,您可以选择现有的方法或设计你自己的,但在考虑简单的规则,服务必须协调的客户,他们是如何构造查询(即定义自己的规格,定制的媒体类型,或扩展现有的)。

另一件事是,你将如何URI的映射到底层实现,并有在REST方面两个非常重要的事情:

  • 连接器 -其中包括HTTP + URI规范,为客户实际上只连接器的问题。
  • 组件 -这是基础实现和除了你没有人关心。 这部分需要从客户完全隐藏,并且你可以选择任何有约束力的方式在这里,没有人能告诉你确切的方式,这完全取决于具体的要求(一个或多个)。


Answer 4:

你搜索像ODATA(库链接 )?

这将解决您的查询解析的问题,你可以只查询转发到您选择的数据库。



Answer 5:

正如前面提到的,仍然心不是一个通用的解决方案来做到这一点。 也许,最好的选择(在tleast最好的香港专业教育学院发现)是春节DATA REST( http://static.springsource.org/spring-data/rest/docs/1.1.0.M1/reference/htmlsingle/ )。 使用CrudRepository你可以像findOne,的findAll等方法,如果你扩展它,你可以添加自己的通用映射。 在例子中,我们所做的是把它扩大到有像通用查询选项:客户国家= notArgentina,为了得到不属于阿根廷的客户。 或者,例如:客户国家=像( ),以获得各界客户提供全国像“岛”(在SQL的方式)。 希望能帮助到你。



文章来源: REST and complex search queries
标签: java rest search