Skip to content

Commit 7ac304b

Browse files
committed
docs: Add /query/sql endpoint and RETURN DISTINCT documentation
- Document POST /query/sql endpoint for SQL generation - Explain SQL array format with SET ROLE example - Add role-based connection pool explanation - Document RETURN DISTINCT for de-duplicating results - Add role parameter to /query endpoint docs - Include Python and curl examples - Note cache behavior and performance characteristics
1 parent 4b7e7fa commit 7ac304b

1 file changed

Lines changed: 154 additions & 0 deletions

File tree

docs/api.md

Lines changed: 154 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,10 +35,17 @@ Content-Type: application/json
3535

3636
**Parameters:**
3737
- `query` (string, required): Cypher query to execute
38+
- Supports `RETURN DISTINCT` for de-duplicating results ✅ **[ADDED: v0.5.1]**
39+
- Use when multiple graph paths lead to the same node (e.g., friend-of-friend queries)
40+
- Example: `MATCH (a)-[:FOLLOWS]->(f)-[:FOLLOWS]->(fof) RETURN DISTINCT fof.name`
3841
- `parameters` (object, optional): Query parameters for parameterized queries ✅ **[COMPLETED: Nov 10, 2025]**
3942
- Supports all JSON data types: String, Int, Float, Bool, Array, Null
4043
- Use `$paramName` syntax in queries (e.g., `WHERE n.age >= $minAge`)
4144
- SQL injection prevention built-in
45+
- `role` (string, optional): ClickHouse RBAC role name for query execution ✅ **[ADDED: v0.5.1]**
46+
- Uses role-based connection pools for optimal performance
47+
- No `SET ROLE` overhead on query execution
48+
- Example: `"analyst"`, `"admin"`
4249
- `schema_name` (string, optional): Graph schema/database name to use for this query. Defaults to `"default"`. Enables multi-database support for queries. **Note**: The `USE` clause in the query itself takes precedence over this parameter.
4350

