{"action":"created","issue":{"url":"https://api.github.com/repos/orbitjs/orbit.js/issues/212","labels_url":"https://api.github.com/repos/orbitjs/orbit.js/issues/212/labels{/name}","comments_url":"https://api.github.com/repos/orbitjs/orbit.js/issues/212/comments","events_url":"https://api.github.com/repos/orbitjs/orbit.js/issues/212/events","html_url":"https://github.com/orbitjs/orbit.js/issues/212","id":108990917,"number":212,"title":"Proposal: Orbit Query Expressions","user":{"login":"dgeb","id":29122,"avatar_url":"https://avatars.githubusercontent.com/u/29122?v=3","gravatar_id":"","url":"https://api.github.com/users/dgeb","html_url":"https://github.com/dgeb","followers_url":"https://api.github.com/users/dgeb/followers","following_url":"https://api.github.com/users/dgeb/following{/other_user}","gists_url":"https://api.github.com/users/dgeb/gists{/gist_id}","starred_url":"https://api.github.com/users/dgeb/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/dgeb/subscriptions","organizations_url":"https://api.github.com/users/dgeb/orgs","repos_url":"https://api.github.com/users/dgeb/repos","events_url":"https://api.github.com/users/dgeb/events{/privacy}","received_events_url":"https://api.github.com/users/dgeb/received_events","type":"User","site_admin":false},"labels":[],"state":"open","locked":false,"assignee":null,"milestone":null,"comments":2,"created_at":"2015-09-30T01:08:52Z","updated_at":"2015-09-30T13:26:16Z","closed_at":null,"body":"In order to consolidate all finds and queries into a single `query` method (#202), it's necessary to first solve the problem posed by @gnarf: \"How do we define queries?\" (#192).\r\n\r\n**I'd like to propose \"query expressions\", a flexible and extensible way to define queries.**\r\n\r\n> Note: an early version of this proposal used the name \"OrbitQL\", but I think that name is better left for a higher level representation of query expressions.\r\n\r\nQuery expressions are composable functions that can be expressed in JSON. Every expression has an `op` and `args` (short for \"operation\" and \"arguments\") and returns a value when evaluated. Arguments will be an array of values - whatever makes the most sense for a particular expression.\r\n\r\nSeveral expressions will be shipped by default, although any number of custom expressions could be developed. Not every source has to understand every expression - they can simply throw an `UnsupportedQueryExpressionException` and allow another source to assist or rescue in the query's resolution.\r\n\r\nA simple expression is `get`, which can be used to retrieve data at any path in a JSON document.\r\n\r\nHere's a simple query of all planets:\r\n\r\n```\r\nquery(\r\n  {\r\n    op: 'get',\r\n    args: ['planet']\r\n  }\r\n)\r\n```\r\n\r\nHere's a query to find an individual planet:\r\n\r\n```\r\nquery(\r\n  {\r\n    op: 'get',\r\n    args: ['planet/p1']\r\n  }\r\n)\r\n```\r\n\r\nThis could be written more concisely using an `exp` (short for \"expression\") helper, which simply outputs the JSON form of an expression:\r\n\r\n```\r\nquery(exp('get', 'planet/p1');\r\n```\r\n\r\nA more advanced expression is `filter`, which takes the a path and where expression as ordered arguments.\r\n\r\nThe following query returns planets named `jupiter`:\r\n\r\n```\r\nquery(\r\n  exp('filter',\r\n      'planet',\r\n      exp('===', exp('get', 'attributes/name'), 'jupiter')\r\n  })\r\n});\r\n```\r\n\r\nNote the `===` expression tests any number of arguments for strict equality.\r\n\r\nComplex conditionals could be expressed with `||`, `&&`, `!`, and `!!` expressions.\r\n\r\nThe following query selects all planets named either `jupiter` or `earth`:\r\n\r\n```\r\nquery(\r\n  exp('filter',\r\n      'planet',\r\n      exp('||',\r\n          exp('===', exp('get', 'attributes/name'), 'jupiter'),\r\n          exp('===', exp('get', 'attributes/name'), 'earth'))\r\n  })\r\n});\r\n```\r\n\r\nAnd this query selects all moons of `jupiter` that have a `mass` over 9000:\r\n\r\n```\r\nquery(\r\n  exp('filter',\r\n      'planet',\r\n      exp('&&',\r\n          exp('===', exp('get', 'relationships/planet'), 'planet:jupiter'),\r\n          exp('>', exp('get', 'attributes/mass'), 9000))\r\n  })\r\n});\r\n```\r\n\r\nNote that all of these queries could be expanded to their simple form in JSON without use of the `exp` helper, and therefore would be serializable.\r\n\r\nIt's possible to define custom query expressions in other query languages like [GraphQL](http://facebook.github.io/graphql/):\r\n\r\n```\r\nquery(\r\n  exp('graphql', 'planet(id: p1) { attributes { name } }')\r\n);\r\n```\r\n\r\nA `graphql` expression might only be understood by some future `GraphQLSource`.\r\n\r\nSimilarly, a `get` expression might accept named arguments for pagination, filter, and `include` parameters particular to [JSONAPI](http://jsonapi.org/):\r\n\r\n```\r\nquery(\r\n  exp('get', \r\n      'planet',\r\n      {include: ['moons', 'sun'],\r\n       page: {offset: 0, limit: 10}}\r\n  })\r\n);\r\n```\r\n\r\nThe optional named arguments for a `get` expression might only be understood by the `JSONAPISource`.\r\n\r\n-----\r\n\r\nAs per #202, each Source that's `Queryable` will just have to support the single `query` method, and can of course choose to support only the level of query complexity that's appropriate.\r\n\r\nIt's envisioned that the new [Store](https://github.com/orbitjs/orbit.js/blob/rethink/lib/orbit-common/store.js) will provide convenience methods for performing simple queries, such as `findRecords(type)` and `findRecord(type, id)`.\r\n"},"comment":{"url":"https://api.github.com/repos/orbitjs/orbit.js/issues/comments/144394766","html_url":"https://github.com/orbitjs/orbit.js/issues/212#issuecomment-144394766","issue_url":"https://api.github.com/repos/orbitjs/orbit.js/issues/212","id":144394766,"user":{"login":"dgeb","id":29122,"avatar_url":"https://avatars.githubusercontent.com/u/29122?v=3","gravatar_id":"","url":"https://api.github.com/users/dgeb","html_url":"https://github.com/dgeb","followers_url":"https://api.github.com/users/dgeb/followers","following_url":"https://api.github.com/users/dgeb/following{/other_user}","gists_url":"https://api.github.com/users/dgeb/gists{/gist_id}","starred_url":"https://api.github.com/users/dgeb/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/dgeb/subscriptions","organizations_url":"https://api.github.com/users/dgeb/orgs","repos_url":"https://api.github.com/users/dgeb/repos","events_url":"https://api.github.com/users/dgeb/events{/privacy}","received_events_url":"https://api.github.com/users/dgeb/received_events","type":"User","site_admin":false},"created_at":"2015-09-30T13:26:16Z","updated_at":"2015-09-30T13:26:16Z","body":"As a thought experiment about \"OrbitQL\", it would be possible to distill a query expression such as the following:\r\n\r\n```\r\nquery(\r\n  exp('filter',\r\n      'planet',\r\n      exp('&&',\r\n          exp('===', exp('get', 'relationships/planet'), 'planet:jupiter'),\r\n          exp('>', exp('get', 'attributes/mass'), 9000))\r\n  })\r\n});\r\n```\r\n\r\nInto the following OrbitQL query string: \r\n\r\n```\r\nquery(\r\n  `filter('planet',\r\n          and( strictEqual( get('relationships/planet'), 'planet:jupiter' ),\r\n               gt( get('attributes/mass'), 9000) ))`\r\n});\r\n```\r\n\r\nThis leads me to think that expression operations should be words instead of symbols.\r\n\r\nAnyone want to write a parser? :stuck_out_tongue: "}}