{"id":21544334,"url":"https://github.com/railken/search-query","last_synced_at":"2025-04-10T04:41:28.830Z","repository":{"id":62532820,"uuid":"107004461","full_name":"railken/search-query","owner":"railken","description":"A library that converts a query string into a tree object","archived":false,"fork":false,"pushed_at":"2024-03-27T19:46:47.000Z","size":630,"stargazers_count":3,"open_issues_count":0,"forks_count":1,"subscribers_count":3,"default_branch":"master","last_synced_at":"2024-07-11T03:12:13.940Z","etag":null,"topics":["api","composer","php","query","search","tree"],"latest_commit_sha":null,"homepage":"","language":"PHP","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/railken.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":"2017-10-15T11:00:11.000Z","updated_at":"2024-03-27T19:46:51.000Z","dependencies_parsed_at":"2024-11-17T14:50:40.654Z","dependency_job_id":null,"html_url":"https://github.com/railken/search-query","commit_stats":null,"previous_names":[],"tags_count":27,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/railken%2Fsearch-query","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/railken%2Fsearch-query/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/railken%2Fsearch-query/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/railken%2Fsearch-query/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/railken","download_url":"https://codeload.github.com/railken/search-query/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248160780,"owners_count":21057549,"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":["api","composer","php","query","search","tree"],"created_at":"2024-11-24T05:17:42.084Z","updated_at":"2025-04-10T04:41:28.804Z","avatar_url":"https://github.com/railken.png","language":"PHP","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Search Query\n\n[![Actions Status](https://github.com/railken/search-query/workflows/Test/badge.svg)](https://github.com/railken/search-query/actions)\n\nConverts a simple expression (e.g. 'x eq 1 or y gt 2') into a tree object.\nThis can be pretty usefull when building the API endpoint for a search.\n\n## Requirements\n\nPHP 7.1 or later.\n\n## Composer\n\nYou can install it via [Composer](https://getcomposer.org/) by typing the following command:\n\n```bash\ncomposer require railken/search-query\n```\n\n## Demo\n\n![demo](https://raw.githubusercontent.com/railken/search-query/master/demo/demo.gif)\n\n## Usage\n\nA simple usage looks like this: \n```php\n   use Railken\\SQ\\QueryParser;\n   use Railken\\SQ\\Resolvers as Resolvers;\n\n   $parser = new QueryParser();\n   $parser-\u003eaddResolvers([\n      new Resolvers\\ValueResolver(),\n      new Resolvers\\KeyResolver(),\n      new Resolvers\\GroupingResolver(),\n      new Resolvers\\NotEqResolver(),\n      new Resolvers\\EqResolver(),\n      new Resolvers\\LteResolver(),\n      new Resolvers\\LtResolver(),\n      new Resolvers\\GteResolver(),\n      new Resolvers\\GtResolver(),\n      new Resolvers\\CtResolver(),\n      new Resolvers\\SwResolver(),\n      new Resolvers\\NotInResolver(),\n      new Resolvers\\InResolver(),\n      new Resolvers\\EwResolver(),\n      new Resolvers\\NotNullResolver(),\n      new Resolvers\\NullResolver(),\n      new Resolvers\\AndResolver(),\n      new Resolvers\\OrResolver(),\n      new Resolvers\\TextResolver(),\n   ]);\n   $result = $query-\u003eparse('x eq 1 or y \u003e 1');\n```\nThe result formatted in json of `$result`:\n\n```json\n{\n  \"type\": \"Railken\\\\SQ\\\\Nodes\\\\OrNode\",\n  \"value\": \"OR\",\n  \"childs\": [\n    {\n      \"type\": \"Railken\\\\SQ\\\\Nodes\\\\EqNode\",\n      \"value\": \"EQ\",\n      \"childs\": [\n        {\n          \"type\": \"Railken\\\\SQ\\\\Nodes\\\\KeyNode\",\n          \"value\": \"x\"\n        },\n        {\n          \"type\": \"Railken\\\\SQ\\\\Nodes\\\\ValueNode\",\n          \"value\": \"1\"\n        }\n      ]\n    },\n    {\n      \"type\": \"Railken\\\\SQ\\\\Nodes\\\\GtNode\",\n      \"value\": \"GT\",\n      \"childs\": [\n        {\n          \"type\": \"Railken\\\\SQ\\\\Nodes\\\\KeyNode\",\n          \"value\": \"y\"\n        },\n        {\n          \"type\": \"Railken\\\\SQ\\\\Nodes\\\\ValueNode\",\n          \"value\": \"1\"\n        }\n      ]\n    }\n  ]\n}\n```\n\n## Nodes\n\n### Key node\nAll alphanumeric (starting with alphabetic character) are converted as ```KeyNode```. Other characters allowed: ```_```\n\nExamples:\n\n```\ncreated_at\n```\n\n\n### Value node\nAll numbers and strings are converted as ```ValueNode```. The delimiter of strings can be either ```'``` or ```\"```\n\nExamples:\n\n```\n\"An apple\"\n30.20\n```\n\n\n### Group node\nAll round parenthesis create a group node ```( ... )```. Of course a nested group can be made. If a parenthesis is missing an exception will be thrown\n\nExamples:\n\n```\n(x or y) and z\n((x \u003e 10 and w) or (y \u003e 5 and f)) and z\n```\n\n### AND, OR\nThis operators requires a ```Node``` before and after, otherwise an exception will be thrown. \n\nCan be expressed as literals (and, or) or as symbols (\u0026\u0026, ||)\n\n**Important**: If a parent group is defined and the LogicNode is the only child, the LogicNode will take the place of the GroupNode.\n\n\nExamples:\n\n```\nx or y || z\nx and y \u0026\u0026 z\n```\n\n\n### EQ, NOT_EQ, GT, GTE, LT, LTE\nAll these operators requires ```KeyNode``` or a ```ValueNode``` before and afters. \nCan be expressed as literals (eq, not eq, gt, gte, lt, lte) or as symbols (=, !=, \u003e, \u003e=, \u003c \u003c=)\n\nExamples:\n\n```\nx eq 1\nx = 1\nx gt 1\nx \u003e 1\nx gte 1\nx \u003e= 1\nx lt 1\nx \u003c 1\nx lte 1\nx \u003c= 1\n```\n\nComparison can be also made between two keys\n\nExamples:\n\n```\nx eq y\n```\n\n\n### CT, SW, EW\nThese operators will handle comparison with strings: contains, start with and end with.\n\nExamples:\n\n```\ndescription ct \"a random phrase\"\ndescription sw \"the text must start with this\"\ndescription ew \"the text must end with this\"\n```\n\n\n### NULL, NOT NULL\nThese operators don't require a node after the operator.\n```\ndeleted_at is null\ndeleted_at is not null\n```\n\n\n## Custom resolver\n\nAs you already saw, in order to parse the query you have to add the resolvers. \nSo you are free to add any resolvers you want, but pay attention to the order: KeyValue and NodeValue are the foundation for all the resolvers, so be carefull.\n\nHere's an example of custom resolver and node\n\nCustomResolver.php\n```php\n\u003c?php\nnamespace App\\SQ\\Resolvers;\n\nuse Railken\\SQ\\Resolvers\\ComparisonOperatorResolver;\nuse App\\SQ\\Nodes\\CustomNode;\n\nclass CustomResolver extends ComparisonOperatorResolver\n{\n    /**\n     * Node resolved\n     *\n     * @var string\n     */\n    public $node = CustomNode::class;\n\n    /**\n     * Regex token\n     *\n     * @var string\n     */\n    public $regex = [\n        '/(?\u003c![^\\s])custom(?![^\\s])/i',\n    ];\n}\n```\n\n\nCustomNode.php\n```php\n\u003c?php\nnamespace App\\SQ\\Nodes;\n\nuse Railken\\SQ\\Nodes\\ComparisonOperatorNode;\n\nclass CustomNode extends ComparisonOperatorNode\n{\n    /**\n     * Value\n     *\n     * @var string\n     */\n    public $value = 'CUSTOM';\n}\n```\n\n\nRemember to add the resolver when you're creating the instance of the parser\n\n```php\n\u003c?php\n$parser-\u003eaddResolvers([\n   new \\App\\SQ\\Resolvers\\CustomResolver(),\n});\n\n```\n\nIf you have a more complicated node to resolve simply add the method resolve to the CustomResolver. \n\n```php\n\u003c?php\nnamespace App\\SQ\\Resolvers;\n\nuse Railken\\SQ\\Contracts\\ResolverContract;\nuse Railken\\SQ\\Contracts\\NodeContract;\n\nclass CustomResolver implements ResolverContract\n{\n\n   /**\n     * Resolve node\n     *\n     * @param NodeContract $node\n     *\n     * @return NodeContract\n     */\n    public function resolve(NodeContract $node)\n    {\n        return $node;\n    }\n\n}\n```\n\n### Railken\\SQ\\Languages\\BoomTree\\Resolvers\\CustomResolver\n\nThere is also a CustomResolver that works and cycle with all children and it's configurabile through a Closure, it's `Railken\\SQ\\Languages\\BoomTree\\Resolvers\\CustomResolver`. For istance you could attach a prefix to all KeyNode doing this:\n\n```php\nuse Railken\\SQ\\Languages\\BoomTree\\Resolvers;\nuse Railken\\SQ\\Languages\\BoomTree\\Nodes;\n\n$parser-\u003eaddResolvers([\n    new Resolvers\\CustomResolver(function ($node) {\n        if($node instanceof Nodes\\KeyNode) {\n            $node-\u003esetValue(\"myCustomPrefix.\".$node-\u003egetValue());\n        }\n    }),\n]);\n```\n\n## License\n\nOpen-source software licensed under the [MIT license](https://opensource.org/licenses/MIT).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frailken%2Fsearch-query","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Frailken%2Fsearch-query","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frailken%2Fsearch-query/lists"}