{"id":28660208,"url":"https://github.com/ddrcode/querylang","last_synced_at":"2025-06-13T10:11:04.176Z","repository":{"id":298390991,"uuid":"998729967","full_name":"ddrcode/querylang","owner":"ddrcode","description":null,"archived":false,"fork":false,"pushed_at":"2025-06-10T21:31:13.000Z","size":21,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2025-06-10T22:24:25.377Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Rust","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/ddrcode.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"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,"zenodo":null}},"created_at":"2025-06-09T06:47:11.000Z","updated_at":"2025-06-10T21:31:13.000Z","dependencies_parsed_at":"2025-06-10T22:24:30.913Z","dependency_job_id":"0845c074-1ad4-4a56-818c-f39ff0a1ba60","html_url":"https://github.com/ddrcode/querylang","commit_stats":null,"previous_names":["ddrcode/querylang"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/ddrcode/querylang","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ddrcode%2Fquerylang","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ddrcode%2Fquerylang/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ddrcode%2Fquerylang/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ddrcode%2Fquerylang/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/ddrcode","download_url":"https://codeload.github.com/ddrcode/querylang/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ddrcode%2Fquerylang/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":259624771,"owners_count":22886334,"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":[],"created_at":"2025-06-13T10:10:52.070Z","updated_at":"2025-06-13T10:11:04.122Z","avatar_url":"https://github.com/ddrcode.png","language":"Rust","funding_links":[],"categories":[],"sub_categories":[],"readme":"# QueryServer POC\n\nA proof of concept (POC) introducing a simple, SQL-inspired language for querying intraday stock\ndata over a time range with custom step size.  Returns data as a table, in either text or JSON format.\n\n\n## Quickstart\n\n1. **Start the mock GraphQL server** (port 8001)\n    ``` cargo run --bin=gqlmock ```\n\n2. **Start the query server** (port 3000):\n    ``` RUST_LOG=debug cargo run --bin=query ```\n\n3. **Run a sample query:**\n    ```\n    curl -X POST http://localhost:3000/query \\\n      -H \"Content-Type: application/json\" \\\n      -d '{\"query\": \"GET APPL.max, GOOGL.open, GOOGL.volume FOR LAST 1 day STEP 1 hour\", \"format\": \"text\"}'\n    ```\n\nSample text output:\n\n```\ntime step  APPL.max  GOOGL.open  GOOGL.v...\n---------------------------------------------\n        0   114.86     109.11    2046.05\n        1   153.54     110.65    2139.16\n        2   115.33     143.61    1587.69\n        3   140.78     149.52    2100.04\n        4   164.45     144.66    1809.34\n        5   148.83     137.22    2204.91\n        6   159.66     138.62    2098.51\n        7   133.88     136.67    2110.11\n        8   159.40     117.53    1675.00\n        9   164.24     126.36    1521.90\n       10   114.29     108.58    1821.63\n       11   116.58     121.65    2243.59\n       12   151.10     149.86    2002.58\n       13   164.82     103.26    2052.62\n       14   159.33     100.71    2150.00\n       15   118.98     107.39    2103.16\n       16   139.43     137.34    1861.30\n       17   126.42     119.04    1788.59\n       18   114.70     109.75    2184.75\n       19   134.39     103.38    2212.73\n       20   135.20     127.30    1957.11\n       21   118.40     106.30    1513.93\n       22   147.36     135.15    1863.22\n       23   146.75     110.89    1745.25\n\n```\n\nGenerated GraphQL query example (server log):\n\n```\n2025-06-10T12:25:32.604435Z DEBUG request{method=POST uri=/query version=HTTP/1.1}: query::query_engine::gql_client: GraphQL Query payload: {\n  \"variables\": {\n    \"symbol\": \"GOOGL\",\n    \"metrics\": [\n      \"open\",\n      \"volume\"\n    ],\n    \"from\": \"2025-06-09T12:25:32.603833+00:00\",\n    \"to\": \"2025-06-10T12:25:32.603833+00:00\",\n    \"step\": \"1\"\n  },\n  \"query\": \"query GetMetrics($symbol: String!, $metrics: [String!]!, $from: String!, $to: String!, $step: String!) {\\n  getMetrics(symbol: $symbol, metrics: $metrics, from: $from, to: $to, step: $step) {\\n    timestamp\\n    values {\\n      metric\\n      value\\n    }\\n  }\\n}\\n\\n\",\n  \"operationName\": \"GetMetrics\"\n}\n```\n\n- The `format` parameter accepts `\"json\"` or `\"text\"` (default is `\"text\"`).\n- A web-based GraphQL playground is also available at `http://localhost:8001`.\n\n\n\n## Query language with examples\n\nThe language allows for querying prices and volumes from the last `n` time units in given interval (time series data).\nI.e. to retrieve GOOGL max price (per interval) for last 3 days in 2h intervals the query should look like\n\n```GET GOOGL.max FOR LAST 3 days STEP 2 hours```\n\nThe number of resulted rows is equal to `last 72 hours / 2 hours step = 36`.\nThe assumption is that if there is no data for given interval the value is 0.\n\n\n### Multiple Assets, Multiple Metrics\n\n```\nGET TSLA.open, AAPL.volume FOR LAST 1 day STEP 1 hour\n```\n\nResult: TSLA opening price and volume of AAPL for each hour in the last day.\n\n\n### Expressions\n\n```\nGET\n    APPL.volume / 1000,\n    (GOOGL.max - GOOGL.min) * 10\nFOR LAST 30 days\nSTEP 1 day\n```\n\n### Rules / assumptions\n\n- If no data for an interval, the value is 0.\n- Multiple metrics for the same asset produce single GQL query\n- Repeated assets in multiple expressions produce only a single GQL query\n- Case-sensitive; multi-line code.\n- DSL grammar is defined in [`query.pest`](src/adapter/parser/query.pest) file\n\n\n\n## Architecture Overview\n\n### Major components and crates\n\n- **HTTP server**: Axum, Tower, Tokio\n- **Language parser**: Pest\n- **GraphQL client**: graphql_client, reqwest\n- **Mock GraphQL server**: Axum, Tokio, async_graphql\n\n### Execution flow (for optimistic path)\n\n1. Query is sent to async HTTP server (Axum, Tower)\n2. Query is parsed with Pest, producing a `Query` structure\n3. Data targets (symbols and metrics) are extracted into a `QueryPlan`\n4. For each target, a separate GraphQL query is generated and sent. No duplicated data fetches are\n   guaranteed\n5. Data is collected (async) from the mock server and stored in memory\n6. Each query expression is executed on real data\n7. Output table is generated (as text or JSON)\n\n\u003cimg width=\"419\" alt=\"image\" src=\"https://github.com/user-attachments/assets/ad8fc9c3-5703-43f3-beb0-413734a4081e\" /\u003e\n\n\n## Features\n\n### Current\n\n- Async networking with Tokio (requests and GQL querying)\n- Parallel column generation (each column in a separate task)\n- Fast parsing engine\n- No panics/unwraps, robust error handling\n\n### Easy to add\n\n- parallel data post-processing with Rayon,\n- query/output caching\n- web client\n\n### Further considerations\n- Compile query language expressions to WebAssembly\n- streaming output data\n\n\n## Key Source Files\n\n- [`query_handler.rs`](src/api/query_handler.rs) - handles query request\n- [`parser.rs`](src/adapter/parser/parser.rs) - query parsing logic (grammar file:\n[`query.pest`](src/adapter/parser/query.pest))\n- [`metrics_repository_gql.rs`](src/repository/metrics_repository_gql.rs) - GraphQL client\n- [`query_service.rs`](src/service/query_service.rs) - main service (glue logic)\n- [`server.rs`](src/gql_server/server.rs) - mock GraphQL server\n\nModels:\n- [`query.rs`](src/domain/query.rs) - query (DSL) representation after parsing\n- [`query_plan.rs`](src/shared/query_plan.rs) - symbol/metric representation for GQL querying\n- [`table.rs`](src/domain/table.rs) - output data representation and formatting\n\n## Folder structure\n\n```\nsrc/\n├── api/                # API endpoint handlers\n├── service/            # System services (called by handlers)\n├── repository/         # Data access handlers and traits\n├── adapter/parser/     # Query DSL, grammar, parser code\n├── domain/             # Domain models (Query, Table, etc)\n├── shared/             # DTO's, non-domain structs, etc\n├── error/              # Error definitions\n├── main.rs             # Entrypoint for query server\n├── config.rs           # Configuration options\n└── ...\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fddrcode%2Fquerylang","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fddrcode%2Fquerylang","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fddrcode%2Fquerylang/lists"}