{"id":51484775,"url":"https://github.com/ekzo-dev/ruby-rsql","last_synced_at":"2026-07-07T05:01:37.472Z","repository":{"id":365300188,"uuid":"1193444683","full_name":"ekzo-dev/ruby-rsql","owner":"ekzo-dev","description":"RSQL/FIQL parser for Ruby","archived":false,"fork":false,"pushed_at":"2026-03-27T08:55:54.000Z","size":12,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-06-16T20:06:32.205Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Ruby","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/ekzo-dev.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-03-27T08:25:24.000Z","updated_at":"2026-03-27T10:13:39.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/ekzo-dev/ruby-rsql","commit_stats":null,"previous_names":["ekzo-dev/ruby-rsql"],"tags_count":null,"template":false,"template_full_name":null,"purl":"pkg:github/ekzo-dev/ruby-rsql","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ekzo-dev%2Fruby-rsql","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ekzo-dev%2Fruby-rsql/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ekzo-dev%2Fruby-rsql/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ekzo-dev%2Fruby-rsql/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/ekzo-dev","download_url":"https://codeload.github.com/ekzo-dev/ruby-rsql/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ekzo-dev%2Fruby-rsql/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":35215221,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-07-07T02:00:07.222Z","response_time":90,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2026-07-07T05:01:36.753Z","updated_at":"2026-07-07T05:01:37.463Z","avatar_url":"https://github.com/ekzo-dev.png","language":"Ruby","funding_links":[],"categories":[],"sub_categories":[],"readme":"# rsql_parser\n\nA Ruby parser library for **RSQL** and **FIQL** query expressions. Parses query strings into structured Ruby hashes that can be used to build database queries, filter collections, or power search APIs.\n\n- **FIQL** (Feed Item Query Language): [RFC draft](https://datatracker.ietf.org/doc/html/draft-nottingham-atompub-fiql-00)\n- **RSQL**: a superset of FIQL with additional convenience syntax\n\n## Installation\n\nAdd to your `Gemfile`:\n\n```ruby\ngem 'rsql_parser'\n```\n\nOr install directly:\n\n```bash\ngem install rsql_parser\n```\n\n## Quick Start\n\n```ruby\nrequire 'rsql_parser'\n\nresult = RsqlParser.parse('name==\"Kill Bill\";year=gt=2003')\n# =\u003e {\n#      type: :COMBINATION,\n#      operator: :AND,\n#      lhs: { type: :CONSTRAINT, selector: \"name\", comparison: \"==\", argument: \"Kill Bill\" },\n#      rhs: { type: :CONSTRAINT, selector: \"year\", comparison: \"=gt=\", argument: \"2003\" }\n#    }\n```\n\n## Return Value Structure\n\nEvery call to `RsqlParser.parse` returns a **node hash**. There are two node types:\n\n### `:CONSTRAINT` — a single condition\n\n| Key          | Type     | Description                        |\n|--------------|----------|------------------------------------|\n| `:type`      | Symbol   | Always `:CONSTRAINT`               |\n| `:selector`  | String   | The field/attribute name           |\n| `:comparison`| String   | The comparison operator            |\n| `:argument`  | String or Array | The value(s) to compare against |\n\n```ruby\nRsqlParser.parse('year==2003')\n# =\u003e { type: :CONSTRAINT, selector: \"year\", comparison: \"==\", argument: \"2003\" }\n```\n\n### `:COMBINATION` — two conditions joined by a logical operator\n\n| Key         | Type   | Description                         |\n|-------------|--------|-------------------------------------|\n| `:type`     | Symbol | Always `:COMBINATION`               |\n| `:operator` | Symbol | `:AND` or `:OR`                     |\n| `:lhs`      | Hash   | Left-hand side node                 |\n| `:rhs`      | Hash   | Right-hand side node                |\n\n```ruby\nRsqlParser.parse('a==1;b==2')\n# =\u003e {\n#      type: :COMBINATION,\n#      operator: :AND,\n#      lhs: { type: :CONSTRAINT, selector: \"a\", comparison: \"==\", argument: \"1\" },\n#      rhs: { type: :CONSTRAINT, selector: \"b\", comparison: \"==\", argument: \"2\" }\n#    }\n```\n\n## Syntax Reference\n\n### Selectors\n\nA selector is a field name consisting of unreserved characters: letters, digits, and `-._~:`.\n\n```\nname\ncreated_at\nuser.email\nhttp://schema.org/name\n```\n\n### Comparison Operators\n\n#### FIQL / RSQL operators\n\n| Operator  | Meaning                  | Example              |\n|-----------|--------------------------|----------------------|\n| `==`      | Equal                    | `name==Alice`        |\n| `!=`      | Not equal                | `status!=inactive`   |\n| `=gt=`    | Greater than             | `year=gt=2000`       |\n| `=gte=`   | Greater than or equal    | `year=gte=2000`      |\n| `=lt=`    | Less than                | `price=lt=100`       |\n| `=lte=`   | Less than or equal       | `price=lte=100`      |\n| `=in=`    | In a set                 | `status=in=(a,b,c)`  |\n| `=out=`   | Not in a set             | `status=out=(x,y)`   |\n| `=custom=`| Any custom operator      | `field=op=value`     |\n\nCustom FIQL operators follow the pattern `=[a-z!]*=` — any lowercase letters or `!` between two `=` signs.\n\n#### Simplified comparison operators\n\n| Operator | Meaning                  | Example       |\n|----------|--------------------------|---------------|\n| `\u003e`      | Greater than             | `year\u003e2000`   |\n| `\u003e=`     | Greater than or equal    | `year\u003e=2000`  |\n| `\u003c`      | Less than                | `price\u003c100`   |\n| `\u003c=`     | Less than or equal       | `price\u003c=100`  |\n\n### Logical Operators\n\nConditions can be combined with AND and OR. AND has higher precedence than OR.\n\n#### Symbol syntax\n\n| Symbol | Operator | Example           |\n|--------|----------|-------------------|\n| `;`    | AND      | `a==1;b==2`       |\n| `,`    | OR       | `a==1,b==2`       |\n\n#### Keyword syntax (case-insensitive)\n\n| Keyword       | Operator | Example              |\n|---------------|----------|----------------------|\n| `and` / `AND` | AND      | `a==1 and b==2`      |\n| `or` / `OR`   | OR       | `a==1 or b==2`       |\n\nSymbol and keyword syntax can be mixed freely. Whitespace around keywords is ignored.\n\n### Arguments\n\n#### Unquoted values\n\nSequences of unreserved characters (`[a-zA-Z0-9\\-._~:]`):\n\n```\nyear==2003\nstatus==active\ndate==2018-09-01T12:14:28Z\n```\n\n#### Single-quoted strings\n\nAllows spaces, semicolons, commas, and double quotes inside the value. Use `\\'` to include a literal single quote:\n\n```\nname=='Kill;\"Bill\"'\ntag=='it\\'s fine'\n```\n\n#### Double-quoted strings\n\nAllows spaces, semicolons, commas, and single quotes inside the value. Use `\\\"` to include a literal double quote:\n\n```\nname==\"Kill Bill\"\ntitle==\"She said \\\"hello\\\"\"\n```\n\n#### Array arguments\n\nA parenthesised, comma-separated list. Used with operators like `=in=`:\n\n```\nstatus=in=(active,pending,review)\nname=in=(\"Kill Bill\",\"Pulp Fiction\")\n```\n\nThe `:argument` key will contain a Ruby `Array` instead of a `String`:\n\n```ruby\nRsqlParser.parse('genre=in=(sci-fi,action)')\n# =\u003e { type: :CONSTRAINT, selector: \"genre\", comparison: \"=in=\",\n#      argument: [\"sci-fi\", \"action\"] }\n```\n\n### Grouping\n\nParentheses override the default AND-before-OR precedence:\n\n```ruby\n# Without grouping: (a AND b) OR c\nRsqlParser.parse('a==1;b==2,c==3')\n\n# With grouping: a AND (b OR c)\nRsqlParser.parse('a==1;(b==2,c==3)')\n```\n\n## Examples\n\n```ruby\nrequire 'rsql_parser'\n\n# Single constraint\nRsqlParser.parse('year==2003')\n# =\u003e { type: :CONSTRAINT, selector: \"year\", comparison: \"==\", argument: \"2003\" }\n\n# Simplified comparison syntax\nRsqlParser.parse('price\u003c=99')\n# =\u003e { type: :CONSTRAINT, selector: \"price\", comparison: \"\u003c=\", argument: \"99\" }\n\n# AND combination (semicolon and keyword are equivalent)\nRsqlParser.parse('name==\"Kill Bill\" and year=gt=2003')\nRsqlParser.parse('name==\"Kill Bill\";year=gt=2003')\n\n# OR combination\nRsqlParser.parse('status==active or status==pending')\nRsqlParser.parse('status==active,status==pending')\n\n# Array argument\nRsqlParser.parse(\"genre=in=(sci-fi,action);year\u003e2000\")\n# =\u003e {\n#      type: :COMBINATION,\n#      operator: :AND,\n#      lhs: { type: :CONSTRAINT, selector: \"genre\", comparison: \"=in=\",\n#             argument: [\"sci-fi\", \"action\"] },\n#      rhs: { type: :CONSTRAINT, selector: \"year\", comparison: \"\u003e\",\n#             argument: \"2000\" }\n#    }\n\n# Chained AND — right-associative tree\nRsqlParser.parse('a=eq=b;c=ne=d;e=gt=f')\n# =\u003e {\n#      type: :COMBINATION, operator: :AND,\n#      lhs: { type: :CONSTRAINT, selector: \"a\", comparison: \"=eq=\", argument: \"b\" },\n#      rhs: {\n#        type: :COMBINATION, operator: :AND,\n#        lhs: { type: :CONSTRAINT, selector: \"c\", comparison: \"=ne=\", argument: \"d\" },\n#        rhs: { type: :CONSTRAINT, selector: \"e\", comparison: \"=gt=\", argument: \"f\" }\n#      }\n#    }\n\n# Grouping to change precedence\nRsqlParser.parse('a=eq=b;(c=ne=d,e=gt=f)')\n# =\u003e {\n#      type: :COMBINATION, operator: :AND,\n#      lhs: { type: :CONSTRAINT, selector: \"a\", comparison: \"=eq=\", argument: \"b\" },\n#      rhs: {\n#        type: :COMBINATION, operator: :OR,\n#        lhs: { type: :CONSTRAINT, selector: \"c\", comparison: \"=ne=\", argument: \"d\" },\n#        rhs: { type: :CONSTRAINT, selector: \"e\", comparison: \"=gt=\", argument: \"f\" }\n#      }\n#    }\n\n# Escaped quotes inside strings\nRsqlParser.parse('title==\"She said \\\"hello\\\"\"')\n# =\u003e { type: :CONSTRAINT, selector: \"title\", comparison: \"==\",\n#      argument: 'She said \"hello\"' }\n```\n\n## Operator Precedence\n\nFrom highest to lowest:\n\n1. Parentheses `( )`\n2. AND — `;` or `and`\n3. OR — `,` or `or`\n\n## Requirements\n\n- Ruby \u003e= 2.7.0\n- [racc](https://github.com/ruby/racc) ~\u003e 1.8\n\n## Development\n\n```bash\n# Run tests\nrake test\n\n# Regenerate the lexer after editing lib/rsql_parser/lexer.rex\nruby -roedipus_lex -e \"\n  lex = OedipusLex.new\n  lex.parse_file('lib/rsql_parser/lexer.rex')\n  File.write('lib/rsql_parser/lexer.rex.rb', lex.generate)\n\"\n```\n\nDevelopment dependencies: `oedipus_lex ~\u003e 2.6`, `minitest ~\u003e 5.21`, `rake ~\u003e 13.0`.\n\n## Contributing\n\nOpen a pull request with your changes and a corresponding test.\n\n## License\n\nMIT © [Ekzo](https://github.com/ekzo-dev)","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fekzo-dev%2Fruby-rsql","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fekzo-dev%2Fruby-rsql","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fekzo-dev%2Fruby-rsql/lists"}