Skip to main content

Documentation Index

Fetch the complete documentation index at: https://honeydew.ai/docs/llms.txt

Use this file to discover all available pages before exploring further.

This guide helps Tableau users understand how to model their analytics in Honeydew.If you are not familiar with Tableau, head over to the introduction instead.

Introduction

Honeydew is a standalone semantic layer. It sits between your data warehouse and the tools that consume it: Tableau, Power BI, Excel, SQL clients, AI agents, notebooks. Tableau is one of those tools. The job of the semantic layer is to define the building blocks once and use them everywhere. Semantics include the following concepts:
  1. How business entities (such as “customer”) are mapped to data sources (which tables to read from)
  2. How data tables are related (which joins to use)
  3. How metrics are calculated (which aggregations to perform)
  4. Metadata (such as descriptions, display names, formatting) associated with presenting those objects to business users.
If you build dashboards in Tableau, you already work with all four: you map fields to tables in a data source, define relationships, write calculated fields and LODs, and add display formatting. The Honeydew equivalents share the same intent, just centralized so multiple workbooks, dashboards, and tools can reuse them. When you connect Tableau to Honeydew, Tableau queries a virtual table instead of querying raw warehouse tables directly. The logic that today lives in your Tableau workbooks (calculated fields, LOD expressions, data blending) moves into Honeydew where it can be reused across dashboards and tools.
The goal is not to replicate Tableau logic 1:1 in Honeydew. Instead, you define the building blocks (entities, relationships, metrics) once, and Tableau consumes them through a live connection.

Visual to declarative

In Tableau you think visually: drag a dimension to Rows, drop a measure on Columns, add a filter to the shelf, write an LOD expression to change the grain. In Honeydew you think declaratively: define entities, connect them with relationships, write metrics that describe what to calculate. The how - which joins, which GROUP BY, which filters - is resolved automatically at query time.
Tableau conceptHoneydew equivalent
Data Source / Data ModelWorkspace + Entities
Relationship / JoinRelationship
Dimension (green pill)Attribute
Measure (blue pill)Metric
Calculated Field (dimension)Calculated Attribute
Calculated Field (measure)Metric
LOD ExpressionCalculated attribute or metric with GROUP BY qualifier
Table CalculationDerived metric or calculated attribute
Set / GroupCalculated attribute
ParameterBI Parameter or conditional filtering
Context FilterSemantic filter
Data Source FilterSource filter
Published Data SourceDomain

Data Sources and Entities

A Tableau data source connects to one or more warehouse tables, defines joins between them, and exposes dimensions and measures for worksheets. In Honeydew, each table becomes an Entity. Joins between tables become Relationships defined once and reused across all queries.
A Tableau Published Data Source maps to a Honeydew Domain - a curated subset of the model tailored to a specific business area.

Custom SQL in data sources

Tableau allows a data source to use Custom SQL. An entity in Honeydew can also be backed by a custom SQL query instead of a single table - see source data.

Data blending

In Tableau, data blending links fields across separate physical data sources using a common dimension. Honeydew operates on a single physical source (a cloud data warehouse), so the equivalent depends on what was being blended. When the blended data lives in the same warehouse and just needs to be linked through a common dimension, use a relationship, often backed by a shared dimension. When the data lives in a different warehouse, database, or system, there is no Honeydew equivalent. The cross-source join has to happen either in the warehouse itself (through federation or external tables) or in the dashboard layer.

Relationships and Joins

Tableau’s data model supports relationships (logical layer) and joins (physical layer). Honeydew relationships combine both concepts:
  • Join fields: Honeydew supports composite keys (multiple fields) and calculated attributes as join keys - no need to create concatenated key fields.
  • Join type: LEFT, INNER, RIGHT, or FULL OUTER.
  • Cardinality: one-to-many and many-to-one work directly. Many-to-many requires a connecting table.
  • Cross-filtering: controls whether filters on one entity affect another. The default is bidirectional, matching how Tableau’s logical layer behaves.
In Tableau’s logical layer, tables are related but not joined until needed. Honeydew works similarly - joins are defined as relationships and only executed when a query requires fields from both entities.

Calculated Fields → Attributes

A Tableau calculated field that produces a dimension (string, date, boolean, etc.) maps to a calculated attribute in Honeydew.

Basic calculated fields

