{"id":13694437,"url":"https://github.com/caibirdme/yql","last_synced_at":"2026-01-17T02:24:42.828Z","repository":{"id":57486850,"uuid":"120152244","full_name":"caibirdme/yql","owner":"caibirdme","description":"yet another query language for rule engine in golang","archived":false,"fork":false,"pushed_at":"2022-03-01T03:42:20.000Z","size":75,"stargazers_count":315,"open_issues_count":9,"forks_count":52,"subscribers_count":11,"default_branch":"master","last_synced_at":"2025-05-03T01:47:12.583Z","etag":null,"topics":["dsl","go","rule-engine"],"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/caibirdme.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}},"created_at":"2018-02-04T03:41:10.000Z","updated_at":"2025-04-14T09:16:06.000Z","dependencies_parsed_at":"2022-09-01T23:00:56.416Z","dependency_job_id":null,"html_url":"https://github.com/caibirdme/yql","commit_stats":null,"previous_names":[],"tags_count":1,"template":false,"template_full_name":null,"purl":"pkg:github/caibirdme/yql","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/caibirdme%2Fyql","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/caibirdme%2Fyql/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/caibirdme%2Fyql/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/caibirdme%2Fyql/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/caibirdme","download_url":"https://codeload.github.com/caibirdme/yql/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/caibirdme%2Fyql/sbom","scorecard":{"id":261956,"data":{"date":"2025-08-11","repo":{"name":"github.com/caibirdme/yql","commit":"a800d6de28a030e2da9d32e2fe331d16542df5f5"},"scorecard":{"version":"v5.2.1-40-gf6ed084d","commit":"f6ed084d17c9236477efd66e5b258b9d4cc7b389"},"score":3,"checks":[{"name":"Packaging","score":-1,"reason":"packaging workflow not detected","details":["Warn: no GitHub/GitLab publishing workflow detected."],"documentation":{"short":"Determines if the project is published as a package that others can easily download, install, easily update, and uninstall.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#packaging"}},{"name":"Dangerous-Workflow","score":-1,"reason":"no workflows found","details":null,"documentation":{"short":"Determines if the project's GitHub Action workflows avoid dangerous patterns.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#dangerous-workflow"}},{"name":"SAST","score":0,"reason":"no SAST tool detected","details":["Warn: no pull requests merged into dev branch"],"documentation":{"short":"Determines if the project uses static code analysis.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#sast"}},{"name":"Code-Review","score":0,"reason":"Found 0/23 approved changesets -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project requires human code review before pull requests (aka merge requests) are merged.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#code-review"}},{"name":"Maintained","score":0,"reason":"0 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project is \"actively maintained\".","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#maintained"}},{"name":"Token-Permissions","score":-1,"reason":"No tokens found","details":null,"documentation":{"short":"Determines if the project's workflows follow the principle of least privilege.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#token-permissions"}},{"name":"Pinned-Dependencies","score":-1,"reason":"no dependencies found","details":null,"documentation":{"short":"Determines if the project has declared and pinned the dependencies of its build process.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#pinned-dependencies"}},{"name":"Binary-Artifacts","score":10,"reason":"no binaries found in the repo","details":null,"documentation":{"short":"Determines if the project has generated executable (binary) artifacts in the source repository.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#binary-artifacts"}},{"name":"CII-Best-Practices","score":0,"reason":"no effort to earn an OpenSSF best practices badge detected","details":null,"documentation":{"short":"Determines if the project has an OpenSSF (formerly CII) Best Practices Badge.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#cii-best-practices"}},{"name":"Security-Policy","score":0,"reason":"security policy file not detected","details":["Warn: no security policy file detected","Warn: no security file to analyze","Warn: no security file to analyze","Warn: no security file to analyze"],"documentation":{"short":"Determines if the project has published a security policy.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#security-policy"}},{"name":"Fuzzing","score":0,"reason":"project is not fuzzed","details":["Warn: no fuzzer integrations found"],"documentation":{"short":"Determines if the project uses fuzzing.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#fuzzing"}},{"name":"License","score":10,"reason":"license file detected","details":["Info: project has a license file: LICENSE:0","Info: FSF or OSI recognized license: MIT License: LICENSE:0"],"documentation":{"short":"Determines if the project has defined a license.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#license"}},{"name":"Signed-Releases","score":-1,"reason":"no releases found","details":null,"documentation":{"short":"Determines if the project cryptographically signs release artifacts.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#signed-releases"}},{"name":"Branch-Protection","score":0,"reason":"branch protection not enabled on development/release branches","details":["Warn: branch protection not enabled for branch 'master'"],"documentation":{"short":"Determines if the default and release branches are protected with GitHub's branch protection settings.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#branch-protection"}},{"name":"Vulnerabilities","score":10,"reason":"0 existing vulnerabilities detected","details":null,"documentation":{"short":"Determines if the project has open, known unfixed vulnerabilities.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#vulnerabilities"}}]},"last_synced_at":"2025-08-17T11:00:52.435Z","repository_id":57486850,"created_at":"2025-08-17T11:00:52.435Z","updated_at":"2025-08-17T11:00:52.435Z"},"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28492265,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-17T00:50:05.742Z","status":"online","status_checked_at":"2026-01-17T02:00:07.808Z","response_time":85,"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":["dsl","go","rule-engine"],"created_at":"2024-08-02T17:01:32.037Z","updated_at":"2026-01-17T02:24:42.808Z","avatar_url":"https://github.com/caibirdme.png","language":"Go","funding_links":[],"categories":["开源类库","Open source library","Go"],"sub_categories":["解释器","Interpreter"],"readme":"### YQL(Yet another-Query-Language)\n[![Build Status](https://www.travis-ci.org/caibirdme/yql.svg?branch=master)](https://www.travis-ci.org/caibirdme/yql)\n[![GoDoc](https://godoc.org/github.com/caibirdme/yql?status.svg)](https://godoc.org/github.com/caibirdme/yql)\n\n\nYQL is very similar with the `where` part of sql. You can see it as another sql which also support comparison between two sets. YQL have nearly no new concepts, so you can use it well short after reading the examples.Though it's designed for rule engine, it can be widely used in your code logic.\n\n### Install\n`go get github.com/caibirdme/yql`\n\n### Exmaple\nSee more examples in the `yql_test.go` and godoc.\n\n``` go\n\trawYQL := `name='deen' and age\u003e=23 and (hobby in ('soccer', 'swim') or score\u003e90))`\n\tresult, _ := yql.Match(rawYQL, map[string]interface{}{\n\t\t\"name\":  \"deen\",\n\t\t\"age\":   int64(23),\n\t\t\"hobby\": \"basketball\",\n\t\t\"score\": int64(100),\n\t})\n\tfmt.Println(result)\n\trawYQL = `score ∩ (7,1,9,5,3)`\n\tresult, _ = yql.Match(rawYQL, map[string]interface{}{\n\t\t\"score\": []int64{3, 100, 200},\n\t})\n\tfmt.Println(result)\n\trawYQL = `score in (7,1,9,5,3)`\n\tresult, _ = yql.Match(rawYQL, map[string]interface{}{\n\t\t\"score\": []int64{3, 5, 2},\n\t})\n\tfmt.Println(result)\n\trawYQL = `score.sum() \u003e 10`\n\tresult, _ = yql.Match(rawYQL, map[string]interface{}{\n\t\t\"score\": []int{1, 2, 3, 4, 5},\n\t})\n\tfmt.Println(result)\n\t//Output:\n\t//true\n\t//true\n\t//false\n\t//true\n```\n\nAnd In most cases, you can use `Rule` to cache the AST and then use `Match` to get the result, which could avoid hundreds of thousands of repeated parsing process.\n\n```go\n\trawYQL := `name='deen' and age\u003e=23 and (hobby in ('soccer', 'swim') or score\u003e90)`\n\truler,_ := yql.Rule(rawYQL)\n\n\tresult, _ := ruler.Match(map[string]interface{}{\n\t\t\"name\":  \"deen\",\n\t\t\"age\":   23,\n\t\t\"hobby\": \"basketball\",\n\t\t\"score\": int64(100),\n\t})\n\tfmt.Println(result)\n\tresult, _ = ruler.Match(map[string]interface{}{\n\t\t\"name\":  \"deen\",\n\t\t\"age\":   23,\n\t\t\"hobby\": \"basketball\",\n\t\t\"score\": int64(90),\n\t})\n\tfmt.Println(result)\n\t//Output:\n\t//true\n\t//false\n```\n\nThough the to be matched data is the type of `map[string]interface{}`, there're only 5 types supported:\n* int\n* int64\n* float64\n* string\n* bool\n\n### Helpers\nIn `score.sum() \u003e 10`, `sum` is a helper function which adds up all the numbers in score, which also means the type of score must be one of the []int,[]int64 or []float64.\n\nThis repo is in the early stage, so now there are just a few helpers, feel free to create an issue about your needs. Supported helpers are listed below:\n* sum: ...\n* count: return the length of a slice or 1 if not a slice\n* avg: return the average number of a slice(`float64(total)/float64(len(slice))`)\n* max: return the maximum number in a slice\n* min: return the minimum number in a slice\n\n### Usage scenario\nObviously, it's easy to use in rule engine.\n```go\nvar handlers = map[int]func(map[string]interface{}){\n\t1: sendEmail,\n\t2: sendMessage,\n\t3: alertBoss,\n}\n\ndata := resolvePostParamsFromRequest(request)\nrules := getRulesFromDB(sql)\n\nfor _,rule := range rules {\n\tif success,_ := yql.Match(rule.YQL, data); success {\n\t\thandler := handlers[rule.ID]\n\t\thandler(data)\n\t\tbreak\n\t}\n}\n```\n\nAlso, it can be used in your daily work, which could significantly reduce the deeply embebbed `if else` statements:\n```go\nfunc isVIP(user User) bool {\n\trule := fmt.Sprintf(\"monthly_vip=true and now\u003c%s or eternal_vip=1 or ab_test!=false\", user.ExpireTime)\n\tok,_ := yql.Match(rule, map[string]interface{}{\n\t\t\"monthly_vip\": user.IsMonthlyVIP,\n\t\t\"now\": time.Now().Unix(),\n\t\t\"eternal_vip\": user.EternalFlag,\n\t\t\"ab_test\": isABTestMatched(user),\n\t})\n\treturn ok\n}\n```\n\nEven, you can use `json.Marshal` to generate the map[string]interface{} if you don't want to write it manually. Make sure the structure tag should be same as the name in rawYQL.\n\n### Syntax\nSee [grammar file](./internal/grammar/Yql.g4)\n\n### Compatibility promise\nThe API `Match`is stable now. Its grammar won't change any more, and what I only will do next is to optimize, speed up and add more helpers if needed.\n\n\n### Further Trial\nThough it's kinder difficult to create a robust new Go compiler, there're still some interesting things could do. For example, bringing lambda function in Go which maybe look like:\n```go\nvar scores = []int{1,2,3,4,5,6,7,8,9,10}\nnewSlice := yql.Filter(`(v) =\u003e v % 2 == 0`).Map(`(v) =\u003e v*v`).Call(scores).Interface()\n//[]int{4,16,36,64,100}\n```\nIf the lambda function won't change all time, it can be cached like opcode, which is as fast as the compiled code. And in most cases, who care?(pythoner?)\n\nIt's not easy but interesting, isn't it? Welcome to join me, open some issues and talk about your ideas with me. Maybe one day it can become a pre-compile tool like [babel](http://babeljs.io/) in javascript.  \n\n#### Attention\n`Lambda expression` now is in its very early stage, **DO NOT USE IT IN PRODUCTION**.\n\nYou can take a quick preview in [test case](/lambda/lambda_test.go)\n\n```go\ntype Student struct {\n\tAge  int\n\tName string\n}\n\nvar students = []Student{\n\tStudent{\n\t\tName: \"deen\",\n\t\tAge:  24,\n\t},\n\tStudent{\n\t\tName: \"bob\",\n\t\tAge:  22,\n\t},\n\tStudent{\n\t\tName: \"alice\",\n\t\tAge:  23,\n\t},\n\tStudent{\n\t\tName: \"tom\",\n\t\tAge:  25,\n\t},\n\tStudent{\n\t\tName: \"jerry\",\n\t\tAge:  20,\n\t},\n}\n\nt = yql.Filter(`(v) =\u003e v.Age \u003e 23 || v.Name == \"alice\"`).Call(students).Interface()\nres,_ := t.([]Student)\n// res: Student{\"deen\",24} Student{\"alice\", 23} Student{\"tom\", 25}\n```\n\nChainable\n```go\ndst := []int{1, 2, 3, 4, 5, 6, 7}\nr := Filter(`(v) =\u003e v \u003e 3 \u0026\u0026 v \u003c= 7`).Map(`(v) =\u003e  v \u003c\u003c 2`).Filter(`(v) =\u003e v % 8 == 0`).Call(dst)\ns, err := r.Interface()\nass := assert.New(t)\nass.NoError(err)\nass.Equal([]int{16, 24}, s)\n```","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcaibirdme%2Fyql","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fcaibirdme%2Fyql","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcaibirdme%2Fyql/lists"}