4451
**Response Format:**
@@ -98,6 +105,153 @@ Content-Type: application/json
98105
}
99106
```
100107

108+
#### POST /query/sql
109+
Generate SQL from Cypher query without executing it. ✅ **Production-Ready**
110+
111+
This endpoint translates Cypher queries to ClickHouse SQL and returns the generated SQL statements as an array, along with query metadata. Useful for:
112+
- Debugging query translation
113+
- Integrating ClickGraph translation into other tools
114+
- Understanding how Cypher patterns map to SQL
115+
- Testing RBAC role handling (includes `SET ROLE` statements when applicable)
116+
117+
**Request Format:**
118+
```http
119+
POST /query/sql
120+
Content-Type: application/json
121+
122+
{
123+
"query": "MATCH (u:User) WHERE u.age > $minAge RETURN u.name, u.age",
124+
"parameters": {"minAge": 25},
125+
"role": "analyst",
126+
"schema_name": "default",
127+
"include_plan": false
128+
}
129+
```
130+
131+
**Parameters:**
132+
- `query` (string, required): Cypher query to translate
133+
- `parameters` (object, optional): Query parameters (shown in SQL as `$paramName` placeholders)
134+
- `view_parameters` (object, optional): ClickHouse view parameters (ClickHouse-specific)
135+
- `role` (string, optional): ClickHouse RBAC role name (adds `SET ROLE` to SQL array)
136+
- `schema_name` (string, optional): Graph schema to use (defaults to `"default"`)
137+
- `target_database` (string, optional): Target SQL dialect (defaults to `"clickhouse"`)
138+
- `include_plan` (boolean, optional): Include logical plan in response (defaults to `false`)
139+
140+
**Response Format:**
141+
```http
142+
200 OK
143+
Content-Type: application/json
144+
145+
{
146+
"cypher_query": "MATCH (u:User) WHERE u.age > $minAge RETURN u.name, u.age",
147+
"target_database": "clickhouse",
148+
"sql": [
149+
"SET ROLE analyst",
150+
"SELECT \n u.name AS \"u.name\", \n u.age AS \"u.age\"\nFROM users AS u\nWHERE u.age > $minAge"
151+
],
152+
"parameters": {
153+
"minAge": 25
154+
},
155+
"role": "analyst",
156+
"metadata": {
157+
"query_type": "read",
158+
"cache_status": "HIT",
159+
"parse_time_ms": 1.234,
160+
"planning_time_ms": 5.678,
161+
"sql_generation_time_ms": 0.456,
162+
"total_time_ms": 7.368
163+
},
164+
"logical_plan": null
165+
}
166+
```
167+
168+
**Response Fields:**
169+
- `cypher_query` (string): Original Cypher query
170+
- `target_database` (string): Target SQL dialect (`"clickhouse"`, `"postgresql"`, etc.)
171+
- `sql` (array of strings): Generated SQL statements to execute in order
172+
- Example: `["SET ROLE analyst", "SELECT ..."]` when role is specified
173+
- Example: `["SELECT ..."]` when no role specified
174+
- Future: May include multi-statement queries like `["CREATE TEMP TABLE ...", "SELECT ...", "DROP TABLE ..."]`
175+
- `parameters` (object, optional): Query parameters (if provided in request)
176+
- `view_parameters` (object, optional): View parameters (if provided in request)
177+
- `role` (string, optional): Role name (if provided in request)
178+
- `metadata` (object): Query translation metadata
179+
- `query_type` (string): Type of query (`"read"`, `"write"`, `"call"`, `"ddl"`)
180+
- `cache_status` (string): Whether SQL was cached (`"HIT"`, `"MISS"`)
181+
- `parse_time_ms` (number): Time to parse Cypher
182+
- `planning_time_ms` (number): Time to plan query
183+
- `sql_generation_time_ms` (number): Time to generate SQL
184+
- `total_time_ms` (number): Total translation time
185+
- `logical_plan` (string, optional): Logical plan representation (when `include_plan=true`)
186+
187+
**Example Usage:**
188+
189+
```bash
190+
# Basic SQL generation
191+
curl -X POST http://localhost:8080/query/sql \
192+
-H "Content-Type: application/json" \
193+
-d '{"query": "MATCH (u:User) RETURN u.name LIMIT 10"}'
194+
195+
# With role (includes SET ROLE in SQL array)
196+
curl -X POST http://localhost:8080/query/sql \
197+
-H "Content-Type: application/json" \
198+
-d '{
199+
"query": "MATCH (u:User) WHERE u.age > 25 RETURN u.name",
200+
"role": "analyst"
201+
}'
202+
203+
# With parameters and logical plan
204+
curl -X POST http://localhost:8080/query/sql \
205+
-H "Content-Type: application/json" \
206+
-d '{
207+
"query": "MATCH (u:User) WHERE u.age > $minAge RETURN u.name",
208+
"parameters": {"minAge": 25},
209+
"include_plan": true
210+
}'
211+
```
212+
213+
**Python Example:**
214+
```python
215+
import requests
216+
217+
response = requests.post('http://localhost:8080/query/sql', json={
218+
'query': 'MATCH (u:User)-[:FOLLOWS]->(f:User) WHERE u.name = $name RETURN f.name',
219+
'parameters': {'name': 'Alice'},
220+
'role': 'analyst',
221+
'include_plan': True
222+
})
223+
224+
data = response.json()
225+
print("SQL Statements:")
226+
for i, stmt in enumerate(data['sql'], 1):
227+
print(f"{i}. {stmt}")
228+
229+
print(f"\nTranslation time: {data['metadata']['total_time_ms']:.2f}ms")
230+
print(f"Cache: {data['metadata']['cache_status']}")
231+
```
232+
233+
**Error Response:**
234+
```http
235+
400 Bad Request
236+
Content-Type: application/json
237+
238+
{
239+
"error": "Cypher syntax error",
240+
"details": {
241+
"query": "MATCH (u:User WHERE u.age > 25",
242+
"position": 15,
243+
"message": "Expected ')' but found 'WHERE'"
244+
}
245+
}
246+
```
247+
248+
**Notes:**
249+
- SQL generation is **very fast** (typically <10ms) because it doesn't execute queries
250+
- Results are **cached** - identical queries return cached SQL instantly
251+
- The `sql` array format allows representing complex multi-statement operations
252+
- When `role` is specified, `SET ROLE` is included in the SQL array for visibility, but actual query execution uses role-based connection pools (no SET ROLE overhead)
253+
- Parameter placeholders (`$paramName`) are NOT substituted in the generated SQL - they remain as placeholders for you to substitute when executing
254+
101255
#### GET /schemas
102256
List all available graph schemas.
103257

0 commit comments

Comments
 (0)