sql - 如何在 API 的查询字符串中设计通用过滤运算符?

标签 sql database rest api

我正在构建一个具有内容和可以由用户定义的架构的通用 API。我想向 API 响应添加过滤逻辑,以便用户可以查询他们存储在 API 中的特定对象。例如,如果用户正在存储事件对象,他们可以执行诸如过滤之类的操作:

  • 数组包含 : 是否properties.categories包含 Engineering
  • 大于 : 是否properties.created_at早于 2016-10-02
  • 不等于 : 是否properties.address.city不是 Washington
  • 等于 : 是否properties.nameMeetup

  • 我正在尝试将过滤设计为 API 响应的查询字符串,并提出了一些选项,但我不确定哪种语法最好......

    1. 操作符作为嵌套键
    /events?properties.name=Harry&properties.address.city.neq=Washington
    
    此示例仅使用嵌套对象来指定运算符(如 neq,如图所示)。这很好,因为它非常简单,易于阅读。
    但在用户可以定义事件属性的情况下,它会遇到名为 address.city.neq 的属性之间存在潜在冲突的问题。使用普通的相等运算符和名为 address.city 的属性使用不等运算符。
    示例:Stripe's API

    2. 操作符作为键后缀
    /events?properties.name=Harry&properties.address.city+neq=Washington
    
    此示例与第一个示例类似,不同之处在于它使用了 +用于操作的分隔符(相当于一个空格),而不是 .以免混淆,因为我的域中的键不能包含空格。
    一个缺点是它稍微难以阅读,尽管这是有争议的,因为它可能被解释为更清晰。另一个可能是它稍微难以解析,但不是那么难。

    3. 运算符作为值前缀
    /events?properties.name=Harry&properties.address.city=neq:Washington
    
    此示例与前一个示例非常相似,不同之处在于它将运算符语法移动到参数的值而不是键中。这样做的好处是消除了解析查询字符串的一些复杂性。
    但这样做的代价是不再能够区分检查文字字符串 neq:Washington 的相等运算符。以及检查字符串 Washington 的不等运算符.
    示例:Sparkpay's API

    4.自定义过滤参数
    /events?filter=properties.name==Harry;properties.address.city!=Washington
    
    此示例使用单个顶级查询参数 filter , 命名下所有过滤逻辑。这很好,因为您永远不必担心顶级命名空间冲突。 (尽管就我而言,所有自定义内容都嵌套在 properties. 下,因此这首先不是问题。)
    但这样做的代价是,当您想要进行基本的相等过滤时,需要输入更难的查询字符串,这可能导致大多数时候必须检查文档。并且依赖运算符的符号可能会导致对非明显操作(例如“near”或“within”或“contains”)的混淆。
    示例:Google Analytics's API

    5.自定义详细过滤器参数
    /events?filter=properties.name eq Harry; properties.address.city neq Washington
    
    这个例子使用了一个类似的顶级 filter参数与前一个相同,但它用单词拼出运算符而不是用符号定义它们,并且它们之间有空格。这可能稍微更具可读性。
    但这是以拥有更长的 URL 和需要编码的大量空间为代价的吗?
    示例:OData's API

    6. 对象过滤参数
    /events?filter[1][key]=properties.name&filter[1][eq]=Harry&filter[2][key]=properties.address.city&filter[2][neq]=Washington
    
    此示例还使用顶级 filter参数,但不是为其创建模拟编程的完全自定义语法,而是使用更标准的查询字符串语法构建过滤器的对象定义。这具有带来更多“标准”的好处。
    但它的代价是输入非常冗长且难以解析。
    示例 Magento's API

    鉴于所有这些示例或不同的方法,哪种语法最好?理想情况下,构造查询参数会很容易,因此在 URL 栏中进行操作是可行的,但也不会对 future 的互操作性造成问题。
    我倾向于 #2 因为它看起来清晰易读,但也没有其他方案的一些缺点。

    最佳答案

    我可能不会回答“哪个最好”的问题,但我至少可以给你一些见解和其他例子供你考虑。

    首先,您谈论的是“具有内容和可以由用户定义的模式的通用 API”。

    这听起来很像 solr/elasticsearch它们都是 Apache Lucene 上的高级包装器它基本上索引和聚合文档。

    这两个人对他们的 REST API 采取了完全不同的方法,我碰巧与他们一起工作。

    Elasticsearch :

    他们制作了整个基于 JSON 的 Query DSL,目前看起来像这样:

    GET /_search
    {
      "query": { 
        "bool": { 
          "must": [
            { "match": { "title":   "Search"        }}, 
            { "match": { "content": "Elasticsearch" }}  
          ],
          "filter": [ 
            { "term":  { "status": "published" }}, 
            { "range": { "publish_date": { "gte": "2015-01-01" }}} 
          ]
        }
      }
    }
    

    取自他们目前的 doc .我很惊讶您居然可以将数据放入 获取 ...
    它现在看起来更好,在早期版本中它更多hierarchical .

    根据我的个人经验,这个 DSL 很强大,但很难流利地学习和使用(尤其是旧版本)。要真正获得一些结果,您需要的不仅仅是使用 URL。许多客户端甚至不支持 中的数据。获取 要求。

    SOLR :

    他们将所有内容放入查询参数中,基本上如下所示(取自 doc ):
    q=*:*&fq={!cache=false cost=5}inStock:true&fq={!frange l=1 u=4 cache=false cost=50}sqrt(popularity)
    

    使用它更简单。但这只是我个人的口味。

    现在说说我的经历。我们在这两层之上实现了另一层,我们采用了方法编号 #4. 其实我觉得 #4 #5 应该同时支持。为什么?因为无论您选择什么,人们都会提示,而且无论如何您都将拥有自己的“微型 DSL”,因此您最好为关键字提供更多别名。

    为什么不 #2 ?在内部拥有单个过滤器参数和查询可以让您完全控制 DSL。在我们制作资源半年后,我们收到了“简单”的功能请求 - 合乎逻辑的 OR和括号() .查询参数基本都是AND的列表操作和逻辑 OR喜欢 city=London OR age>25真的不适合那里。另一方面括号将嵌套引入到 DSL 结构中,这在平面查询字符串结构中也是一个问题。

    好吧,这些是我们偶然发现的问题,您的情况可能会有所不同。但是仍然值得考虑,这个 API 的 future 期望是什么。

    关于sql - 如何在 API 的查询字符串中设计通用过滤运算符?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/40618327/

    相关文章:

    javascript - 如何使用 Sequelize.js 获取 MAX(id) GROUP BY 其他字段?

    sql - 更新两列排序的累积总和

    java - 验证 JAX-RS 中 PUT、POST 方法的空主体请求

    mysql - 带有 "where in"子句的嵌套 MySql Select 语句

    mysql - 简而言之,Hadoop与SQL之间的关系到底是什么? Hadoop会淘汰SQL吗?请建议

    sql - 在 "eventual"一致性和无事务(又名 SimpleDB)的情况下,你真的能走多远?

    Python SQL 单元交互

    mysql - 如何在 MySQL 中创建复合自引用外键?

    java - 与 Jackson 的一对多 Json 响应不起作用

    java - 从 CSR 文件获取 SSL 证书