Tableau:
IF [Region] = "West" THEN "Pacific" ELSE "Other" END
Honeydew calculated attribute in the customer entity:
customer.region_group =
  CASE WHEN customer.region = 'West'
    THEN 'Pacific' ELSE 'Other' END
In Tableau, a calculated field can reference any field in the data source after a join. In Honeydew, relationships are resolved automatically. Reference any related entity directly:
sales.discount = product.unit_price - sales.net_price
No need to specify how to join - Honeydew resolves the path from sales to product through defined relationships.

Groups and Sets

Tableau Groups let you manually bucket dimension values. Tableau Sets create binary in/out classifications based on conditions. Both map to calculated attributes in Honeydew: Tableau Group:
product.size_bucket =
  CASE
    WHEN product.weight < 1 THEN 'Small'
    WHEN product.weight < 10 THEN 'Medium'
    ELSE 'Large'
  END
Tableau Set (high-value customers above a threshold):
customer.is_high_value = sales.total_sales > 10000
The is_high_value attribute uses a metric (sales.total_sales) in its definition. Calculated attributes that reference metrics are evaluated at the entity’s grain - here, per customer.

Calculated Fields → Metrics

A Tableau calculated field that produces a measure (an aggregation) maps to a metric in Honeydew.

Basic aggregations

Tableau:
SUM([Sales Amount])
Honeydew:
sales.total_sales = SUM(sales.sales_amount)
Tableau:
COUNTD([Customer ID])
Honeydew:
sales.unique_customers = customer.count
customer.count is the entity count metric of the customer entity. Honeydew resolves the relationship between sales and customer automatically, giving a distinct count of customers per the query’s grouping.COUNT(DISTINCT sales.customer_id) would also work, but referencing the entity count is the recommended approach.

Common aggregation mapping

The SQL syntax depends on the connected data warehouse (Snowflake, Databricks, etc.). The examples below use Snowflake syntax.
TableauSQL
SUM(expr)SUM(expr)
AVG(expr)AVG(expr)
COUNT(expr)COUNT(expr)
COUNTD(expr)COUNT(DISTINCT expr)
MIN(expr)MIN(expr)
MAX(expr)MAX(expr)
MEDIAN(expr)MEDIAN(expr)
PERCENTILE(expr, n)PERCENTILE_CONT(n) WITHIN GROUP (ORDER BY expr)
STDEV(expr)STDDEV_SAMP(expr)
VAR(expr)VAR_SAMP(expr)
ZN(expr)COALESCE(expr, 0)
IIF(test, then, else)CASE WHEN test THEN then_val ELSE else_val END
IF THEN ELSEIF ENDCASE WHEN ... THEN ... WHEN ... THEN ... ELSE ... END

Derived metrics

Metrics in Honeydew can reference other metrics:
sales.total_sales = SUM(sales.sales_amount)
sales.total_cost = SUM(sales.quantity * product.unit_price)
sales.profit = sales.total_sales - sales.total_cost
sales.margin = DIV0(sales.profit, sales.total_sales)
Define each building block once and reuse it. The profit and margin metrics do not repeat the aggregation logic - they compose existing metrics.

Filters

Tableau has multiple layers of filters that execute in a specific order: extract filters, data source filters, context filters, dimension filters, measure filters. Honeydew domains have two types of filters:
  • Source filters apply directly on source tables before any calculation or join. Used for performance optimization (e.g., partition pruning) and data deduplication.
  • Semantic filters apply to every query in a domain, after joins are resolved. Used for governance and access control (e.g., tenant filtering). A semantic filter may introduce additional joins to enforce its condition.
In addition, user query filters are applied through SQL:
  • WHERE filters on attributes (dimensions)
  • HAVING filters on metrics (measures)
See order of filtering for the full computation order.
Tableau filterHoneydew equivalentWhen it applies
Extract filterSource filterBefore joins and calculations
Data Source filterSource filterBefore joins, on source tables
Context filterSemantic filterAfter joins, before user query filters
Dimension filterWHERE in user queryAfter semantic filters, before metrics
Measure filterHAVING in user queryAfter metric aggregation
Semantic filters always apply to every query in a domain - they cannot be toggled by the user. For filters that should apply by default but can be overridden by user selections, see conditional filtering.

Adding filters to a metric

