Your First Query
Now that you have a cube defined, let's query it! Olytix Core provides multiple ways to access your semantic layer: REST API, GraphQL, and Python SDK.
Prerequisites
Make sure you have:
- A Olytix Core project with at least one cube defined
- The Olytix Core server running (
olytix-core serve)
Understanding Semantic Queries
Unlike traditional SQL, semantic queries use business concepts:
Traditional SQL:
SELECT region, SUM(total_amount)
FROM orders
GROUP BY region
Semantic Query:
{
"measures": ["orders.total_revenue"],
"dimensions": ["orders.region"]
}
The semantic layer handles:
- Generating optimal SQL
- Applying joins automatically
- Enforcing security rules
- Caching and optimization
REST API Queries
Basic Query
curl -X POST http://localhost:8000/api/v1/query \
-H "Content-Type: application/json" \
-d '{
"measures": ["orders.total_revenue", "orders.count"],
"dimensions": ["orders.region"]
}'
Response:
{
"data": [
{ "orders.region": "NORTH", "orders.total_revenue": 250000.00, "orders.count": 3200 },
{ "orders.region": "SOUTH", "orders.total_revenue": 180000.00, "orders.count": 2100 },
{ "orders.region": "EAST", "orders.total_revenue": 320000.00, "orders.count": 4500 },
{ "orders.region": "WEST", "orders.total_revenue": 275000.00, "orders.count": 3800 }
],
"query": {
"sql": "SELECT UPPER(region) AS region, SUM(total_amount) AS total_revenue, COUNT(*) AS count FROM fct_orders GROUP BY UPPER(region)",
"duration_ms": 45
}
}
Adding Filters
Filter your data using the filters parameter:
curl -X POST http://localhost:8000/api/v1/query \
-H "Content-Type: application/json" \
-d '{
"measures": ["orders.total_revenue"],
"dimensions": ["orders.status"],
"filters": [
{
"dimension": "orders.region",
"operator": "equals",
"value": "NORTH"
},
{
"dimension": "orders.order_date",
"operator": "gte",
"value": "2024-01-01"
}
]
}'
Filter Operators
| Operator | Description | Example |
|---|---|---|
equals | Exact match | "value": "completed" |
notEquals | Not equal | "value": "cancelled" |
contains | String contains | "value": "ship" |
gt, gte | Greater than (or equal) | "value": 100 |
lt, lte | Less than (or equal) | "value": 1000 |
inList | In array | "value": ["A", "B"] |
notInList | Not in array | "value": ["X", "Y"] |
between | Range | "value": [10, 100] |
set | Is not null | (no value needed) |
notSet | Is null | (no value needed) |
Time Dimension Queries
Query by time with granularity:
curl -X POST http://localhost:8000/api/v1/query \
-H "Content-Type: application/json" \
-d '{
"measures": ["orders.total_revenue", "orders.count"],
"dimensions": ["orders.order_date.month"],
"filters": [
{
"dimension": "orders.order_date.year",
"operator": "equals",
"value": 2024
}
],
"order_by": [
{ "field": "orders.order_date.month", "direction": "asc" }
]
}'
Response:
{
"data": [
{ "orders.order_date.month": "2024-01", "orders.total_revenue": 125000.00, "orders.count": 1523 },
{ "orders.order_date.month": "2024-02", "orders.total_revenue": 142000.00, "orders.count": 1687 },
{ "orders.order_date.month": "2024-03", "orders.total_revenue": 158000.00, "orders.count": 1892 }
]
}
Ordering and Limiting
Control result ordering and pagination:
curl -X POST http://localhost:8000/api/v1/query \
-H "Content-Type: application/json" \
-d '{
"measures": ["orders.total_revenue"],
"dimensions": ["orders.customer_id"],
"order_by": [
{ "field": "orders.total_revenue", "direction": "desc" }
],
"limit": 10,
"offset": 0
}'
GraphQL Queries
Olytix Core also exposes a GraphQL endpoint for more flexible querying.
Basic GraphQL Query
query GetOrdersByRegion {
query(
measures: ["orders.total_revenue", "orders.count"]
dimensions: ["orders.region"]
) {
data
query {
sql
duration_ms
}
}
}
Using curl:
curl -X POST http://localhost:8000/graphql \
-H "Content-Type: application/json" \
-d '{
"query": "query { query(measures: [\"orders.total_revenue\"], dimensions: [\"orders.region\"]) { data } }"
}'
GraphQL with Variables
query GetOrders($measures: [String!]!, $dimensions: [String!]!, $filters: [FilterInput!]) {
query(measures: $measures, dimensions: $dimensions, filters: $filters) {
data
query {
sql
}
}
}
Variables:
{
"measures": ["orders.total_revenue"],
"dimensions": ["orders.status"],
"filters": [
{
"dimension": "orders.region",
"operator": "equals",
"value": "NORTH"
}
]
}
Exploring the Schema
Access the GraphQL Playground at http://localhost:8000/graphql to:
- Browse available queries
- See all cubes, measures, and dimensions
- Test queries interactively
Python SDK
For Python applications, use the Olytix Core client:
Installation
pip install olytix-core-client
Basic Usage
from olytix-core import Olytix CoreClient
# Connect to Olytix Core
client = Olytix CoreClient("http://localhost:8000")
# Simple query
result = client.query(
measures=["orders.total_revenue", "orders.count"],
dimensions=["orders.region"]
)
# Access data
for row in result.data:
print(f"{row['orders.region']}: ${row['orders.total_revenue']:,.2f}")
Query with Filters
from olytix-core import Olytix CoreClient, Filter
client = Olytix CoreClient("http://localhost:8000")
result = client.query(
measures=["orders.total_revenue"],
dimensions=["orders.status", "orders.order_date.month"],
filters=[
Filter("orders.region", "equals", "NORTH"),
Filter("orders.order_date", "gte", "2024-01-01"),
],
order_by=[("orders.order_date.month", "asc")],
limit=12
)
print(result.data)
print(f"Query took {result.query.duration_ms}ms")
Pandas Integration
import pandas as pd
from olytix-core import Olytix CoreClient
client = Olytix CoreClient("http://localhost:8000")
# Get data as DataFrame
df = client.query(
measures=["orders.total_revenue", "orders.count"],
dimensions=["orders.order_date.month"]
).to_dataframe()
# Now use pandas for analysis
df['avg_order_value'] = df['orders.total_revenue'] / df['orders.count']
print(df.describe())
Async Queries
For large datasets, use async queries:
import asyncio
from olytix-core import Olytix CoreClient
async def main():
client = Olytix CoreClient("http://localhost:8000")
# Start async query
job = await client.query_async(
measures=["orders.total_revenue"],
dimensions=["orders.customer_id"]
)
# Check status
while not job.is_complete:
print(f"Progress: {job.progress}%")
await asyncio.sleep(1)
await job.refresh()
# Get results
result = await job.get_result()
print(f"Got {len(result.data)} rows")
asyncio.run(main())
Query Examples
Top Customers by Revenue
curl -X POST http://localhost:8000/api/v1/query \
-H "Content-Type: application/json" \
-d '{
"measures": ["orders.total_revenue", "orders.count"],
"dimensions": ["orders.customer_id"],
"order_by": [{ "field": "orders.total_revenue", "direction": "desc" }],
"limit": 10
}'
Monthly Revenue Trend
curl -X POST http://localhost:8000/api/v1/query \
-H "Content-Type: application/json" \
-d '{
"measures": ["orders.total_revenue"],
"dimensions": ["orders.order_date.month"],
"filters": [
{
"dimension": "orders.order_date.year",
"operator": "equals",
"value": 2024
}
],
"order_by": [{ "field": "orders.order_date.month", "direction": "asc" }]
}'
Revenue by Region and Status
curl -X POST http://localhost:8000/api/v1/query \
-H "Content-Type: application/json" \
-d '{
"measures": ["orders.total_revenue", "orders.avg_order_value"],
"dimensions": ["orders.region", "orders.status"],
"filters": [
{
"dimension": "orders.status",
"operator": "notEquals",
"value": "cancelled"
}
]
}'
Orders in Date Range
curl -X POST http://localhost:8000/api/v1/query \
-H "Content-Type: application/json" \
-d '{
"measures": ["orders.count"],
"dimensions": ["orders.order_date.day"],
"filters": [
{
"dimension": "orders.order_date",
"operator": "between",
"value": ["2024-01-01", "2024-01-31"]
}
]
}'
Understanding Query Response
Every query returns:
{
"data": [...], // The result rows
"query": {
"sql": "...", // Generated SQL
"duration_ms": 45 // Execution time
},
"meta": {
"total_rows": 100, // Total available rows
"returned_rows": 10, // Rows in this response
"cached": false // Whether result was cached
}
}
Error Handling
Invalid Measure/Dimension
{
"error": {
"code": "INVALID_MEASURE",
"message": "Measure 'orders.invalid_measure' not found",
"available_measures": ["orders.count", "orders.total_revenue", "orders.avg_order_value"]
}
}
Invalid Filter
{
"error": {
"code": "INVALID_FILTER",
"message": "Operator 'like' is not valid for dimension type 'number'",
"valid_operators": ["equals", "notEquals", "gt", "gte", "lt", "lte", "between"]
}
}
Next Steps
Now that you can query your semantic layer: