ThoughtSpot best practices
Modeling for Search & Spotter
Every setting you configure in your data model shapes what users can find — and what Spotter's AI can reason about. This reference covers every option, why it matters, and what happens when you get it wrong.
Modeling for Search
Settings that control what users see in autocomplete, how their typed queries match columns, and how ThoughtSpot generates SQL from natural language.
Modeling for Spotter
Settings that shape the AI's schema comprehension — what it reads before generating queries, what business rules it follows, and how accurately it interprets intent.
Column names are the first thing ThoughtSpot uses to match a user's typed query to a column. If your backend column is QTY_RETAPP_P and a user types "quantity sold", Search finds nothing. The column name is the semantic bridge between business language and database schema.
Spotter reads all column names in the model before constructing any query — like reading a schema overview first. Well-named columns let Spotter match user intent to the right column even when the user's phrasing doesn't exactly match. Spotter 3 is fully data-aware and uses names as the primary signal for column selection.
✕ Poor column name
Column: QTY_RETAPP_P
User types "quantity sold" → No column matched. Search returns no results. Spotter generates a query against the wrong column or asks for clarification.
✓ Good column name
Column: Quantity Sold
User types "quantity sold" → Exact match. Spotter also matches "units", "how many sold", "qty" — all correctly resolved to this column.
💡Use names your business users would say aloud in a meeting. Test by asking a new team member what they'd type to find that column — if they guess wrong, rename it.
✨Spotter 3 improvement: well-named models produce 40–60% more accurate first-turn answers compared to models with raw database column names. Every column name is part of the AI's schema comprehension context.
Descriptions surface as a tooltip when users hover a column in Search. They disambiguate columns with similar names and help users understand what they're filtering on — especially important in models with many revenue or date columns that look alike.
Spotter reads descriptions as inline documentation before constructing any query. A good description can encode business rules, calculation notes, and disambiguation guidance that isn't expressible in a column name alone — directly improving query accuracy without any prompting from the user.
✕ No description
Column: Revenue (no description)
User asks "show revenue" — Spotter ambiguously picks GROSS_SALES. Finance team gets the wrong number and loses trust in the dashboard.
✓ Clear description
Description: "Net revenue after discounts and returns, before tax. Excludes intercompany. Use GROSS_SALES for pre-discount figures."
Spotter picks NET_SALES every time "revenue" is mentioned. Users hover the column and immediately understand what they're looking at.
💡Write descriptions as one-line data dictionary entries. If you wouldn't omit it from a data dictionary, don't omit it here.
Different teams use different words for the same thing — Finance says "Net Sales", Sales says "Revenue", the CEO says "Top Line." Synonyms let all three resolve to the correct column without renaming it. They also catch abbreviations and legacy backend names users may type from memory.
Always add these synonym types
Backend column name
Add NET_SALES as a synonym for "Revenue" — catches users who type raw DB names
Common abbreviations
Rev, Qty, Cust, Txn — users type shortcuts constantly and expect them to work
Business unit variants
Different teams may call the same metric different things — add all known aliases
Common misspellings
If your users commonly misspell a key column, add the misspelling as a synonym
✕ No synonyms
Column: Revenue (no synonyms)
User types "sales" → no match. Types "top line" → no match. Types "rev" → no match. User gives up and asks IT instead.
✓ Rich synonyms
Synonyms: Sales, Top Line, Net Sales, NET_SALES, Rev, Income
All six terms resolve correctly to Revenue. Users self-serve regardless of which term they use.
ThoughtSpot infers MEASURE for any numeric column. This is frequently wrong. ZIP codes, order IDs, phone numbers, and product codes are numeric but should never be summed or averaged. Getting Column Type wrong causes incorrect aggregations, bad chart defaults, and Spotter answers that are confidently wrong.
Decision guide
MEASURE
Values you sum, average, or aggregate: Revenue, Quantity, Duration, Headcount. Can have a default aggregation.
ATTRIBUTE
Values you group or filter by: Region, Customer ID, ZIP Code, Order ID, Status, Phone Number — even if numeric.
✕ Wrong type (MEASURE)
ZIP_CODE left as MEASURE
Spotter: "Sum of ZIP Code = 4,829,473,021" — completely meaningless. Chart default: bar chart of total ZIP value.
✓ Correct type (ATTRIBUTE)
ZIP_CODE set to ATTRIBUTE
Spotter groups by ZIP correctly. User can filter "sales in ZIP 90210" — works as expected. Geo map suggested automatically when geo config also set.
MEASURES are additive by default — meaning ThoughtSpot will SUM them when aggregating across rows. For ratio metrics, percentages, and averages this produces silent, confident wrong answers. A Margin % that sums to 101% across regions will be presented without any warning unless Additive = false is set.
⚠️Rule of thumb: if a column's values don't add up to a meaningful total (percentages, ratios, rates, averages), set Additive = false. Examples: Margin %, NPS Score, Average Order Value, Conversion Rate.
✕ Additive = true on %
Margin % — Additive = true (default)
West 32% + East 28% + North 41% = Total: 101% margin. Confidently presented. Finance raises an incident.
✓ Additive = false
Margin % — Additive = false
Spotter uses weighted average. Total shows 33.4% blended margin. Correct, auditable, consistent with finance reports.
When a user types "Revenue by Region" without specifying SUM or AVERAGE, ThoughtSpot uses the column's default aggregation. Setting this correctly means the most common query pattern returns the right answer with zero extra keywords — and tells Spotter which aggregation to assume when constructing queries autonomously.
Recommended defaults by metric type
SUM
Revenue, Quantity, Spend, Units — any value that should be totaled across rows
COUNT
Transactions, Orders, Events — counting distinct occurrences of something
AVERAGE
Rate metrics: Avg Order Value, NPS, Duration — summing these produces nonsense
MIN / MAX
Useful for date ranges, price bounds, or any column where extremes are more meaningful than totals
NONE
All ATTRIBUTEs — they are grouped and filtered, never aggregated
Indexing determines whether column values (not just the column name) appear in Search autocomplete as a user types. Without indexing, users must type exact values from memory — no suggestions, no dropdown, high chance of mismatches and zero results.
Spotter uses indexed values to resolve ambiguous user input to actual data values. If a State column is indexed, Spotter can match "Pacific Northwest" to the specific state codes. Without indexing, Spotter must guess — and may generate a WHERE clause with values that don't exist in your data.
Index type options
DEFAULT
Indexes all column values. Best for low-to-medium cardinality: Region, Product Category, Status, Account Name.
PREFIX_ONLY
Auto-applied to strings longer than 50 characters. Matches only from the start of values. Use only for structured short values.
DONT_INDEX
No value suggestions. Use for sensitive columns, very high-cardinality free text, or ABAC/RLS-restricted fields.
🔒Embedded security note: The suggestion index is built independently of ABAC/RLS runtime token filters. A user restricted by a token variable may still see unauthorized values in autocomplete. Use DONT_INDEX on sensitive columns (dealer_id, customer_segment) or apply an RLS rule at the model level so the index itself is filtered.
Some columns exist to support joins, formula dependencies, or RLS rules — but should never appear in Search. Hidden removes them from autocomplete and suggestions while keeping them fully active in joins and formulas behind the scenes.
Hidden columns are excluded from the schema context Spotter reads before generating queries. This reduces schema noise — a Spotter reasoning over a clean 30-column model performs significantly better than one parsing 80 columns where half are internal plumbing. Hide aggressively: expose only what users actually query.
Good candidates for hiding
Join keys
Foreign keys like PRODUCT_ID, CUSTOMER_FK — users should never search on raw key columns
Formula inputs
Raw columns that feed calculated fields — hide the input, expose only the formula result
RLS driver columns
Columns used in security rule formulas — must be hidden from direct user access
Technical flags
IS_DELETED, CREATED_BY_SYSTEM, ETL_BATCH_ID — internal housekeeping columns with no business value
⚠️Hidden is a UI control only — it does not restrict data access. For actual row-level security in embedded apps, use RLS rules or ABAC token variable values, not Hidden alone.
Controls whether values for a column populate the Search bar dropdown as users type. Only available at the Model level (not Table level). Disable for very high-cardinality columns where suggestions would be overwhelming and slow — transaction IDs, timestamps, free-text fields. Enable for dimensions users commonly filter on.
✕ Suggestions on high-cardinality
Transaction ID — suggest values: ON
User starts typing → dropdown shows millions of GUIDs. Slow, useless, and clutters the search experience. Users learn to avoid Search entirely.
✓ Suggestions on useful column
Order Status — suggest values: ON
User types "pend" → sees "Pending", "Pending Review" → correct filter applied in one click. Reduces mistyped values to near zero.
ThoughtSpot is case-insensitive by default, but some databases are not. Without this setting, ThoughtSpot wraps filter values in LOWER() or UPPER() — which can prevent index usage in Snowflake, Redshift, and BigQuery, causing full table scans on large fact tables. Setting the correct casing eliminates this overhead.
Options
ALL UPPER CASE
Data is stored uppercase. ThoughtSpot uppercases user input before comparison — no LOWER() needed in SQL.
ALL LOWER CASE
Data stored lowercase. ThoughtSpot lowercases user input. Clean SQL, no function wrapping.
MIXED
ThoughtSpot applies LOWER() to both sides. Correct but may impact query performance on large tables.
-- Without casing setting (MIXED): slow on large tables
WHERE LOWER("PRODUCTTYPE") = 'jackets'
-- With ALL LOWER CASE set: index-friendly
WHERE "PRODUCTTYPE" = 'jackets'
⚠️Audit your ETL pipeline before setting this. Inconsistent source data (some rows "Jackets", some "JACKETS") will cause missed filter matches — users get zero results for correct queries.
Relative date keywords are resolved against the calendar type assigned to the date column. If your fiscal year starts April 1 but you use Gregorian calendar, "Q1" returns January–March — wrong for every fiscal report. This is the single most common cause of "why is my quarter number off?" questions.
Calendar options
Gregorian (default)
Jan 1–Dec 31 year, weeks start Monday. Correct for calendar-year businesses.
Fiscal
Custom year start/end month. Critical if your Q1 ≠ January. Configure the start month per connection.
Custom
Arbitrary period boundaries: retail 4-4-5, broadcast calendar, or any non-standard period structure.
✕ Gregorian calendar
User query: "sales this quarter" (asked in May)
Returns April–June (Gregorian Q2). Business fiscal Q1 is also Apr–Jun but labeled Q1. Numbers don't match any finance report. Trust eroded.
✓ Fiscal calendar set
User query: "sales this quarter" (asked in May)
Returns fiscal Q1 (April–June). Matches every other business report. Relative keywords "last year", "YTD" all align correctly to fiscal boundaries.
⚠️Per-user timezone is separate — set via POST /users/{id}/update with extended_properties: {"preferredTimezone": "America/New_York"}. It cannot be passed via variable_values in the token API — that causes a type error.
Without Geo Config, ThoughtSpot treats a column named "State" as just another string attribute and defaults to a table or bar chart. With the correct Geo Config type set, Search and Spotter automatically generate geo maps when a location column is referenced — no extra keywords needed from the user.
Geo config types
Country / State / City
Named place columns — ThoughtSpot resolves the name to its boundary automatically
ZIP / Postal code
ZIP-level mapping for US and international postal codes
Latitude + Longitude
Exact coordinate pairs for precise point mapping — requires two separate columns both configured
Custom region
User-defined geographic groupings that don't map to standard boundaries
✕ No geo config
User: "revenue by state"
Returns a bar chart or table. Map never suggested. User has to manually change chart type — most won't bother.
✓ Geo config = State
User: "revenue by state"
Geo map automatically generated and recommended as the default visualization. Spotter also selects map as preferred chart type for location-based queries.
A chasm trap occurs when two fact tables (Sales and Budget) share a common dimension (Customer) but have no direct join between them. When a user asks "Sales and Budget by Customer", ThoughtSpot must determine which shared dimensions are valid join bridges. Attribution Dimension controls this — set incorrectly it silently inflates or distorts results through row multiplication.
✕ Date = Attribution YES
Date dimension set as Attribution = YES
ThoughtSpot bridges Sales and Budget via Date. Every Sales row multiplied by every Budget row that shares the same date. Revenue fans out to 10× actual value. Confidently wrong.
✓ Customer = Attribution YES, Date = NO
Customer = YES (causal), Date = NO (lookup)
Sales and Budget correctly joined via Customer. "Sales and Budget by Customer" returns correct figures. Date used as a filter only, not as a bridge.
⚠️Set Attribution on DIMENSION tables only, never on fact tables. Always test chasm trap queries after modeling — verify "Fact A + Fact B by [dimension]" produces expected row counts before publishing to users.
Model Instructions are the single highest-leverage Spotter modeling action. They encode business logic at the model level so every conversation inherits it automatically — no per-user setup, no repeated prompting needed. This is where you capture the "rules everyone knows but no one types" that determine whether Spotter's answers are business-correct or just technically valid SQL.
High-impact instruction examples
Metric definitions
"Revenue means net revenue (NET_SALES). Do not use GROSS_SALES unless explicitly asked."
Default filters
"Always exclude cancelled orders (STATUS = 'CANCELLED') unless the user explicitly asks to include them."
Date defaults
"When no time period is specified, default to the current fiscal year."
Column preference
"When asked about 'region', use SALES_REGION, not SHIPPING_REGION."
Aggregation rules
"Margin % should always be calculated as a weighted average, never summed."
Hierarchy guidance
"Product hierarchy: Category → Subcategory → Product. Use Category for high-level queries."
✕ No model instructions
User: "show me revenue last quarter"
Spotter picks GROSS_SALES (first revenue-like column). Includes cancelled orders. Uses calendar quarter, not fiscal. Finance rejects the number in the board deck.
✓ Model instructions set
User: "show me revenue last quarter"
Spotter uses NET_SALES, filters out cancelled orders, uses fiscal Q3. Answer matches finance report exactly. Zero follow-up questions needed.
✨Column names tell Spotter what exists. Descriptions explain individual columns. Model Instructions tell Spotter how to use them correctly — encoding the business rules that turn technically valid SQL into business-correct answers. Think of coaching feedback from users as signals for which instructions to add next.