Skip to content
This repository has been archived by the owner on Mar 2, 2022. It is now read-only.

Proposal

Hannah Wolfe edited this page Jul 17, 2018 · 2 revisions

Adding advanced filtering to JSONAPI via a top-level filter parameter which accepts a string representation of an MongoDB query object, highly optimised for the kinds of queries which are common to Ghost, but flexible enough to grow outside of anticipated use cases.

Top-Level Filter Parameter

A top-level filter parameter will provide advanced querying capabilities available in varying contexts in a standard way, such that the parameter's value is always a string:

  • HTTP: GET /api/posts?limit=5&filter=???
  • Method Call: api.posts.browse({limit: 5, filter: '???'})
  • Handlebars Helper: {{#get "posts" limit="5" filter="???"}}

Careful handling of quotes, and the ability to URL encode are a requirement.

  • Pojo Example:
apiOptions = {
   filter: 'slug:-' + slug + '+published_at:' + op + '\'' + publishedAt + '\'
};
  • Pojo ES6 Example:
apiOptions = {
   filter: `slug:-${slug}+published_at:${op}'${publishedAt}'`
};

NQL Requirements

The key requirement of NQL is to parse values which can represent a MongoDB query object. The fundamentals of this proposal are therefore the definition of a language which fits Ghost's use cases and can be converted into MongoDB JSON.

MongoDB Query capabilities are extensive. To start with, the focus for Ghost is on supporting logical and & or combinations, comparisons using equals, greater than & less than (with support for not). The importance of the relation between Post & Tags also drives the need to support in.

Filter Anatomy

An individual filter clause is composed of 3 parts, the property that is being filtered, the operator and the value. Filters need to be combined in groups of or & and rules.

The property needs to support aliasing: e.g. tag currently really means tag.slug.

The operators need to include equals, not equals, and numerical comparisons.

The values are strings, numbers, nulls etc that are matched/compared against.

Filter Style / Syntax

There are a number of common styles for combining properties, operators and values into expressions. This proposal borrows heavily from the style used in GitHub, Gmail & Slack amongst others: E.g.label:api or from:-hannah or stars:<10.

The syntax of this style is the use of property-operator-value triplets in the form: property:operatorvalue, where the colon is a separator, and operator is optional.

This syntax is short and compact for the common use cases in Ghost, for example: featured:true, tags:photo, author:john, image:-null, posts.count:>10

This syntax is also flexible enough to be extended to support multiple complex expressions by combining the property-operator-value triplets together with other symbols to represent and & or. Following on from using - for negation, the proposal is to use + for and as well as , for or e.g: featured:true+tags.count:>10, tags:photo,tags:video.

This is then used inside of the filter="" parameter, E.g:

  • HTTP: GET /api/posts?limit=5&filter=tags:photo,featured:true
  • Method Call: api.posts.browse({limit: 5, filter: "tags:photo,featured:true"})
  • Get Helper: {{#get "posts" limit="5" filter="tags:photo,featured:true"}}
Clone this wiki locally