domo-dataset-query

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

@domoinc/query - Data Query Builder

@domoinc/query - 数据查询构建器

CRITICAL: Use the Query API (via @domoinc/query) for all dataset queries in Domo apps. This is essential because:
  1. Page Filter Integration - The Query API automatically respects page-level filters when your app is embedded in a Domo dashboard. This is key for apps that need to respond to dashboard filter changes.
  2. Performance - The Query API allows you to query only the data you need at the aggregation level required, rather than fetching entire datasets. This is critical for performance, especially with large datasets.
  3. Server-Side Processing - Aggregations and filtering happen on Domo's servers, reducing data transfer and client-side processing.
The Query library provides a chainable API for building complex data queries. It constructs URLs that work with
domo.get()
and the
/data/v1/
endpoint.
npm/yarn:
bash
yarn add @domoinc/query
typescript
import Query from '@domoinc/query';
CDN (Vanilla JavaScript):
html
<script src="https://cdn.jsdelivr.net/npm/@domoinc/query@3.0.0/dist/main.min.js"></script>
javascript
// Query is available globally after CDN script loads
const data = await new Query()
  .select(['region', 'sales'])
  .fetch('sales-dataset');
重要提示: 在Domo应用中,所有数据集查询都要使用Query API(通过@domoinc/query)。这一点至关重要,原因如下:
  1. 页面筛选集成 - 当你的应用嵌入到Domo仪表盘中时,Query API会自动遵循页面级筛选规则。这对于需要响应仪表板筛选变化的应用来说是关键特性。
  2. 性能优化 - Query API允许你仅查询所需数据,并按所需的聚合级别获取,而非获取整个数据集。这对性能提升至关重要,尤其是处理大型数据集时。
  3. 服务器端处理 - 聚合和筛选操作在Domo的服务器上完成,减少了数据传输量和客户端处理压力。
该Query库提供了一个可链式调用的API来构建复杂的数据查询。它会构造可与
domo.get()
/data/v1/
端点配合使用的URL。
npm/yarn安装:
bash
yarn add @domoinc/query
typescript
import Query from '@domoinc/query';
CDN引入(原生JavaScript):
html
<script src="https://cdn.jsdelivr.net/npm/@domoinc/query@3.0.0/dist/main.min.js"></script>
javascript
// 加载CDN脚本后,Query将作为全局对象可用
const data = await new Query()
  .select(['region', 'sales'])
  .fetch('sales-dataset');

Basic Usage

基础用法

typescript
// Simple query
const data = await new Query()
  .select(['region', 'sales', 'date'])
  .fetch('sales-dataset');

// With filtering
const filtered = await new Query()
  .select(['region', 'product', 'sales'])
  .where('sales').greaterThan(1000)
  .where('region').in(['North', 'South'])
  .fetch('sales-dataset');

// With grouping and aggregation
const summary = await new Query()
  .select(['region', 'sales', 'quantity'])
  .groupBy('region')
  .groupBy({ sales: 'sum', quantity: 'avg' })
  .orderBy('sales', 'descending')
  .limit(10)
  .fetch('sales-dataset');
typescript
// 简单查询
const data = await new Query()
  .select(['region', 'sales', 'date'])
  .fetch('sales-dataset');

// 带筛选条件的查询
const filtered = await new Query()
  .select(['region', 'product', 'sales'])
  .where('sales').greaterThan(1000)
  .where('region').in(['North', 'South'])
  .fetch('sales-dataset');

// 带分组和聚合的查询
const summary = await new Query()
  .select(['region', 'sales', 'quantity'])
  .groupBy('region')
  .groupBy({ sales: 'sum', quantity: 'avg' })
  .orderBy('sales', 'descending')
  .limit(10)
  .fetch('sales-dataset');

Select

选择字段

typescript
// Select specific columns
new Query().select(['col1', 'col2', 'col3'])

// Select all (omit select)
new Query().fetch('dataset')
typescript
// 选择特定列
new Query().select(['col1', 'col2', 'col3'])

// 选择所有列(省略select方法)
new Query().fetch('dataset')

Where Filters

筛选条件(Where)

All filter methods return the Query for chaining.
typescript
// Comparison filters
.where('amount').lessThan(100)           // .lt(100)
.where('amount').lessThanOrEqual(100)    // .lte(100)
.where('amount').greaterThan(100)        // .gt(100)
.where('amount').greaterThanOrEqual(100) // .gte(100)
.where('amount').equals(100)
.where('amount').notEquals(100)
.where('amount').between(100, 500)

