{"id":25425011,"url":"https://github.com/defer-panic/dumbql","last_synced_at":"2025-05-14T01:33:35.852Z","repository":{"id":276701825,"uuid":"927148308","full_name":"defer-panic/dumbql","owner":"defer-panic","description":"Simple query language","archived":false,"fork":false,"pushed_at":"2025-02-10T00:05:22.000Z","size":104,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-02-10T00:17:13.918Z","etag":null,"topics":["parser","query-language"],"latest_commit_sha":null,"homepage":"","language":"Go","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/defer-panic.png","metadata":{"files":{"readme":"README.md","changelog":null,"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}},"created_at":"2025-02-04T13:40:45.000Z","updated_at":"2025-02-10T00:05:22.000Z","dependencies_parsed_at":"2025-02-10T00:27:15.437Z","dependency_job_id":null,"html_url":"https://github.com/defer-panic/dumbql","commit_stats":null,"previous_names":["defer-panic/dumbql"],"tags_count":1,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/defer-panic%2Fdumbql","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/defer-panic%2Fdumbql/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/defer-panic%2Fdumbql/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/defer-panic%2Fdumbql/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/defer-panic","download_url":"https://codeload.github.com/defer-panic/dumbql/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":254050886,"owners_count":22006377,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","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":["parser","query-language"],"created_at":"2025-02-16T23:18:56.511Z","updated_at":"2025-05-14T01:33:35.810Z","avatar_url":"https://github.com/defer-panic.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"# dumbql ![GitHub go.mod Go version](https://img.shields.io/github/go-mod/go-version/defer-panic/dumbql) ![GitHub License](https://img.shields.io/github/license/defer-panic/dumbql) ![GitHub Tag](https://img.shields.io/github/v/tag/defer-panic/dumbql) [![Go Report Card](https://goreportcard.com/badge/github.com/defer-panic/dumbql)](https://goreportcard.com/report/github.com/defer-panic/dumbql) [![CI](https://github.com/defer-panic/dumbql/actions/workflows/main.yml/badge.svg)](https://github.com/defer-panic/dumbql/actions/workflows/main.yml) [![Coverage Status](https://coveralls.io/repos/github/defer-panic/dumbql/badge.svg?branch=main)](https://coveralls.io/github/defer-panic/dumbql?branch=main) [![Go Reference](https://pkg.go.dev/badge/github.com/defer-panic/dumbql.svg)](https://pkg.go.dev/github.com/defer-panic/dumbql)\n\n\u003e [!WARNING]  \n\u003e This version of DumbQL is archived. Further development will take place here: https://github.com/tomakado/dumbql\n\nSimple (dumb) query language and parser for Go. \n\n## Features\n\n- Field expressions (`age \u003e= 18`, `field.name:\"field value\"`, etc.)\n- Boolean expressions (`age \u003e= 18 and city = Barcelona`, `occupation = designer or occupation = \"ux analyst\"`)\n- One-of/In expressions (`occupation = [designer, \"ux analyst\"]`)\n- Schema validation\n- Drop-in usage with [squirrel](https://github.com/Masterminds/squirrel) query builder or SQL drivers directly\n- Struct matching with `dumbql` struct tag\n\n## Examples\n\n### Simple parse\n\n```go\npackage main\n\nimport (\n    \"fmt\"\n\n    \"github.com/defer-panic/dumbql\"\n)\n\nfunc main() {\n    const q = `profile.age \u003e= 18 and profile.city = Barcelona`\n    ast, err := dumbql.Parse(q)\n    if err != nil {\n        panic(err)\n    }\n\n    fmt.Println(ast)\n    // Output: (and (\u003e= profile.age 18) (= profile.city \"Barcelona\"))\n}\n```\n\n### Validation against schema\n\n```go\npackage main\n\nimport (\n    \"fmt\"\n\n    \"github.com/defer-panic/dumbql\"\n    \"github.com/defer-panic/dumbql/schema\"\n)\n\nfunc main() {\n    schm := schema.Schema{\n        \"status\": schema.All(\n            schema.Is[string](),\n            schema.EqualsOneOf(\"pending\", \"approved\", \"rejected\"),\n        ),\n        \"period_months\": schema.Max(int64(3)),\n        \"title\":         schema.LenInRange(1, 100),\n    }\n\n    // The following query is invalid against the schema:\n    // \t- period_months == 4, but max allowed value is 3\n    // \t- field `name` is not described in the schema\n    //\n    // Invalid parts of the query are dropped.\n    const q = `status:pending and period_months:4 and (title:\"hello world\" or name:\"John Doe\")`\n    expr, err := dumbql.Parse(q)\n    if err != nil {\n        panic(err)\n    }\n\n    validated, err := expr.Validate(schm)\n    fmt.Println(validated)\n    fmt.Printf(\"validation error: %v\\n\", err)\n    // Output: \n    // (and (= status \"pending\") (= title \"hello world\"))\n    // validation error: field \"period_months\": value must be equal or less than 3, got 4; field \"name\" not found in schema\n}\n```\n\n### Convert to SQL\n\n```go\npackage main\n\nimport (\n  \"fmt\"\n\n  sq \"github.com/Masterminds/squirrel\"\n  \"github.com/defer-panic/dumbql\"\n)\n\nfunc main() {\n  const q = `status:pending and period_months \u003c 4 and (title:\"hello world\" or name:\"John Doe\")`\n  expr, err := dumbql.Parse(q)\n  if err != nil {\n    panic(err)\n  }\n\n  sql, args, err := sq.Select(\"*\").\n    From(\"users\").\n    Where(expr).\n    ToSql()\n  if err != nil {\n    panic(err)\n  }\n\n  fmt.Println(sql)\n  fmt.Println(args)\n  // Output: \n  // SELECT * FROM users WHERE ((status = ? AND period_months \u003c ?) AND (title = ? OR name = ?))\n  // [pending 4 hello world John Doe]\n}\n```\n\nSee [dumbql_example_test.go](dumbql_example_test.go)\n\n### Match against structs\n\n```go\npackage main\n\nimport (\n  \"fmt\"\n\n  \"github.com/defer-panic/dumbql\"\n  \"github.com/defer-panic/dumbql/match\"\n  \"github.com/defer-panic/dumbql/query\"\n)\n\ntype User struct {\n  ID       int64   `dumbql:\"id\"`\n  Name     string  `dumbql:\"name\"`\n  Age      int64   `dumbql:\"age\"`\n  Score    float64 `dumbql:\"score\"`\n  Location string  `dumbql:\"location\"`\n  Role     string  `dumbql:\"role\"`\n}\n\nfunc main() {\n  users := []User{\n    {\n      ID:       1,\n      Name:     \"John Doe\",\n      Age:      30,\n      Score:    4.5,\n      Location: \"New York\",\n      Role:     \"admin\",\n    },\n    {\n      ID:       2,\n      Name:     \"Jane Smith\",\n      Age:      25,\n      Score:    3.8,\n      Location: \"Los Angeles\",\n      Role:     \"user\",\n    },\n    {\n      ID:       3,\n      Name:     \"Bob Johnson\",\n      Age:      35,\n      Score:    4.2,\n      Location: \"Chicago\",\n      Role:     \"user\",\n    },\n    // This one will be dropped:\n    {\n      ID:       4,\n      Name:     \"Alice Smith\",\n      Age:      25,\n      Score:    3.8,\n      Location: \"Los Angeles\",\n      Role:     \"admin\",\n    },\n  }\n\n  q := `(age \u003e= 30 and score \u003e 4.0) or (location:\"Los Angeles\" and role:\"user\")`\n  ast, _ := query.Parse(\"test\", []byte(q))\n  expr := ast.(query.Expr)\n\n  matcher := \u0026match.StructMatcher{}\n\n  filtered := make([]User, 0, len(users))\n\n  for _, user := range users {\n    if expr.Match(\u0026user, matcher) {\n      filtered = append(filtered, user)\n    }\n  }\n\n  fmt.Println(filtered)\n  // [{1 John Doe 30 4.5 New York admin} {2 Jane Smith 25 3.8 Los Angeles user} {3 Bob Johnson 35 4.2 Chicago user}]\n}\n```\n\nSee [match_example_test.go](match_example_test.go) for more examples.\n\n## Query syntax\n\nThis section is a non-formal description of DumbQL syntax. For strict description see [grammar file](query/grammar.peg).\n\n### Field expression\n\nField name \u0026 value pair divided by operator. Field name is any alphanumeric identifier (with underscore), value can be string, int64 or floa64.\nOne-of expression is also supported (see below).\n\n```\n\u003cfield_name\u003e \u003coperator\u003e \u003cvalue\u003e\n```\n\nfor example\n\n```\nperiod_months \u003c 4\n```\n\n### Field expression operators\n\n| Operator             | Meaning                       | Supported types              |\n|----------------------|-------------------------------|------------------------------|\n| `:` or `=`           | Equal, one of                 | `int64`, `float64`, `string` |\n| `!=` or `!:`         | Not equal                     | `int64`, `float64`, `string` |\n| `~`                  | “Like” or “contains” operator | `string`                     |\n| `\u003e`, `\u003e=`, `\u003c`, `\u003c=` | Comparison                    | `int64`, `float64`           |\n\n\n### Boolean operators\n\nMultiple field expression can be combined into boolean expressions with `and` (`AND`) or `or` (`OR`) operators:\n\n```\nstatus:pending and period_months \u003c 4 and (title:\"hello world\" or name:\"John Doe\")\n```\n\n### “One of” expression\n\nSometimes instead of multiple `and`/`or` clauses against the same field:\n\n```\noccupation = designer or occupation = \"ux analyst\"\n```\n\nit's more convenient to use equivalent “one of” expressions:\n\n```\noccupation: [designer, \"ux analyst\"]\n```\n\n### Numbers\n\nIf number does not have digits after `.` it's treated as integer and stored as `int64`. And it's `float64` otherwise.\n\n### Strings\n\nString is a sequence on Unicode characters surrounded by double quotes (`\"`). In some cases like single word it's possible to write string value without double quotes.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdefer-panic%2Fdumbql","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdefer-panic%2Fdumbql","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdefer-panic%2Fdumbql/lists"}