{"id":15885758,"url":"https://github.com/rutledgepaulv/q-builders","last_synced_at":"2025-07-16T12:32:48.294Z","repository":{"id":3881616,"uuid":"43654553","full_name":"RutledgePaulV/q-builders","owner":"RutledgePaulV","description":"Type safe database agnostic query builders.","archived":false,"fork":false,"pushed_at":"2022-06-25T07:28:00.000Z","size":239,"stargazers_count":55,"open_issues_count":4,"forks_count":22,"subscribers_count":4,"default_branch":"develop","last_synced_at":"2025-04-03T11:51:16.406Z","etag":null,"topics":["elasticsearch","mongodb","query-builder","query-dsl","rsql"],"latest_commit_sha":null,"homepage":"","language":"Java","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/RutledgePaulV.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":"2015-10-04T22:03:17.000Z","updated_at":"2024-07-26T06:49:50.000Z","dependencies_parsed_at":"2022-09-14T01:28:46.224Z","dependency_job_id":null,"html_url":"https://github.com/RutledgePaulV/q-builders","commit_stats":null,"previous_names":[],"tags_count":7,"template":false,"template_full_name":null,"purl":"pkg:github/RutledgePaulV/q-builders","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/RutledgePaulV%2Fq-builders","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/RutledgePaulV%2Fq-builders/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/RutledgePaulV%2Fq-builders/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/RutledgePaulV%2Fq-builders/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/RutledgePaulV","download_url":"https://codeload.github.com/RutledgePaulV/q-builders/tar.gz/refs/heads/develop","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/RutledgePaulV%2Fq-builders/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":263071244,"owners_count":23409255,"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":["elasticsearch","mongodb","query-builder","query-dsl","rsql"],"created_at":"2024-10-06T05:07:23.493Z","updated_at":"2025-07-02T03:37:10.494Z","avatar_url":"https://github.com/RutledgePaulV.png","language":"Java","funding_links":[],"categories":[],"sub_categories":[],"readme":"[![Build Status](https://travis-ci.org/RutledgePaulV/q-builders.svg)](https://travis-ci.org/RutledgePaulV/q-builders)\n[![Coverage Status](https://coveralls.io/repos/RutledgePaulV/q-builders/badge.svg?branch=develop\u0026service=github)](https://coveralls.io/github/RutledgePaulV/q-builders?branch=develop)\n[![Maven Central](https://maven-badges.herokuapp.com/maven-central/com.github.rutledgepaulv/q-builders/badge.svg)](https://maven-badges.herokuapp.com/maven-central/com.github.rutledgepaulv/q-builders)\n\n### Overview\nA lightweight abstraction for constructing queries oriented around domain models / entities that minimizes\nmagic strings, provides type safety, produces queries that read well, and provides a decoupled way to define new query target formats - allowing you to change your mind about where you store your data without having to change all of your query code.\n\n\n### Why does this exist?\nA lot of existing query builders are lacking. It's difficult to write a query builder that is both convenient and safe, which has resulted in most query builders giving up on type safety or allowing you to call methods that don't make sense to be able to call at that time.\n\n\n### Why is this better?\nIt uses the type system to enforce that you won't call an unapplicable method at any point as you build your queries. Also, there's no need to worry about someone passing an integer to a string field, etc. It supports both chaining and composition to build a query and when used with static imports it becomes a succinct (as far as java goes) query DSL.\n\n\n### Out Of Box Target Formats:\n- Java Predicates\n- A string in RSQL format\n- Elasticsearch's QueryBuilder\n- Spring Data's MongoDB Criteria\n\n\n### Simple Usage:\n```java\n//--------------------------------------------------------------------------\n// define a single query model for each of the models you want to be able to \n// query against. Return the appropriate property interface for each field\n// based on the type of the value on your model\n//--------------------------------------------------------------------------\n\npublic class PersonQuery extends QBuilder\u003cPersonQuery\u003e {\n\n    public StringProperty\u003cPersonQuery\u003e firstName() {\n        return stringField(\"firstName\");\n    }\n    \n    public IntegerProperty\u003cPersonQuery\u003e age() {\n        return integerField(\"age\");\n    }\n    \n}\n\n\nCondition\u003cPersonQuery\u003e q = new PersonQuery().firstName().eq(\"Paul\").and().age().eq(23);\n\n\nString rsql = q.query(new RSQLVisitor()); \n// firstName==Paul;age==23\n\nCriteria mongoCriteria = q.query(new MongoVisitor()); \n// {firstName: \"Paul\", age: 23}\n\nFilterBuilder filter = q.query(new ElasticsearchVisitor());\n// { \"and\" : { \"filters\" : [ { \"term\" : { \"firstName\" : \"Paul\" } }, { \"term\" : { \"age\" : 23 } } ] } }\n\nPredicate\u003cPerson\u003e predicate = q.query(new PredicateVisitor\u003c\u003e());\n// List\u003cPerson\u003e personsNamedPaulAndAge23 = persons.stream().filter(predicate).collect(toList());\n```\n\n\n### Static Import Usage:\n```java\n\npublic class PersonQuery extends QBuilder\u003cPersonQuery\u003e {\n\n    public static StringProperty\u003cPersonQuery\u003e firstName() {\n        return new PersonQuery().stringField(\"firstName\");\n    }\n    \n    public static IntegerProperty\u003cPersonQuery\u003e age() {\n        return new PersonQuery().integerField(\"age\");\n    }\n    \n}\n\nimport static com.company.queries.PersonQuery.*;\n\nCondition\u003cPersonQuery\u003e query = firstName().eq(\"Paul\").or(and(firstName().ne(\"Richard\"), age().gt(22)));\n```\n\n### Precedence:\nChaining with the infix forms of ```and()``` and ```or()``` is complicated when you use both and then want to talk about\nprecedence. The implementation is such that whenever you change from a chain of ```and()``` to\na chain of ```or()``` (or vice versa), then the previous statements are wrapped together and the existing set\nbecomes one of the elements in the new ```or()``` (or ```and()```) chain. \n\nIn general, to avoid unintended precedence concerns, it's best to limit your chains\nto only ```and()``` or ```or()``` operators and use the compositional forms \n```and(Condition\u003cT\u003e c1, Condition\u003cT\u003e c2, Condition\u003cT\u003e... cn)``` and \n```or(Condition\u003cT\u003e c1, Condition\u003cT\u003e c2, Condition\u003cT\u003e... cn)``` for queries that must mix the two.\n\n\n\n### Common Use Cases\n\n#### Such-\u0026-Such Isn't Easy to Use or Type Safe\nMany criteria / query builders don't provide much in the way of a good \"user experience\". You can use q-builders simply because they provide an improvement over these.\n\n#### I Might Change Datastores\nBy using a layer that provides some decoupling from your storage, if you decide to swap out a datastore for something else but you're not also changing your data's structure then you can just write a new target backend and keep your query building the same.\n\n#### I'm Providing a Rest API\nChances are you want to provide an ability for people to query against your API, but you also make queries against the database yourself in the implementation of business logic on the server. One option could be to use these builders to define one query model in a shared jar that you use in both your SDK and API and simply target different query formats. Like RSQL for over-the-wire and directly into mongo criteria on the API server.\n\n\n### RSQL Flavor\nThe RSQL builder introduces some new operators to the standard RSQL set. Since this library only\nbuilds queries it doesn't dictate what you use to parse them. However, it's written specifically\nto be compatible with [rsql-parser](https://github.com/jirutka/rsql-parser). So, you should\nmake sure that you add the following operators before parsing:\n\n- \"=ex=\" The exists clause. It has values of either ```true``` or ```false```.\n- \"=q=\" The subquery clause. It has a string value that itself might be an entire RSQL query.\n- \"=re=\" A regular expression as a string. The string will be passed as-is to the backend visitor you use, so the regex string must be in the same flavor as the visitor you intend to use (mongo regex vs java regex, etc)\n\n\n\n### Installation \n\n\n#### Release Versions\n```xml\n\u003cdependencies\u003e\n    \u003cdependency\u003e\n        \u003cgroupId\u003ecom.github.rutledgepaulv\u003c/groupId\u003e\n        \u003cartifactId\u003eq-builders\u003c/artifactId\u003e\n        \u003cversion\u003e1.5\u003c/version\u003e\n    \u003c/dependency\u003e\n\u003c/dependencies\u003e\n```\n\n#### Snapshot Versions\n```xml\n\u003cdependencies\u003e\n    \u003cdependency\u003e\n        \u003cgroupId\u003ecom.github.rutledgepaulv\u003c/groupId\u003e\n        \u003cartifactId\u003eq-builders\u003c/artifactId\u003e\n        \u003cversion\u003e1.6-SNAPSHOT\u003c/version\u003e\n    \u003c/dependency\u003e\n\u003c/dependencies\u003e\n\n\n\u003crepositories\u003e\n    \u003crepository\u003e\n        \u003cid\u003eossrh\u003c/id\u003e\n        \u003cname\u003eRepository for snapshots\u003c/name\u003e\n        \u003curl\u003ehttps://oss.sonatype.org/content/repositories/snapshots\u003c/url\u003e\n        \u003csnapshots\u003e\n            \u003cenabled\u003etrue\u003c/enabled\u003e\n        \u003c/snapshots\u003e\n    \u003c/repository\u003e\n\u003c/repositories\u003e\n```\n\n\n#### Optional dependencies\n```xml\n\u003cdependencies\u003e\n    \n    \u003c!-- only necessary if you're using the spring data mongodb criteria target type --\u003e\n    \u003cdependency\u003e\n        \u003cgroupId\u003eorg.springframework.data\u003c/groupId\u003e\n        \u003cartifactId\u003espring-data-mongodb\u003c/artifactId\u003e\n        \u003cversion\u003e1.9.2.RELEASE\u003c/version\u003e\n    \u003c/dependency\u003e\n    \n    \u003c!-- only necessary if you're using the elasticsearch filter builder target type --\u003e\n    \u003cdependency\u003e\n        \u003cgroupId\u003eorg.elasticsearch\u003c/groupId\u003e\n        \u003cartifactId\u003eelasticsearch\u003c/artifactId\u003e\n        \u003cversion\u003e2.3.4\u003c/version\u003e\n    \u003c/dependency\u003e\n            \n\u003c/dependencies\u003e\n```\n\n\n### License\n\nThis project is licensed under [MIT license](http://opensource.org/licenses/MIT).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frutledgepaulv%2Fq-builders","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Frutledgepaulv%2Fq-builders","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frutledgepaulv%2Fq-builders/lists"}