// String filters
.where('name').contains('test')
.where('name').notContains('test')

// List filters
.where('category').in(['A', 'B', 'C'])
.where('status').notIn(['deleted', 'archived'])

// Multiple conditions (AND)
.where('amount').greaterThan(100)
.where('status').equals('active')
.where('region').in(['North', 'South'])
所有筛选方法都会返回Query对象以支持链式调用。
typescript
// 比较筛选
.where('amount').lessThan(100)           // .lt(100)
.where('amount').lessThanOrEqual(100)    // .lte(100)
.where('amount').greaterThan(100)        // .gt(100)
.where('amount').greaterThanOrEqual(100) // .gte(100)
.where('amount').equals(100)
.where('amount').notEquals(100)
.where('amount').between(100, 500)

// 字符串筛选
.where('name').contains('test')
.where('name').notContains('test')

// 列表筛选
.where('category').in(['A', 'B', 'C'])
.where('status').notIn(['deleted', 'archived'])

// 多条件筛选(逻辑与)
.where('amount').greaterThan(100)
.where('status').equals('active')
.where('region').in(['North', 'South'])

Group By

分组(Group By)

typescript
// Group by single column
.groupBy('region')

// Group by multiple columns
.groupBy('region')
.groupBy('product')

// Group by column with aggregations (second parameter)
.groupBy('region', { 
  sales: 'sum',
  quantity: 'avg',
  price: 'max',
  orders: 'count',
  skus: 'unique'
})

// Multiple groupBy calls
.groupBy('region')
.groupBy('product', { sales: 'sum', orders: 'count' })
Aggregation Functions:
  • 'count'
    - Count rows
  • 'sum'
    - Sum values
  • 'avg'
    - Average values
  • 'min'
    - Minimum value
  • 'max'
    - Maximum value
  • 'unique'
    - Count distinct values
typescript
// 按单个列分组
.groupBy('region')

// 按多个列分组
.groupBy('region')
.groupBy('product')

// 按列分组并指定聚合函数(第二个参数)
.groupBy('region', { 
  sales: 'sum',
  quantity: 'avg',
  price: 'max',
  orders: 'count',
  skus: 'unique'
})

// 多次调用groupBy
.groupBy('region')
.groupBy('product', { sales: 'sum', orders: 'count' })
聚合函数:
  • 'count'
    - 统计行数
  • 'sum'
    - 求和
  • 'avg'
    - 求平均值
  • 'min'
    - 求最小值
  • 'max'
    - 求最大值
  • 'unique'
    - 统计不同值的数量

CRITICAL - Aggregation Key Syntax

重要提示 - 聚合键语法

Aggregation keys MUST be the actual field names from your dataset, NOT custom aliases.
The key in the aggregation object determines the output property name. If you use an alias that doesn't match a field, you'll get
[object Object]
errors.
typescript
// ✅ CORRECT - Keys match actual field names
// If your dataset has fields: 'Sales_Amount', 'Order_Qty'
.groupBy('region', { 
  Sales_Amount: 'sum',   // Key matches dataset field
  Order_Qty: 'count'     // Key matches dataset field
})

// Results: [{ region: 'North', Sales_Amount: 50000, Order_Qty: 150 }]

// ❌ WRONG - Custom aliases as keys cause [object Object] errors
.groupBy('region', { 
  totalSales: 'sum',     // 'totalSales' is not a field name!
  orderCount: 'count'    // 'orderCount' is not a field name!
})

// ✅ If you need custom names, rename AFTER fetching:
const data = await new Query()
  .groupBy('region', { Sales_Amount: 'sum' })
  .fetch('sales');

const renamed = data.map(row => ({
  region: row.region,
  totalSales: row.Sales_Amount  // Rename here
}));
聚合键必须是数据集中实际存在的字段名,而非自定义别名。
聚合对象中的键决定了输出结果的属性名。如果使用与字段不匹配的别名,会导致
[object Object]
错误。
typescript
// ✅ 正确写法 - 键与实际字段名匹配
// 若数据集包含字段:'Sales_Amount', 'Order_Qty'
.groupBy('region', { 
  Sales_Amount: 'sum',   // 键与数据集字段匹配
  Order_Qty: 'count'     // 键与数据集字段匹配
})

// 结果:[{ region: 'North', Sales_Amount: 50000, Order_Qty: 150 }]

