Tuesday, 21 October 2025

Querying Apache Druid via HTTP APIs

Apache Druid supports both SQL and native query APIs. Understanding both is crucial for flexible and optimized querying depending on your use case. In this post, we'll walk through how to run both SQL and native queries using curl, leveraging a sample sales dataset.

For example, I onboarded following sales_data to Druid.

 

sales_data.csv

timestamp,product,city,total_sales
2025-04-01T10:00:00Z,Laptop,Delhi,300
2025-04-01T10:00:00Z,Laptop,Delhi,200
2025-04-01T11:00:00Z,Tablet,Mumbai,150
2025-04-01T11:00:00Z,Tablet,Mumbai,50
2025-04-01T12:00:00Z,Mobile,Bengaluru,200
2025-04-01T13:00:00Z,Laptop,Hyderabad,250
2025-04-01T14:00:00Z,Tablet,Chennai,180
2025-04-01T15:00:00Z,Mobile,Pune,220
2025-04-01T15:00:00Z,Mobile,Pune,80

 


 

1. SQL Query via /druid/v2/sql

Druid’s SQL endpoint allows for familiar querying syntax:

 

Example SQL Query

Get total sales per product

SELECT product, SUM(total_sales) AS total_sales
FROM sales_data
GROUP BY product
ORDER BY total_sales DESC

 


 

Execute Using curl

Save the following to a file sql_query.json.

 

sql_query.json 

{
  "query": "SELECT product, SUM(total_sales) AS total_sales FROM sales_data GROUP BY product ORDER BY total_sales DESC"
}

 

Run the query

curl -X POST http://localhost:8888/druid/v2/sql -H 'Content-Type:application/json' -d @sql_query.json

$curl -X POST http://localhost:8888/druid/v2/sql -H 'Content-Type:application/json' -d @sql_query.json
[{"product":"Laptop","total_sales":750.0},{"product":"Mobile","total_sales":500.0},{"product":"Tablet","total_sales":380.0}]

You can pass this curl output to jq command to pretty print.

$curl -X POST http://localhost:8888/druid/v2/sql -H 'Content-Type:application/json' -d @sql_query.json | jq
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100   246  100   125  100   121   9939   9621 --:--:-- --:--:-- --:--:-- 20500
[
  {
    "product": "Laptop",
    "total_sales": 750.0
  },
  {
    "product": "Mobile",
    "total_sales": 500.0
  },
  {
    "product": "Tablet",
    "total_sales": 380.0
  }
]

 

You can even pass the payload directly to druid/v2/sql API.

curl -X POST http://localhost:8888/druid/v2/sql -H 'Content-Type:application/json' -d '{"query":"SELECT product, SUM(total_sales) AS total_sales FROM sales_data GROUP BY product ORDER BY total_sales DESC"}'

$curl -X POST http://localhost:8888/druid/v2/sql -H 'Content-Type:application/json' -d '{"query":"SELECT product, SUM(total_sales) AS total_sales FROM sales_data GROUP BY product ORDER BY total_sales DESC"}' | jq
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100   243  100   125  100   118   9806   9257 --:--:-- --:--:-- --:--:-- 20250
[
  {
    "product": "Laptop",
    "total_sales": 750.0
  },
  {
    "product": "Mobile",
    "total_sales": 500.0
  },
  {
    "product": "Tablet",
    "total_sales": 380.0
  }
]

2. Native Query via /druid/v2

Native queries are powerful, fine-grained JSON-based constructs used for more complex operations.

 

Example Native Query

Equivalent native groupBy query for total sales per product:

 

Save to native_query.json.

 

Execute Using curl

 

native_query.json

{
  "queryType": "groupBy",
  "dataSource": "sales_data",
  "granularity": "all",
  "dimensions": ["product"],
  "aggregations": [
    {
      "type": "longSum",
      "name": "total_sales",
      "fieldName": "total_sales"
    }
  ],
  "intervals": ["2025-04-01T00:00:00.000Z/2025-04-02T00:00:00.000Z"]
}

curl -X POST http://localhost:8888/druid/v2/?pretty -H 'Content-Type:application/json' -d @native_query.json

$curl -X POST http://localhost:8888/druid/v2/?pretty -H 'Content-Type:application/json' -d @native_query.json
[ {
  "version" : "v1",
  "timestamp" : "2025-04-01T00:00:00.000Z",
  "event" : {
    "product" : "Laptop",
    "total_sales" : 750
  }
}, {
  "version" : "v1",
  "timestamp" : "2025-04-01T00:00:00.000Z",
  "event" : {
    "product" : "Mobile",
    "total_sales" : 500
  }
}, {
  "version" : "v1",
  "timestamp" : "2025-04-01T00:00:00.000Z",
  "event" : {
    "product" : "Tablet",
    "total_sales" : 380
  }
} ]

 

Passing Native Query payload inline

curl -X POST http://localhost:8888/druid/v2/?pretty -H 'Content-Type:application/json' -d '{"queryType":"groupBy","dataSource":"sales_data","granularity":"all","dimensions":["product"],"aggregations":[{"type":"longSum","name":"total_sales","fieldName":"sales"}],"intervals":["2025-04-01T00:00:00.000Z/2025-04-02T00:00:00.000Z"]}'

$ curl -X POST http://localhost:8888/druid/v2/?pretty -H 'Content-Type:application/json' -d '{"queryType":"groupBy","dataSource":"sales_data","granularity":"all","dimensions":["product"],"aggregations":[{"type":"longSum","name":"total_sales","fieldName":"sales"}],"intervals":["2025-04-01T00:00:00.000Z/2025-04-02T00:00:00.000Z"]}'
[ {
  "version" : "v1",
  "timestamp" : "2025-04-01T00:00:00.000Z",
  "event" : {
    "product" : "Laptop",
    "total_sales" : null
  }
}, {
  "version" : "v1",
  "timestamp" : "2025-04-01T00:00:00.000Z",
  "event" : {
    "product" : "Mobile",
    "total_sales" : null
  }
}, {
  "version" : "v1",
  "timestamp" : "2025-04-01T00:00:00.000Z",
  "event" : {
    "product" : "Tablet",
    "total_sales" : null
  }
} ]

 

Previous                                                    Next                                                    Home

No comments:

Post a Comment