{"id":16767860,"url":"https://github.com/eolinker/sql2dsl","last_synced_at":"2025-10-04T09:07:10.656Z","repository":{"id":45566852,"uuid":"436144084","full_name":"eolinker/sql2dsl","owner":"eolinker","description":"sql convert to elasticsearch dsl","archived":false,"fork":false,"pushed_at":"2022-02-11T02:16:32.000Z","size":61,"stargazers_count":18,"open_issues_count":0,"forks_count":9,"subscribers_count":3,"default_branch":"main","last_synced_at":"2025-06-02T00:28:06.528Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Java","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"gpl-3.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/eolinker.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":"2021-12-08T06:43:03.000Z","updated_at":"2025-05-23T01:23:13.000Z","dependencies_parsed_at":"2022-09-14T00:22:08.013Z","dependency_job_id":null,"html_url":"https://github.com/eolinker/sql2dsl","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/eolinker/sql2dsl","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/eolinker%2Fsql2dsl","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/eolinker%2Fsql2dsl/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/eolinker%2Fsql2dsl/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/eolinker%2Fsql2dsl/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/eolinker","download_url":"https://codeload.github.com/eolinker/sql2dsl/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/eolinker%2Fsql2dsl/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":278289584,"owners_count":25962377,"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","status":"online","status_checked_at":"2025-10-04T02:00:05.491Z","response_time":63,"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":"2024-10-13T06:10:09.474Z","updated_at":"2025-10-04T09:07:10.629Z","avatar_url":"https://github.com/eolinker.png","language":"Java","funding_links":[],"categories":[],"sub_categories":[],"readme":"## 简介\n\nconvert SQL to Elasticsearch DSL in java.\n\n将SQL转成Elasticsearch的DSL的工具，语言类型：Java。\n\n内含javabean转sql的工具类（DSLSqlHelper）\n\n实现DSLSelectHandler接口可将SQL转成其他类SQL查询语句，如presto sql/hive sql等。\n\nSQL的AST解析原理使用的是 [alibaba/druid](https://github.com/alibaba/druid) ，druid不支持的语法将无法解析。\n\n## sql语法支持\n\n#### 普通查询条件支持\n\n- [x] and\n- [x] or\n- [x] equal(=)\n- [x] not equal(!=)\n- [x] gt(\u003e)\n- [x] gte(\u003e=)\n- [x] lt(\u003c)\n- [x] lte(\u003c=)\n- [x] in (如 id in (1,2,3) )\n- [x] not in (如 id not in (1,2,3) )\n- [x] 括号语法 (如 where (a=1 or b=1) and (c=1 or d=1))\n- [x] 模糊查询 like 表达式 (目前用query_string实现)\n- [x] order by\n- [x] limit\n- [x] not like\n- [x] 空字段检查(is null, is not null)\n\n#### 聚合功能支持\n\n- [x] group by\n\n\u003e group by中多个字段用“,”隔开，生成的dsl聚合会从左到右深入，sql中存在group by才会生成聚合\n\n- [x] having聚合（如 having a=1 and b=2）\n\n\u003e having条件最后会作为一个filter的聚合放入到最底层聚合中，最终放在buckets=HAVING.COUNT中\n\n- [x] 统计类聚合函数，如 count(\\*), count(field), min(field), max(field), avg(field), sum(field)\n\n\u003e 统计类函数会放到最底层聚合中，且只有group by时这些函数才会生效\n\u003e\n\u003e * 每个聚合必须带别名，如（select count(user) userTotal ...）\n\n- [x] 仅es支持的统计聚合函数，如 stats(field), extended_stats(field), percentiles(field)\n\n\u003e 此3个方法仅在dsl聚合中支持，sql不支持，这里是对sql的扩充\n\u003e\n\u003e * 每个聚合必须带别名，如（select stats(user) userStats ...）\n\n- [x] 其他支持，filter函数\n\n\u003e 此函数在dsl和sql均不支持，作为dsl聚合的扩充，用法filter($sql_expr)，如 filter(sex = 1)， filter(sex = 0 and name like 'lucy')，每一个filter会转换成一个聚合放入最底层聚合当中，最终放在buckets = $Alias.COUNT中\n\u003e\n\u003e * 每个聚合必须带别名，如（select filter(sex=1) maleTotal, filter(sex=2) femaleTotal ...）\n\n#### 暂不支持\n\n- [ ] join表达式\n- [ ] 多表查询\n\n## 如何使用\n\n1. 添加倚赖\n\n```xml\n\u003c!-- maven --\u003e\n\u003cdependency\u003e\n    \u003cgroupId\u003ecom.eolinker\u003c/groupId\u003e\n    \u003cartifactId\u003esql2dsl\u003c/artifactId\u003e\n    \u003cversion\u003e1.0.0-RELEASE\u003c/version\u003e\n\u003c/dependency\u003e\n```\n\n或者\n\n```\n// gradle\nimplementation 'com.eolinker:sql2dsl:1.0.0-RELEASE'\n```\n\n2. Demo\n\n```java\npackage com.eolinker.sql2dsl;\n\nimport com.alibaba.fastjson.JSON;\nimport com.alibaba.fastjson.JSONObject;\nimport com.eolinker.sql2dsl.*;\nimport org.apache.commons.lang3.tuple.ImmutablePair;\n\nimport org.junit.Test;\n\nimport java.util.Arrays;\nimport java.util.List;\n\npublic class TestDemo {\n\n    ESSelectHandler esSelectHandler = new ESSelectHandler();\n\n    @Test\n    public void normalSql2DSLTest() {\n        DSLConvert dslConvert = new DSLConvert();\n        // normal sql\n        String sql = \"select * from user where sex = 1 and age \u003e= 18\";\n        try {\n            ImmutablePair\u003cString, String\u003e immutablePair = dslConvert.convertSelect(sql, esSelectHandler);\n            System.out.println(\"es index: \" + immutablePair.getRight());\n            // es index: user\n            System.out.println(\"dsl: \" + immutablePair.getLeft());\n            // dsl: {\"query\" : {\"bool\" : {\"must\" : [{\"match_phrase\" : {\"sex\" : \"1\"}},{\"range\" : {\"age\" : {\"from\" : \"18\"}}}]}}  ,\"from\" : 0  ,\"size\" : 10 }\n        } catch (Exception e) {\n            e.printStackTrace();\n        }\n    }\n\n    @Test\n    public void testJavaBean2normalSQL() {\n        String table = \"user\";  // or es_index\n        UserDTO userDTO = new UserDTO();\n        userDTO.setSex(1);\n        userDTO.setNameQuery(\"chen\");\n        userDTO.setDepts(Arrays.asList(\"A\", \"B\", \"C\"));\n        userDTO.setOffset(0);\n        userDTO.setLimit(10);\n\n        String whereJson = JSON.toJSONString(userDTO);\n        System.out.println(\"where: \" + whereJson);\n        // where: {\"dept\":[\"A\",\"B\",\"C\"],\"limit\":10,\"name like \":\"chen\",\"offset\":0,\"sex=\":1}\n\n        String sql = DSLSqlHelper.json2sql(whereJson, table);\n        System.out.println(\"sql: \" + sql);\n        // sql: SELECT * FROM user WHERE sex= 1 and name like  \"chen\" and dept  IN (\"A\",\"B\",\"C\") LIMIT 0,10\n    }\n\n    @Test\n    public void testJavaBean2GroupBySQL() {\n        String table = \"score\";  // or es_index\n        ScoreDTO scoreDTO = new ScoreDTO();\n        scoreDTO.setChineseStart(60f);\n        scoreDTO.setMathStart(60f);\n        scoreDTO.setEnglishStart(60f);\n        scoreDTO.setTotalScoreStart(180f);\n\n        List\u003cString\u003e selectFieldList = Arrays.asList(\"count(*)\", \"max(chinese)\", \"max(math)\", \"max(english)\");\n        List\u003cString\u003e groupByList = Arrays.asList(\"dept\");\n\n        String whereJson = JSONObject.toJSONString(scoreDTO);\n        System.out.println(\"where: \" + whereJson);\n        // where: {\"chinese\u003e=\":60.0,\"english\u003e=\":60.0,\"math\u003e=\":60.0,\"total_score\u003e=\":180.0}\n\n        String sql = DSLSqlHelper.json2sql(whereJson, selectFieldList, table, groupByList);\n        System.out.println(\"sql: \" +sql);\n        // sql: SELECT count(*),max(chinese),max(math),max(english) FROM score WHERE total_score\u003e= 180.0 and english\u003e= 60.0 and chinese\u003e= 60.0 and math\u003e= 60.0 GROUP BY dept\n\n    }\n}\n```\n\n## 示例代码类\n\n* com.eolinker.sql2dsl.TestDemo\n\n## 例子展示\n\n1. select * from user where sex = 1 and age \u003e= 18\n\n```json\n{\"query\" : {\"bool\" : {\"must\" : [{\"match_phrase\" : {\"sex\" : \"1\"}},{\"range\" : {\"age\" : {\"from\" : \"18\"}}}]}}  ,\"from\" : 0  ,\"size\" : 0 }\n```\n\n2. select * from user where sex = 1 or age \u003c 18\n\n```json\n{\"query\" : {\"bool\" : {\"should\" : [{\"match_phrase\" : {\"sex\" : \"1\"}},{\"range\" : {\"age\" : {\"lt\" : \"18\"}}}]}}  ,\"from\" : 0  ,\"size\" : 0 }\n```\n\n3. select * from user where dept in ('A','B','C')\n\n```json\n{\"query\" : {\"bool\" : {\"must\" : [{\"terms\" : {\"dept\" : [\"A\",\"B\",\"C\"]}}]}}  ,\"from\" : 0  ,\"size\" : 0 }\n```\n\n4. select * from user where dept not in ('A')\n\n```json\n{\"query\" : {\"bool\" : {\"must\" : [{\"bool\" : {\"must_not\" : {\"terms\" : {\"dept\" : [\"A\"]}}}}]}}  ,\"from\" : 0  ,\"size\" : 0 }\n```\n\n5. select * from user where (sex = 1 and age \u003e 18) or (sex = 0 and age \u003e 18)\n\n```json\n{\"query\" : {\"bool\" : {\"should\" : [{\"bool\" : {\"must\" : [{\"match_phrase\" : {\"sex\" : \"1\"}},{\"range\" : {\"age\" : {\"gt\" : \"18\"}}}]}},{\"bool\" : {\"must\" : [{\"match_phrase\" : {\"sex\" : \"0\"}},{\"range\" : {\"age\" : {\"gt\" : \"18\"}}}]}}]}}  ,\"from\" : 0  ,\"size\" : 0 }\n```\n\n6. select * from user where name like 'lucy'\n\n```json\n{\"query\" : {\"bool\" : {\"must\" : [{\"query_string\":{\"default_field\": \"name\",\"query\":\"*lucy*\"}}]}}  ,\"from\" : 0  ,\"size\" : 0 }\n```\n\n7. select * from user where name not like 'lucy'\n\n```json\n{\"query\" : {\"bool\" : {\"must\" : [{\"bool\" : {\"must_not\" : {\"match_phrase\" : {\"name\" : {\"query\" : \"lucy\"}}}}}]}}  ,\"from\" : 0  ,\"size\" : 0 }\n```\n\n8. select * from user order by id desc, name asc limit 0,10\n\n```json\n{\"query\" : {\"match_all\": {}}  ,\"from\" : 0  ,\"size\" : 10  ,\"sort\" : [{\"id\":\"DESC\"},{\"name\":\"ASC\"}]}\n```\n\n9. select * from user where mobile is null\n\n```json\n{\"query\" : {\"bool\" : {\"must\" : [{\"bool\": { \"must_not\": { \"exists\": { \"field\": \"mobile\" }}}}]}}  ,\"from\" : 0  ,\"size\" : 0 }\n```\n\n10. select * from user where mobile is not null\n\n```json\n{\"query\" : {\"bool\" : {\"must\" : [{\"bool\": { \"must\": { \"exists\": { \"field\": \"mobile\" }}}}]}}  ,\"from\" : 0  ,\"size\" : 0 }\n```\n\n11. select * from user group by dept,class,level\n\n```json\n{\"query\" : {\"match_all\": {}}  ,\"from\" : 0  ,\"size\" : 0  ,\"aggregations\" : {\"dept\":{\"terms\":{\"field\":\"dept\",\"size\":500},\"aggregations\":{\"class\":{\"terms\":{\"field\":\"class\",\"size\":500},\"aggregations\":{\"level\":{\"terms\":{\"field\":\"level\",\"size\":500},\"aggregations\":{}}}}}}}}\n```\n\n12. select * from user group by dept,class having sex = 1 and age \u003e18\n\n```json\n{\"query\" : {\"match_all\": {}}  ,\"from\" : 0  ,\"size\" : 0  ,\"aggregations\" : {\"dept\":{\"terms\":{\"field\":\"dept\",\"size\":500},\"aggregations\":{\"class\":{\"terms\":{\"field\":\"class\",\"size\":500},\"aggregations\":{\"HAVING_RESULT\":{\"filters\":{\"filters\":{\"COUNT\":{\"bool\":{\"must\":[{\"match_phrase\":{\"sex\":\"1\"}},{\"range\":{\"age\":{\"gt\":\"18\"}}}]}}}}}}}}}}}\n```\n\n13. select count(*) userTotal from user group by class\n\n```json\n{\"query\" : {\"match_all\": {}}  ,\"from\" : 0  ,\"size\" : 0  ,\"aggregations\" : {\"class\":{\"terms\":{\"field\":\"class\",\"size\":500},\"aggregations\":{\"userTotal\":{\"value_count\":{\"field\":\"_index\"}}}}}}\n```\n\n14. select min(math) min_math,min(chinese) min_ch,max(english) max_eng, avg(total_score) avg_score from score group by class\n\n```json\n{\"query\" : {\"match_all\": {}}  ,\"from\" : 0  ,\"size\" : 0  ,\"aggregations\" : {\"class\":{\"terms\":{\"field\":\"class\",\"size\":500},\"aggregations\":{\"min_math\":{\"min\":{\"field\":\"math\"}},\"max_eng\":{\"max\":{\"field\":\"english\"}},\"min_ch\":{\"min\":{\"field\":\"chinese\"}},\"avg_score\":{\"avg\":{\"field\":\"total_score\"}}}}}}\n```\n\n15. select extended_stats(total_score) stat_score,percentiles(total_score) pc_score from score group by class\n\n```json\n{\"query\" : {\"match_all\": {}}  ,\"from\" : 0  ,\"size\" : 0  ,\"aggregations\" : {\"class\":{\"terms\":{\"field\":\"class\",\"size\":500},\"aggregations\":{\"stat_score\":{\"extended_stats\":{\"field\":\"total_score\"}},\"pc_score\":{\"percentiles\":{\"field\":\"total_score\"}}}}}}\n```\n\n16. select filter(sex=1 and age\u003c14) boy_total, filter(sex=2 and age\u003c14) girl_total, filter(sex=1 and age\u003e=14) man_total, filter(sex=2 and age\u003e=14) women_total from user group by dept\n\n```json\n{\"query\" : {\"match_all\": {}}  ,\"from\" : 0  ,\"size\" : 0  ,\"aggregations\" : {\"dept\":{\"terms\":{\"field\":\"dept\",\"size\":500},\"aggregations\":{\"women_total\":{\"filters\":{\"filters\":{\"COUNT\":{\"bool\":{\"must\":[{\"match_phrase\":{\"sex\":\"2\"}},{\"range\":{\"age\":{\"from\":\"14\"}}}]}}}}},\"girl_total\":{\"filters\":{\"filters\":{\"COUNT\":{\"bool\":{\"must\":[{\"match_phrase\":{\"sex\":\"2\"}},{\"range\":{\"age\":{\"lt\":\"14\"}}}]}}}}},\"man_total\":{\"filters\":{\"filters\":{\"COUNT\":{\"bool\":{\"must\":[{\"match_phrase\":{\"sex\":\"1\"}},{\"range\":{\"age\":{\"from\":\"14\"}}}]}}}}},\"boy_total\":{\"filters\":{\"filters\":{\"COUNT\":{\"bool\":{\"must\":[{\"match_phrase\":{\"sex\":\"1\"}},{\"range\":{\"age\":{\"lt\":\"14\"}}}]}}}}}}}}}\n```","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Feolinker%2Fsql2dsl","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Feolinker%2Fsql2dsl","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Feolinker%2Fsql2dsl/lists"}