{"id":47997498,"url":"https://github.com/rrohitramsen/expression-evaluator","last_synced_at":"2026-04-04T12:02:30.582Z","repository":{"id":43350095,"uuid":"174217122","full_name":"rrohitramsen/Expression-Evaluator","owner":"rrohitramsen","description":"Expression Evaluator + Tree Data Structure + Postorder Traversal + Rest API + Spring Boot","archived":false,"fork":false,"pushed_at":"2022-03-06T19:54:44.000Z","size":446,"stargazers_count":0,"open_issues_count":0,"forks_count":1,"subscribers_count":2,"default_branch":"master","last_synced_at":"2024-12-06T21:22:57.451Z","etag":null,"topics":["data","data-structures","design-patterns","json","microservice","postorder","problem-solving","spring-boot","swagger-api","swagger-docs","swagger-ui","tree","tree-structure"],"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/rrohitramsen.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":"2019-03-06T20:36:53.000Z","updated_at":"2022-03-06T19:54:47.000Z","dependencies_parsed_at":"2022-08-20T02:41:38.218Z","dependency_job_id":null,"html_url":"https://github.com/rrohitramsen/Expression-Evaluator","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/rrohitramsen/Expression-Evaluator","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rrohitramsen%2FExpression-Evaluator","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rrohitramsen%2FExpression-Evaluator/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rrohitramsen%2FExpression-Evaluator/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rrohitramsen%2FExpression-Evaluator/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/rrohitramsen","download_url":"https://codeload.github.com/rrohitramsen/Expression-Evaluator/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rrohitramsen%2FExpression-Evaluator/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":31398770,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-04T10:20:44.708Z","status":"ssl_error","status_checked_at":"2026-04-04T10:20:06.846Z","response_time":60,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"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":["data","data-structures","design-patterns","json","microservice","postorder","problem-solving","spring-boot","swagger-api","swagger-docs","swagger-ui","tree","tree-structure"],"created_at":"2026-04-04T12:02:26.209Z","updated_at":"2026-04-04T12:02:30.571Z","avatar_url":"https://github.com/rrohitramsen.png","language":"Java","readme":"Expression Evaluator\n==========================\n\n`Problem Statement`\n-------------------\n* ![swagger-home](/src/main/resources/images/problem_statement.png)\n\n\n\n## `How to Start`\n* [expression_evaluator.sh](/expression_evaluator.sh) execute this script. \n```\n$ ./expression_evaluator.sh\n```\n* It will build the project and start the spring boot application.\n## How to access the rest API - Swagger-UI\n* After starting the application Click on [Swagger-home](http://localhost:8080/api/swagger-ui.html)\n* Sample Request is given below. Please use that for testing.\n\n`Solution`\n========= \n* Spring Boot based micro service.\n* Rest end point [/expression](/expression). Its a HTTP POST request.\n* `API Request`\n    * \"expression\" : Provide Array of expressions to be executed on the user.\n    * \"user\" : User entity. \n```json\n{\n  \"expression\": [\n[\"OR\", [\"IN\", \"event.category\", [\"infant\", \"child\", \"teen\"]], [\"LT\", \"user.age\", 18]],\n[\"AND\", [\"EQ\", \"user.address.city\", \"Los Angeles\"], [\"GT\", \"user.age\", 35]],\n[\"OR\", [\"EQ\", \"user.address.city\", \"San Francisco\"], [\"GT\", \"user.age\", 35]],\n[\"OR\", [\"EQ\", \"user.address.city\", \"Los Angeles\"], [\"EQ\", \"user.age\", 35]]\n\n]\n,\n  \"user\": {\n    \"address\": {\n      \"address-line\": \"XYZ Street\",\n      \"city\": \"San Francisco\",\n      \"state\": \"CA\",\n      \"zipcode\": \"94150\"\n    },\n    \"age\": 35,\n    \"event\": {\n      \"category\": \"infant\"\n    },\n    \"first-name\": \"Jhon\",\n    \"last-name\": \"Marley\"\n  }\n}\n```\n* `API Response`\n```json\n{\n  \"returnCode\": 200,\n  \"message\": \"Expression evaluated successfully.\",\n  \"response_code\": 200,\n  \"result_body\": {\n    \"[OR, [IN, event.category, [infant, child, teen]], [LT, user.age, 18]]\": true,\n    \"[OR, [EQ, user.address.city, Los Angeles], [EQ, user.age, 35]]\": true,\n    \"[OR, [EQ, user.address.city, San Francisco], [GT, user.age, 35]]\": true,\n    \"[AND, [EQ, user.address.city, Los Angeles], [GT, user.age, 35]]\": false\n  }\n}\n```\n* Operators are implemented using `Command Deisgn Pattern`\n```java\n/**\n * @author rohitkumar\n * creation date 22/07/18\n * project name expression-evaluator\n */\npublic interface Operator\u003cL, R\u003e {\n\n    /**\n     *\n     * @param left operand\n     * @param right operand or value\n     * @return true or false\n     */\n    boolean execute(L left, R right);\n}\n```\n* OR operator implementation. Rest of the other operators also implemented in the same way.\n```java\n/**\n * @author rohitkumar\n * creation date 22/07/18\n * project name expression-evaluator\n */\npublic class Or implements Operator\u003cBoolean, Boolean\u003e {\n\n    @Override\n    public boolean execute(Boolean left, Boolean right) {\n        return (left || right);\n    }\n}\n```\n* Operator Instantiation is handled using Factory Design Pattern. [OperatorFactory](/src/main/java/com/expression/evaluator/operator/OperatorFactory.java)\n* `TREE` data structure is used for expression tree. Class - [Tree](/src/main/java/com/expression/evaluator/tree/Node.java) \n* `Pre-Order` traversal is used for building the tree from expression. Class - [ExpressionEvaluatorEngine](/src/main/java/com/expression/evaluator/engine/ExpressionEvaluatorEngine.java) `buildTree()` method.\n```code\n/**\n     * @implNote Build Binary Tree of {@link com.expression.evaluator.operator.Operator} and {@link com.expression.evaluator.tree.Operand} using Pre order Traversal.\n     * Format - [ OPERATOR, OPERAND, COMPARISON_VALUE(S) ]\n     *          [\"AND\", [\"EQ\", \"user.address.city\", \"Los Angeles\"], [\"GT\", \"user.age\", 35]]\n     *          [\"OR\", [\"IN\", \"event.category\", [\"infant\", \"child\", \"teen\"]], [\"LT\", \"user.age\", 18]]\n     * @param user\n     * @param expression\n     * @return root node of the tree.\n     */\n    private static Node buildTree(User user, ArrayList\u003cObject\u003e expression) throws EvaluatorExpressionException {\n\n        Node root = null;\n        if (expression != null) {\n\n            /**\n             * Check whether its operator or not\n             */\n            Object value = expression.get(0);\n            if (OperatorNames.isOperator(value.toString())) {\n\n                Operator operator = OperatorFactory.getOperator(value.toString());\n                if (Objects.isNull(operator)) {\n                    throw new EvaluatorExpressionException(\"Invalid Operator Name,\"+value.toString());\n                }\n                root = new Node(operator);\n            }\n\n            if ( (expression.get(1) instanceof ArrayList) \u0026\u0026 isExpression((ArrayList\u003cObject\u003e) expression.get(1))) {\n                /**\n                 * sub-expression, recursive call\n                 */\n                root.setLeft(buildTree(user, (ArrayList\u003cObject\u003e) expression.get(1)));\n\n            } else {\n\n                /**\n                 * \"user.address.city\" , \"event.category\"\n                 */\n                Operand operand = buildOperandFromExpression(user, expression.get(1));\n                Node leftNode = new Node(operand);\n                root.setLeft(leftNode);\n            }\n\n            if ( (expression.get(2) instanceof ArrayList) \u0026\u0026 isExpression((ArrayList\u003cObject\u003e) expression.get(2))) {\n                /**\n                 * sub-expression, recursive call\n                 */\n                root.setRight(buildTree(user, (ArrayList\u003cObject\u003e) expression.get(2)));\n\n            } else {\n\n                /**\n                 * Substitution-Values\n                 */\n                Node rightNode = new Node(new Operand(expression.get(2)));\n                root.setRight(rightNode);\n            }\n\n        }\n        return root;\n    }\n```\n* `Post-Order` traversal is used for evaluation of expression tree. Class - [ExpressionEvaluatorEngine](/src/main/java/com/expression/evaluator/engine/ExpressionEvaluatorEngine.java) `evaluateExpressionTree()` method.\n```code\n private static Object evaluateExpressionTree(Node root) {\n\n        if (root != null) {\n\n            /**\n             * leaf node will contains the operand and substitution values..\n             */\n            if (root.getLeft() == null \u0026\u0026 root.getRight() == null) {\n                return root.getOperand();\n            }\n\n            /**\n             * Evaluate left subtree.\n             */\n            Object left = evaluateExpressionTree(root.getLeft());\n\n            /**\n             * Evaluate right subtree.\n             */\n            Object right = evaluateExpressionTree(root.getRight());\n\n            /**\n             * Since All non leaf nodes are Operator, Now evaluate the operator.\n             */\n            if (left instanceof Operand \u0026\u0026 right instanceof Operand) {\n\n                return root.getOperator().execute(((Operand)left).getValue(), ((Operand)right).getValue());\n\n            } else {\n\n                return root.getOperator().execute(left, right);\n\n            }\n\n\n        }\n        return false;\n    }\n```\n* `ExpressionEvaluatorEngine` is util class which provides all the tree traversal, expression evaluation methods.\n* `Swagger-UI` is used for rest documentation and testing.\n* Spring Junit and Mockito is used for Junt.\n```java\n@RunWith(SpringRunner.class)\npublic class APITest {\n\n    @InjectMocks\n    private API api;\n\n    private MockMvc mockMvc;\n    private ExpressionEvaluatorService expressionEvaluatorService;\n\n    @Before\n    public void setUp() {\n\n        MockitoAnnotations.initMocks(this);\n        expressionEvaluatorService = Mockito.mock(ExpressionEvaluatorServiceImpl.class);\n\n        Field field = ReflectionUtils.findField(API.class, \"expressionEvaluatorService\");\n        ReflectionUtils.makeAccessible(field);\n        ReflectionUtils.setField(field, api, expressionEvaluatorService);\n\n        this.mockMvc = MockMvcBuilders.standaloneSetup(api).build();\n\n    }\n\n    @Test\n    public void testEvaluateExpression() throws Exception {\n\n        String apiRequestJson = FileUtils.readFileIntoJson(APIRequest.class, \"api_request.json\");\n        Map\u003cObject, Boolean\u003e map = new HashMap\u003c\u003e();\n        map.put(\"[OR, [IN, event.category, [infant, child, teen]], [LT, user.age, 18]]\", true);\n        map.put(\"[OR, [EQ, user.address.city, Los Angeles], [EQ, user.age, 35]]\",true);\n\n        String message = \"Expression evaluated successfully.\";\n        APIResponse\u003cMap\u003cObject, Boolean\u003e\u003e  apiResponse = new APIResponse(message,  HttpStatus.OK.value(), map);\n        ResponseEntity expectedResponse = new ResponseEntity\u003cAPIResponse\u003e(apiResponse, HttpStatus.OK);\n        Mockito.when(expressionEvaluatorService.evaluateExpression(any())).thenReturn(map);\n\n        this.mockMvc.perform(post(\"/expression\")\n                .contentType(MediaType.APPLICATION_JSON_VALUE)\n                .content(apiRequestJson))\n                .andExpect(status().is(200))\n                .equals(expectedResponse);\n    }\n}\n```\n\n## How to use swagger-\n1. Open Swagger Home\n2. Execute the post request using request json.\n3. Server will return json response.\n![swagger-home](/src/main/resources/images/swagger_home.png)\n![swagger-request](src/main/resources/images/Swagger_Request.png)\n![swagger-response](/src/main/resources/images/Swagger_Response.png)\n\n\n### Cases not supported for now.\n* Not operator not supported.\n* Expression - [\"AND\", [\"IN\", \"event.category\", [\"infant\", \"child\", \"teen\"]], [\"LT1\", \"user.age\", 18]] is failing.\n\n### Thanks for your time.\n\n\n\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frrohitramsen%2Fexpression-evaluator","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Frrohitramsen%2Fexpression-evaluator","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frrohitramsen%2Fexpression-evaluator/lists"}