在 Ember 中写一个 Or 过滤器

Write an Or filter in Ember

我想在 Ember 中编写一个 Or 过滤器。我要实现的过滤器如下:

(end_date <= filter_end_date && end_date >= filter_start_date) || (start_date >= filter_start_date && start_date <= filter_end_date) || (start_date <= filter_start_date && end_date >= filter_end_date)

我将所有过滤器收集在一起,然后在发送查询时传递它:

filterOptions: [
    {
      name : 'state',
      op   : 'eq',
      val  : 'published'
    }
  ]

查询如下:

return this.get('store').query('event', {
      sort   : 'starts-at',
      filter : filterOptions
    });

我浏览了文档,但没有找到任何东西。对此过滤器建模的最佳方法是什么?

您要求的是您的 REST API 使用的过滤策略。这不是 ember.js 特有的。您正在使用的 query 方法的 store 服务由 ember-data 提供。 Ember-data 是 ember 的默认持久层,默认包含 ember-cli 的蓝图到新项目中。

默认情况下 ember-data 正在执行 JSON API specification。该规范注册了用于过滤的 filter 查询参数,但不知道所使用的过滤策略:

Note: JSON API is agnostic about the strategies supported by a server. The filter query parameter can be used as the basis for any number of filtering strategies. (Source)

只有一个过滤建议,它解决了通过关联记录的 ID 进行过滤的问题:

It’s recommended that servers that wish to support filtering of a resource collection based upon associations do so by allowing query parameters that combine filter with the association name.

For example, the following is a request for all comments associated with a particular post:

GET /comments?filter[post]=1 HTTP/1.1

Source

ember-data 中内置的唯一过滤策略是通过所请求资源的多个 ID 进行过滤。如果使用内置 JSONAPI 适配器并且 coalesceFindRequests optiontrue,则使用它。它默认为 false。在那种情况下,ember-data 将对一个资源的特定记录的多个请求合并到一个 GET 查询中,以避免触发多个查询。它使用 filter 查询参数和用于过滤的 属性 (id),用方括号和逗号分隔的 ID 列表括起来。例如。对 ID 为 1 和 2 的帖子的 /posts 端点查询如下所示:GET /posts?filter[id]=1,2.

因此,将所有现有部分放在一起只是关于按(关联)资源的 ID 进行过滤。

有一个很好的理由 JSON API 规范与过滤策略无关:一个非常合适的过滤策略是高度特定于应用程序的。在 REST API 上支持 SQL 的全部功能可能会给 API.

带来很大的性能问题

但是有很多复杂的过滤策略,可以实现并且很符合JSONAPI和ember的coalesceFindRequests特性的推荐-数据。

例如可以采用filtering syntax used by OpenStack。该方法允许将用冒号与值分隔的运算符添加到过滤器中。对于请求 2018 年 1 月 1 日之后发布的所有帖子的查询,它看起来像:GET /posts?filter[published-at]=gt:2018-01-01。请注意,我还假设使用驼峰式字段名称和 ISO-8601 日期字符串。但是,此语法不支持 or 运算符。对于不将 or 运算符的复杂性置于 REST API 过滤策略中存在一些争论。通过使用分离的查询并合并返回的集合,您可以轻松获得该选项。

更复杂的过滤策略是 Resource Query Language (RQL)。那一个用等号分隔运算符和值。因此,上面示例中使用的请求如下所示: GET /posts?filter[published-at]=gt=2018-01-01 RQL 还支持 or 运算符,使用括号对由竖线分隔的表达式进行分组。对 2018 年 1 月 1 日之后但 2018 年 2 月 1 日之前发布的帖子的查询如下所示:GET /posts?(filter[published-at]=gt=2018-01-01|filter[published-at]=lt=2018-02-01) 然而,有人可能会争辩说,这不是查询参数的一种非常常见的语法,可能会导致 Web 服务器、缓存、代理等出现问题。

由于ember-data通过store.query() directly forward to jQuery.ajax()的第二个参数作为data设置,您完全可以根据您的应用需求自由选择过滤策略。

是的,@jelhan 是正确的。我们使用 Flask-REST-JSONAPI 作为过滤系统。过滤系统与 ResourceList 管理器使用的数据层完全相关。所以,我使用 https://flask-rest-jsonapi.readthedocs.io/en/latest/filtering.html 作为参考,并将过滤器编写如下:

dateFilterWithEndDate: computed('start_date', 'end_date', function() {
    return EmberObject.create(
      {
        or:
          [
            {
              and: [
                {
                  name : 'starts-at',
                  op   : 'ge',
                  val  : this.get('start_date')
                },
                {
                  name : 'starts-at',
                  op   : 'le',
                  val  : this.get('end_date')
                }
              ]
            },
            {
              and: [
                {
                  name : 'ends-at',
                  op   : 'ge',
                  val  : this.get('start_date')
                },
                {
                  name : 'ends-at',
                  op   : 'le',
                  val  : this.get('end_date')
                }
              ]
            },
            {
              and: [
                {
                  name : 'starts-at',
                  op   : 'le',
                  val  : this.get('start_date')
                },
                {
                  name : 'ends-at',
                  op   : 'ge',
                  val  : this.get('end_date')
                }
              ]
            }
          ]
      }

    );
  }),

  dateFilterBasic: computed('start_date', function() {
    return EmberObject.create(
      {
        or: [
          {
            name : 'starts-at',
            op   : 'ge',
            val  : this.get('start_date')
          },
          {
            name : 'ends-at',
            op   : 'ge',
            val  : this.get('start_date')
          }
        ]
      }
    );
  }),

我在查询方法中的filter参数中添加如下:

    if (endDate) {
      filterOptions.pushObject(this.get('dateFilterWithEndDate'));
    } else {
      filterOptions.pushObject(this.get('dateFilterBasic'));
    }

    return this.get('store').query('event', {
      sort   : 'starts-at',
      filter : filterOptions
    });