// ❌ 错误写法 - 自定义别名作为键会导致[object Object]错误
.groupBy('region', { 
  totalSales: 'sum',     // 'totalSales'不是字段名!
  orderCount: 'count'    // 'orderCount'不是字段名!
})

// ✅ 若需要自定义名称,请在获取数据后重命名:
const data = await new Query()
  .groupBy('region', { Sales_Amount: 'sum' })
  .fetch('sales');

const renamed = data.map(row => ({
  region: row.region,
  totalSales: row.Sales_Amount  // 在这里重命名
}));

Order By

排序(Order By)

typescript
.orderBy('sales', 'descending')  // or 'desc'
.orderBy('date', 'ascending')    // or 'asc'

// Multiple sort columns
.orderBy('region', 'ascending')
.orderBy('sales', 'descending')
typescript
.orderBy('sales', 'descending')  // 或 'desc'
.orderBy('date', 'ascending')    // 或 'asc'

// 多列排序
.orderBy('region', 'ascending')
.orderBy('sales', 'descending')

Limit and Offset

限制行数与偏移(Limit and Offset)

typescript
.limit(100)    // Max rows
.offset(50)     // Skip rows (for pagination)

// Pagination example
const page = 2;
const pageSize = 25;
new Query()
  .limit(pageSize)
  .offset((page - 1) * pageSize)
  .fetch('dataset');
typescript
.limit(100)    // 最大行数
.offset(50)     // 跳过行数(用于分页)

// 分页示例
const page = 2;
const pageSize = 25;
new Query()
  .limit(pageSize)
  .offset((page - 1) * pageSize)
  .fetch('dataset');

Date Operations

日期操作

dateGrain - Group by Date Period

dateGrain - 按日期周期分组

typescript
// Group by month
.dateGrain('order_date', 'month')

// Group by month with aggregations
// NOTE: Aggregation keys must be actual field names!
.dateGrain('order_date', 'month', { Revenue: 'sum', Order_Count: 'count' })

// Available grains: 'day', 'week', 'month', 'quarter', 'year'
.dateGrain('date', 'day')
.dateGrain('date', 'week')
.dateGrain('date', 'month')
.dateGrain('date', 'quarter')
.dateGrain('date', 'year')
CRITICAL: The third parameter (aggregations) follows the same rules as
groupBy
- keys must match actual dataset field names:
typescript
// ✅ CORRECT - field names as keys
.dateGrain('order_date', 'month', { Sales_Amount: 'sum' })

// ❌ WRONG - custom aliases cause errors
.dateGrain('order_date', 'month', { totalSales: 'sum' })
typescript
// 按月分组
.dateGrain('order_date', 'month')

// 按月分组并指定聚合
// 注意:聚合键必须是实际字段名!
.dateGrain('order_date', 'month', { Revenue: 'sum', Order_Count: 'count' })

// 可用的周期粒度:'day', 'week', 'month', 'quarter', 'year'
.dateGrain('date', 'day')
.dateGrain('date', 'week')
.dateGrain('date', 'month')
.dateGrain('date', 'quarter')
.dateGrain('date', 'year')
重要提示: 第三个参数(聚合)遵循与
groupBy
相同的规则——键必须与数据集中的实际字段名匹配:
typescript
// ✅ 正确写法 - 键为字段名
.dateGrain('order_date', 'month', { Sales_Amount: 'sum' })

// ❌ 错误写法 - 自定义别名会导致错误
.dateGrain('order_date', 'month', { totalSales: 'sum' })

periodToDate - YTD, MTD, QTD, etc.

periodToDate - 年初至今、月初至今、季初至今等

typescript
// Year to date
.periodToDate('date', 'year')

// Month to date
.periodToDate('date', 'month')

// Quarter to date
.periodToDate('date', 'quarter')

// Full example
const ytdSales = await new Query()
  .select(['date', 'sales'])
  .periodToDate('date', 'year')
  .groupBy({ sales: 'sum' })
  .fetch('sales');
typescript
// 年初至今
.periodToDate('date', 'year')

// 月初至今
.periodToDate('date', 'month')

// 季初至今
.periodToDate('date', 'quarter')

// 完整示例
const ytdSales = await new Query()
  .select(['date', 'sales'])
  .periodToDate('date', 'year')
  .groupBy({ sales: 'sum' })
  .fetch('sales');

previousPeriod - Last Period Comparison

previousPeriod - 上期对比

typescript
// Last year
.previousPeriod('date', 'year')

// Last month
.previousPeriod('date', 'month')