Tableau does not have a first-class “filtered measure” construct. To make a measure that always filters to a subset of rows - regardless of what the user selects in the view - you fold the filter into the aggregate using an IF expression. Non-matching rows return NULL and SUM ignores them. Tableau:
SUM(IF [Color] = "Red" THEN [Sales Amount] END)
Honeydew has filtered metrics as a first-class concept: a FILTER (WHERE ...) qualifier attached to a metric expression. The filter lives next to the metric, not inside it.
sales.red_sales = SUM(sales.sales_amount)
  FILTER (WHERE product.color = 'Red')
The qualifier also works on derived metrics that wrap an existing metric:
sales.red_sales = sales.total_sales
  FILTER (WHERE product.color = 'Red')
The FILTER (WHERE ...) qualifier adds a filter to the metric without affecting the rest of the query. All user filters still apply on top.

LOD Expressions

LOD (Level of Detail) expressions are one of the most common Tableau constructs that need to be rethought in Honeydew. In Tableau, LODs let you control the granularity of a calculation independently from the level of detail in the view. In Honeydew, there are two ways to control granularity, and the choice depends on how the calculation should interact with user filters: This distinction is important when translating LOD expressions.

FIXED

A FIXED LOD computes an aggregation at a specific grain. In Tableau, FIXED ignores dimension filters but respects context filters and data source filters.
For the full reference of how each Tableau filter type interacts with LOD expressions, see Tableau’s Filters and Level of Detail Expressions.
Tableau:
{ FIXED [Customer ID] : SUM([Sales Amount]) }
Honeydew - as a calculated attribute (ignores user filters):
customer.lifetime_sales = SUM(sales.sales_amount)
A calculated attribute that uses an aggregation automatically groups to the entity’s primary key. Since calculated attributes are computed before user filters, this closely matches the Tableau FIXED behavior of ignoring dimension filters. Honeydew - as a metric with fixed grouping (respects user filters):
sales.sales_per_customer = SUM(sales.sales_amount)
  GROUP BY (customer.customer_id)
The calculated attribute and the metric with GROUP BY both fix the grain to customer, but they differ in how they handle user filters:
  • customer.lifetime_sales (attribute) always returns the same value regardless of user filters.
  • sales.sales_per_customer (metric) respects user WHERE filters - if a user filters to 2024 data, the metric returns sales per customer in 2024 only.
Choose based on whether the calculation should be affected by user filters or not.

FIXED with multiple dimensions

Tableau:
{ FIXED [Region], [Category] : SUM([Sales]) }
Honeydew:
sales.regional_category_sales = SUM(sales.sales_amount)
  GROUP BY (customer.region, product.category)

INCLUDE

An INCLUDE LOD adds dimensions to the view’s current grain. Tableau:
{ INCLUDE [Customer ID] : COUNTD([Order ID]) }
Honeydew:
sales.orders_per_customer =
  COUNT(DISTINCT sales.order_id)
  GROUP BY (*, sales.customer_id)
GROUP BY (*, field) means “the user’s current grouping plus this additional field.” This is the Honeydew equivalent of Tableau’s INCLUDE.
This is the foundation of multi-level aggregations. Wrap sales.orders_per_customer in an outer aggregate to get an “average orders per customer” metric:
sales.avg_orders_per_customer =
  AVG(sales.orders_per_customer)  -- the metric defined above
The inner metric fixes the per-customer grain; the outer AVG averages across whatever the user breaks the result down by - region, segment, month, or all of them at once.

EXCLUDE

An EXCLUDE LOD removes dimensions from the view’s current grain. Tableau:
{ EXCLUDE [Month] : SUM([Sales]) }
This removes the month dimension from the aggregation, giving a coarser total while the view still shows monthly detail. Honeydew:
sales.sales_excl_month = SUM(sales.sales_amount)
  GROUP BY (NOT date.month)
GROUP BY (NOT field) means “the user’s current grouping minus this field.” This is the Honeydew equivalent of Tableau’s EXCLUDE.

Percent of total

A very common Tableau pattern is percent of total using LODs: Tableau:
SUM([Sales]) / { FIXED : SUM([Sales]) }
Or using a table calculation:
SUM([Sales]) / TOTAL(SUM([Sales]))
Honeydew:
sales.pct_of_total = DIV0(
  sales.total_sales,
  sales.total_sales GROUP BY ()
)
GROUP BY () removes all grouping, giving the grand total. The numerator uses the query’s grouping.

Percent of parent (nested LOD pattern)

Tableau:
SUM([Sales])
/ { FIXED [Region] : SUM([Sales]) }
Honeydew:
sales.pct_of_region = DIV0(
  sales.total_sales,
  sales.total_sales GROUP BY (customer.region)
)

