Modern analytics systems often repeat the same filtering logic across dashboards, APIs, reports, and AI applications.
For example:
· Delivered orders
· Cancelled orders
· Premium customers
· Late deliveries
· High-value orders
If every analyst rewrites these filters manually, the logic becomes duplicated, inconsistent, and difficult to maintain.
This is where Cube Segments become extremely useful.
Segments allow you to define reusable business filters once inside your semantic layer and reuse them everywhere.
In this post, we’ll learn:
· What Cube segments are
· Why they matter
· When to use them
· When NOT to use them
· How to implement them using a realistic food delivery platform schema
· Real-world analytics examples using PostgreSQL and Cube
We’ll use a production-style PostgreSQL schema containing:
· Customers
· Orders
· Restaurants
· Delivery events
· Payments
· Refunds
· Driver payouts
· Promotions
This makes the examples much closer to real enterprise analytics systems.
1. What Are Cube Segments?
In analytics systems, the same filtering conditions are often reused repeatedly across:
· Dashboards
· KPI reports
· Embedded analytics
· APIs
· AI-generated queries
· Operational monitoring
· Data notebooks
For example:
· Delivered orders
· Cancelled orders
· Premium customers
· Late deliveries
· High-value transactions
Without a semantic layer, teams usually rewrite these conditions repeatedly using raw SQL:
WHERE order_status = 'DELIVERED'
This creates several problems:
· SQL duplication: Same logic repeated everywhere
· Inconsistent definitions: Different teams define metrics differently
· Hard maintenance: Business rule changes require updating many dashboards
· BI drift: Tableau, Superset, Power BI may all implement logic differently
· AI inconsistency: AI-generated queries may produce different business logic
1.1 What Is a Segment?
A segment is a named reusable filter defined inside a Cube model. Think of a segment as a reusable WHERE clause with a business-friendly name.
Instead of rewriting SQL filters repeatedly, you define them once inside the semantic layer and reuse them everywhere.
Suppose we have a food delivery platform. The orders table contains:
| order_id | order_status | final_amount | | -------- | ------------ | ------------ | | 1 | DELIVERED | 850 | | 2 | CANCELLED | 420 | | 3 | DELIVERED | 1200 |
We frequently need analytics only for delivered orders. Instead of rewriting:
WHERE order_status = 'DELIVERED'
we define a segment once.
cubes: - name: orders sql_table: public.orders description: Food delivery orders fact table measures: - name: count type: count description: Total number of orders - name: total_revenue sql: final_amount type: sum description: Total order revenue - name: average_order_value sql: final_amount type: avg description: Average order value dimensions: - name: order_id sql: order_id type: number primary_key: true - name: order_status sql: order_status type: string - name: payment_status sql: payment_status type: string - name: final_amount sql: final_amount type: number - name: ordered_at sql: ordered_at type: time segments: - name: delivered_orders description: Orders successfully delivered to customers sql: "{CUBE}.order_status = 'DELIVERED'"
Let's understand each part in the segment definition.
segments: - name: delivered_orders sql: "{CUBE}.order_status = 'DELIVERED'"
|
Property |
Meaning |
|
segments |
Section where reusable filters are defined |
|
name |
Business-friendly reusable filter name |
|
sql |
SQL condition injected into generated queries |
|
{CUBE} |
Placeholder automatically replaced with cube table alias |
What Does {CUBE} Mean?
Cube dynamically generates SQL queries. {CUBE} is replaced with the actual SQL table alias.
For example:
{CUBE}.order_status becomes orders.order_status
This abstraction makes Cube models portable and maintainable.
1.2 Query Specification Using the Segment
Now suppose a dashboard wants "Total revenue from delivered orders only". Instead of manually writing filters, the query simply references the segment.
measures: - orders.total_revenue segments: - orders.delivered_orders
How Cube Interprets This Query?
Cube internally understands:
- orders.total_revenue: Aggregate final_amount using SUM
- orders.delivered_orders: Apply predefined delivered filter
Generated SQL
Cube generates SQL similar to:
SELECT sum("orders".final_amount) "orders__total_revenue" FROM public.orders AS "orders" WHERE ("orders".order_status = 'DELIVERED')
Notice something important that the dashboard never explicitly wrote "WHERE order_status = 'DELIVERED'". The semantic layer injected it automatically. This is the power of reusable business logic.
1.3 Why This Is Powerful
Imagine your company has:
· 40 dashboards
· 15 APIs
· 10 AI applications
· 5 data science notebooks
Without segments every system rewrites:
WHERE order_status = 'DELIVERED'
If business changes the logic later:
WHERE order_status IN ('DELIVERED', 'COMPLETED')
You must update dozens of places. With segments, you update only one place.
segments: - name: delivered_orders sql: "{CUBE}.order_status IN ('DELIVERED', 'COMPLETED')"
Every dashboard and API instantly inherits the new logic. This is one of the biggest advantages of semantic layers.
Note:
· A segment is NOT a metric.
· A segment is NOT a dimension.
· A segment is:
o a reusable filter
o reusable business logic
o centralized WHERE condition
Example:
· WHERE clause is a Segment
· SUM() is a Measure
· GROUP BY column is a Dimension
2. Examples using orders cube
We are using following cube spec for orders.
cubes: - name: orders sql_table: public.orders measures: - name: count type: count - name: total_revenue sql: final_amount type: sum - name: avg_delivery_time sql: actual_delivery_minutes type: avg dimensions: - name: order_id sql: order_id type: number primary_key: true - name: order_status sql: order_status type: string - name: payment_status sql: payment_status type: string - name: final_amount sql: final_amount type: number - name: customer_rating sql: customer_rating type: number - name: estimated_delivery_minutes sql: estimated_delivery_minutes type: number - name: actual_delivery_minutes sql: actual_delivery_minutes type: number - name: ordered_at sql: ordered_at type: time
Example 1: Orders successfully delivered to customers.
Segment Definition
segments: - name: delivered_orders description: Successfully delivered orders sql: "{CUBE}.order_status = 'DELIVERED'"
Query Spec
measures: - orders.total_revenue segments: - orders.delivered_orders
Generated SQL
SELECT sum("orders".final_amount) "orders__total_revenue" FROM public.orders AS "orders" WHERE ("orders".order_status = 'DELIVERED')
Example 2: Cancelled Orders Segment
Cancellation analytics are critical in food delivery systems.
Segment Definition
segments: - name: cancelled_orders description: Orders cancelled before completion sql: "{CUBE}.order_status = 'CANCELLED'"
Query Spec
measures: - orders.count segments: - orders.cancelled_orders
Generated SQL
SELECT count("orders".order_id) "orders__count" FROM public.orders AS "orders" WHERE ("orders".order_status = 'CANCELLED')
Example 3: Late Deliveries Segment
This is where segments become very powerful. Instead of simple equality filters, we now use business logic.
Segment Definition
segments: - name: late_deliveries description: Orders delivered after estimated time sql: | {CUBE}.actual_delivery_minutes > {CUBE}.estimated_delivery_minutes
Query Spec
measures: - orders.count segments: - orders.late_deliveries
Generated Query
SELECT count("orders".order_id) "orders__count" FROM public.orders AS "orders" WHERE ("orders".actual_delivery_minutes > "orders".estimated_delivery_minutes )
Example 4: High-Value Orders Segment
Many businesses track premium transactions.
Segment Definition
segments: - name: high_value_orders description: Orders above 1000 INR sql: "{CUBE}.final_amount >= 1000"
Query Spec
measures: - orders.total_revenue segments: - orders.high_value_orders
Generated Query
SELECT sum("orders".final_amount) "orders__total_revenue" FROM public.orders AS "orders" WHERE ("orders".final_amount >= 1000)
Example 5: Poor Customer Ratings Segment
Customer experience analytics often require reusable filters.
Segment Definition
segments: - name: poorly_rated_orders description: Orders with low customer ratings sql: "{CUBE}.customer_rating <= 2"
Query Spec
measures: - orders.count segments: - orders.poorly_rated_orders
Generated Query
SELECT count("orders".order_id) "orders__count" FROM public.orders AS "orders" WHERE ("orders".customer_rating <= 2)
Example 6: Payment Failure Segment
Operational finance teams frequently monitor failed payments.
Segment Definition
segments: - name: failed_payments description: Orders with failed payments sql: "{CUBE}.payment_status = 'FAILED'"
Query Spec
measures: - orders.count segments: - orders.failed_payments
Generated Query
SELECT count("orders".order_id) "orders__count" FROM public.orders AS "orders" WHERE ("orders".payment_status = 'FAILED')
Example 7: Complex Operational Segment
Orders are operationally problematic if cancelled OR delayed by more than 20 minutes
Segment Definition
segments: - name: operational_issues description: Orders with major operational problems sql: | {CUBE}.order_status = 'CANCELLED' OR ( {CUBE}.actual_delivery_minutes > {CUBE}.estimated_delivery_minutes + 20 )
Query Spec
measures: - orders.count segments: - orders.operational_issues
Generated Query
SELECT count("orders".order_id) "orders__count" FROM public.orders AS "orders" WHERE ("orders".order_status = 'CANCELLED' OR ( "orders".actual_delivery_minutes > "orders".estimated_delivery_minutes + 20 ) )
3. Examples using delivery_events table
One of the most powerful parts of our food delivery platform schema is the delivery_events table.
Unlike traditional transactional tables such as orders, the delivery_events table behaves like an event stream.
This enables:
· real-time analytics
· operational monitoring
· live tracking dashboards
· logistics intelligence
· driver movement analytics
· SLA monitoring
· event-driven AI analytics
This is where Cube segments become extremely valuable.
CREATE TABLE public.delivery_events ( event_id bigserial NOT NULL, order_id int8 NOT NULL, driver_id int8 NULL, event_type varchar(100) NOT NULL, event_timestamp timestamp NOT NULL, latitude numeric(10, 6) NULL, longitude numeric(10, 6) NULL, event_metadata jsonb NULL, created_at timestamp DEFAULT CURRENT_TIMESTAMP NULL, CONSTRAINT delivery_events_pkey PRIMARY KEY (event_id) );
Let’s create spec for delivery_events.
cubes: - name: delivery_events sql_table: public.delivery_events description: Real-time delivery lifecycle event stream measures: - name: count type: count dimensions: - name: event_id sql: event_id type: number primary_key: true - name: order_id sql: order_id type: number - name: driver_id sql: driver_id type: number - name: event_type sql: event_type type: string - name: latitude sql: latitude type: number - name: longitude sql: longitude type: number - name: event_timestamp sql: event_timestamp type: time segments: - name: driver_wait_events description: Events where drivers are waiting sql: "{CUBE}.event_type = 'DRIVER_WAITING'"
Example 1: Driver Waiting Events
This segment identifies events where:
· driver arrived
· restaurant delayed preparation
· operational inefficiency occurred
Segment Definition
segments: - name: driver_wait_events description: Events where drivers are waiting sql: "{CUBE}.event_type = 'DRIVER_WAITING'"
Query Spec
measures: - delivery_events.count segments: - delivery_events.driver_wait_events
Generated Query
SELECT count("delivery_events".event_id) "delivery_events__count" FROM public.delivery_events AS "delivery_events" WHERE ("delivery_events".event_type = 'DRIVER_WAITING')
Example 2: Failed Delivery Segment
Now let’s create a more advanced reusable operational definition.
A delivery is operationally problematic if:
order cancelled
OR
customer unreachable
Segment Definition
segments: - name: failed_delivery_events description: Operational delivery failure events sql: | {CUBE}.event_type IN ( 'ORDER_CANCELLED', 'CUSTOMER_NOT_RESPONDING' )
Query Spec
measures: - delivery_events.count segments: - delivery_events.failed_delivery_events
Generated Query
SELECT count("delivery_events".event_id) "delivery_events__count" FROM public.delivery_events AS "delivery_events" WHERE ("delivery_events".event_type IN ( 'ORDER_CANCELLED', 'CUSTOMER_NOT_RESPONDING' ) )
4. When NOT to Use Segments
One of the biggest beginner mistakes in Cube modeling is trying to convert every filter into a segment.
At first, segments look extremely convenient because they let you reuse filtering logic.
So beginners often start creating segments like:
segments: - name: bangalore_orders sql: "{CUBE}.city = 'Bangalore'"
or:
segments: - name: hyderabad_orders sql: "{CUBE}.city = 'Hyderabad'"
or:
segments: - name: mumbai_orders sql: "{CUBE}.city = 'Mumbai'"
This looks harmless initially. But in real enterprise systems, this becomes a serious modeling problem.
4.1 The Core Rule
Use segments for:
· reusable business logic
· operational definitions
· complex filters
· standardized KPIs
Do NOT use segments for:
· highly dynamic filter values, use dimension filters in Query Spec.
· user-driven exploration
· frequently changing values
· ad hoc slicing
Dimension filter in Query spec example
measures: - orders.total_revenue filters: - member: orders.city operator: equals values: - Bangalore
4.2 Another Bad Example
segments: - name: orders_above_500 sql: "{CUBE}.final_amount > 500" - name: orders_above_1000 sql: "{CUBE}.final_amount > 1000" - name: orders_above_2000 sql: "{CUBE}.final_amount > 2000"
This is usually a bad design. Why?
Because users may want:
700
1500
2500
custom ranges
Better solution, use filters dynamically in Query Spec
measures: - orders.count filters: - member: orders.final_amount operator: gt values: - '1000'
Before creating a segment, it is important to ask whether you are defining reusable business logic or simply hardcoding a filter value. This distinction is one of the most important concepts in semantic modeling. If the condition represents a meaningful business concept that will be reused consistently across dashboards, APIs, reports, and AI queries such as late deliveries, failed payments, or operational issues, then it is a good candidate for a segment.
However, if the condition is only a dynamic filter value chosen by users at runtime, such as a city name, date range, cuisine, or amount threshold, then dimensions and query filters are a much better fit. Segments should represent stable, reusable business definitions, while dimensions and filters should handle flexible exploratory filtering and user-driven analysis.
5. Final Thoughts
Segments are one of the most powerful features in Cube semantic modeling because they allow organizations to centralize and standardize reusable business logic inside the semantic layer. Instead of repeatedly rewriting the same SQL filters across dashboards, APIs, notebooks, and AI-generated queries, teams can define business rules once and reuse them everywhere consistently. This not only reduces SQL duplication, but also simplifies analytics development, improves governance, and ensures that all consumers of data are working with the same definitions and KPIs.
Throughout this guide, we used a realistic food delivery platform schema containing orders, delivery events, payments, refunds, drivers, restaurants, and promotions. Using a real-world domain makes it much easier to understand how segments are applied in enterprise analytics systems compared to overly simplified toy examples. Real production systems frequently require reusable operational filters such as late deliveries, failed payments, SLA breaches, refund scenarios, or delivery event classifications. These are exactly the kinds of business definitions that segments are designed to model.
We also explored an equally important concept: knowing when not to use segments. Not every filter belongs in the semantic layer as a reusable segment. Dynamic user-driven filters such as city names, date ranges, cuisines, or arbitrary thresholds are usually better modeled using dimensions and query filters. Strong semantic modeling is not just about creating segments—it is about carefully deciding which logic should become reusable business meaning and which logic should remain dynamic and exploratory.
As your semantic layer grows, segments become foundational building blocks for scalable analytics architecture. They help create a shared business vocabulary across engineering, analytics, BI, and AI systems. In modern data platforms where dashboards, APIs, embedded analytics, and AI agents all consume the same semantic layer, reusable segments provide the consistency and governance necessary for trustworthy analytics at scale.
Ultimately, segments transform raw SQL filters into governed business concepts. That is one of the core reasons semantic layers like Cube are becoming increasingly important in modern analytics and AI-driven data architectures.
Previous Next Home
No comments:
Post a Comment