// Last quarter
.previousPeriod('date', 'quarter')
typescript
// 去年同期
.previousPeriod('date', 'year')

// 上月同期
.previousPeriod('date', 'month')

// 上季同期
.previousPeriod('date', 'quarter')

rollingPeriod - Rolling Windows

rollingPeriod - 滚动窗口

typescript
// Last 30 days
.rollingPeriod('date', 'days', 30)

// Last 12 weeks
.rollingPeriod('date', 'weeks', 12)

// Last 6 months
.rollingPeriod('date', 'months', 6)

// Last 4 quarters
.rollingPeriod('date', 'quarters', 4)

// Last 3 years
.rollingPeriod('date', 'years', 3)
typescript
// 过去30天
.rollingPeriod('date', 'days', 30)

// 过去12周
.rollingPeriod('date', 'weeks', 12)

// 过去6个月
.rollingPeriod('date', 'months', 6)

// 过去4个季度
.rollingPeriod('date', 'quarters', 4)

// 过去3年
.rollingPeriod('date', 'years', 3)

Additional Options

额外选项

typescript
// Use fiscal calendar
.useFiscalCalendar(true)

// Enable beast modes (calculated fields)
.useBeastModes()

// ⚠️ WARNING: .aggregate() does NOT work in practice
// It causes error: "DA0057: An alias list was provided but it could not be parsed"
// Use .groupBy() with a grouping column instead, or .select() + client-side aggregation
// ❌ .aggregate({ total: 'sum', average: 'avg' })  // DOES NOT WORK
typescript
// 使用财日历
.useFiscalCalendar(true)

// 启用Beast Modes(计算字段)
.useBeastModes()

// ⚠️ 警告:.aggregate() 实际无法正常工作
// 会导致错误:"DA0057: An alias list was provided but it could not be parsed"
// 请改用带分组列的.groupBy(),或先.select()再在客户端进行聚合
// ❌ .aggregate({ total: 'sum', average: 'avg' })  // 无法正常工作

Complete Examples

完整示例

typescript
// Sales dashboard query
const salesByRegion = await new Query()
  .select(['region', 'product_category', 'sales', 'quantity', 'date'])
  .where('sales').greaterThan(0)
  .where('date').greaterThanOrEqual('2024-01-01')
  .dateGrain('date', 'month')
  .groupBy('region')
  .groupBy('product_category')
  .groupBy({ sales: 'sum', quantity: 'sum', orders: 'count' })
  .orderBy('sales', 'descending')
  .limit(100)
  .fetch('sales-dataset');

// YoY comparison
const thisYear = await new Query()
  .select(['month', 'revenue'])
  .periodToDate('date', 'year')
  .dateGrain('date', 'month', { revenue: 'sum' })
  .fetch('revenue');

const lastYear = await new Query()
  .select(['month', 'revenue'])
  .previousPeriod('date', 'year')
  .dateGrain('date', 'month', { revenue: 'sum' })
  .fetch('revenue');

// Trend analysis - last 90 days
const trend = await new Query()
  .select(['date', 'sales', 'orders'])
  .rollingPeriod('date', 'days', 90)
  .dateGrain('date', 'day', { sales: 'sum', orders: 'count' })
  .orderBy('date', 'ascending')
  .fetch('sales');
typescript
// 销售仪表板查询
const salesByRegion = await new Query()
  .select(['region', 'product_category', 'sales', 'quantity', 'date'])
  .where('sales').greaterThan(0)
  .where('date').greaterThanOrEqual('2024-01-01')
  .dateGrain('date', 'month')
  .groupBy('region')
  .groupBy('product_category')
  .groupBy({ sales: 'sum', quantity: 'sum', orders: 'count' })
  .orderBy('sales', 'descending')
  .limit(100)
  .fetch('sales-dataset');

// 同比对比
const thisYear = await new Query()
  .select(['month', 'revenue'])
  .periodToDate('date', 'year')
  .dateGrain('date', 'month', { revenue: 'sum' })
  .fetch('revenue');

const lastYear = await new Query()
  .select(['month', 'revenue'])
  .previousPeriod('date', 'year')
  .dateGrain('date', 'month', { revenue: 'sum' })
  .fetch('revenue');

// 趋势分析 - 过去90天
const trend = await new Query()
  .select(['date', 'sales', 'orders'])
  .rollingPeriod('date', 'days', 90)
  .dateGrain('date', 'day', { sales: 'sum', orders: 'count' })
  .orderBy('date', 'ascending')
  .fetch('sales');