LOD summary

Tableau LODHoneydew qualifierEffect
{ FIXED [A], [B] : AGG(expr) }GROUP BY (a, b)Sets an exact grain
{ FIXED : AGG(expr) }GROUP BY ()Grand total - no grouping
{ INCLUDE [A] : AGG(expr) }GROUP BY (*, a)Adds a dimension to the user grain
{ EXCLUDE [A] : AGG(expr) }GROUP BY (NOT a)Removes a dimension from the user grain

Table Calculations

Tableau table calculations operate on the result set after aggregation - running totals, ranks, moving averages, percent differences. They depend on the sort order and partitioning of the view. Honeydew handles these patterns differently depending on the calculation type.

Running total / cumulative sum

Tableau:
RUNNING_SUM(SUM([Sales]))
Honeydew - as a calculated attribute with a window function:
sales.cumulative_sales =
  SUM(sales.sales_amount) OVER (
    ORDER BY sales.order_date
    ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW
  )
Window functions in calculated attributes operate at the entity row level, not at the query result level. For cumulative sums over a query result, use Tableau’s built-in table calculation on a Honeydew metric.

Rank

Tableau:
RANK(SUM([Sales]))
Ranking over a query result is best done in Tableau on top of a Honeydew metric. Honeydew provides the aggregated values; Tableau applies the rank. For ranking at the entity level (e.g., rank orders by price within a customer), use a window function in a calculated attribute:
orders.price_rank = RANK() OVER (
  PARTITION BY orders.customer_id ORDER BY orders.total_price DESC
)

Percent difference

Tableau:
(ZN(SUM([Sales])) - LOOKUP(ZN(SUM([Sales])), -1))
/ ABS(LOOKUP(ZN(SUM([Sales])), -1))
For period-over-period comparisons, Honeydew offers time-based metrics that compute offsets like previous period values without relying on result-set ordering.
Table calculations that depend on the visual layout (sort order, partition) are best left in Tableau. Honeydew provides the well-defined aggregated metrics; Tableau applies layout-dependent calculations on top.

Parameters

Tableau parameters let users select a value at runtime to control filters, reference lines, or calculation logic. Honeydew supports two approaches:

BI parameters

Honeydew can pass through Tableau parameter values using BI parameters. This lets you build metrics whose behavior changes based on user selection.

Conditional filtering

For parameters that switch between dimensions or control which filter to apply, conditional filtering in Honeydew provides GET_FIELD_SELECTION():
-- Returns the value the user filtered on,
-- or a default if no filter is applied
GET_FIELD_SELECTION(product.category, 'All')
This is useful for building dynamic titles, conditional logic, or metrics that behave differently based on user filter selections.

Other Patterns

Threshold filtering

Tableau lets you create filters based on measure values (e.g., only show products with more than $1,000 in sales). In Tableau, the threshold is often controlled by a parameter. In Honeydew, use a metric in a filter with conditional filtering to make the threshold dynamic:
sales.sales_from_active_products =
  SUM(sales.sales_amount)
  FILTER (WHERE
    sales.total_sales
    GROUP BY (product.product_id)
      > GET_FIELD_SELECTION(dim_threshold.min_sales, 1000)
  )
The threshold defaults to 1,000 but the user can change it by filtering dim_threshold.min_sales to a different value - similar to how a Tableau parameter works.

Top N filtering

Tableau lets you filter a dimension to show only the top N items by a measure (e.g., top 10 products by sales). Top N filtering depends on the query’s grouping and sort order, so it should be applied in Tableau using a Tableau parameter and a Top N filter on a Honeydew metric. Honeydew provides the aggregated metric values; Tableau handles the ranking and filtering.

Period-over-period comparison

Tableau:
SUM([Sales]) - LOOKUP(SUM([Sales]), -1)
Honeydew provides time-based metrics for period offsets. This is the year-over-year comparison:
sales.total_sales_prev_year = TIME_METRIC(
  sales.total_sales,
  date_field => sales.order_date,
  offset => '1 year'
)
sales.yoy_growth =
  sales.total_sales - sales.total_sales_prev_year
Use time-based metrics when the comparison must be defined centrally so every consumer sees the same year-over-year (or other-period) definition. For comparisons that only matter inside one workbook, it is often simpler to keep them in Tableau as table calculations or part of the visual presentation.

Next Steps