Evaluating Formulas
The evaluator.evaluate() method lets you compute cell values on the server without needing a full export. This is useful for retrieving specific cell values, performing ad-hoc calculations, and building dynamic reports.
Basic Usage
import * as farseer from 'farseer-client';
const client = new farseer.FarseerClient();
const cells = await client.evaluator.evaluate({
filters: [],
cellQueries: [
{ type: 'VARIABLE', formula: 'sum("Revenue","Plan","Y2024")' },
],
});
console.log(cells[0].value); // e.g. 1250000
Each cell query returns an EvaluateCell with the computed value, plus metadata like readonly, calculated, and frozen.
CellQuery Types
Cell queries define what to compute. There are two types:
VARIABLE
Evaluates a formula expression against variables:
const query: farseer.CellQuery = {
type: 'VARIABLE',
formula: 'sum("Revenue","Plan","Y2024")',
};
DTABLE
Queries dimension table data directly:
const query: farseer.CellQuery = {
type: 'DTABLE',
tableGid: 42, // Dimension table global ID
columnIds: [1, 2, 3], // Column IDs to retrieve
formula: 'filter-expr',
};
Formula Syntax
Formulas follow Farseer's expression language. You can reference tags by name or by ID:
// By name (human-readable)
{ type: 'VARIABLE', formula: 'sum("Revenue","Plan","Y2024")' }
// By ID (use $ prefix)
{ type: 'VARIABLE', formula: 'sum($42,$27,$100)' }
// Mixed
{ type: 'VARIABLE', formula: 'sum("Revenue",$27,$100)' }
Using Filters for Variable Substitution
Filters let you define aliases that get substituted into formulas. This avoids repeating the same tag references across multiple queries:
const revenue = await client.getVariable('Revenue');
const plan = await client.getDimensionMember('Versions', 'Plan');
const y2024 = await client.getDimensionMember('Years', 'Y2024');
const cells = await client.evaluator.evaluate({
filters: [
['RV', `$${revenue.id}`], // RV → $42
['P', `$${plan.id}`], // P → $27
['Y', `$${y2024.id}`], // Y → $100
],
cellQueries: [
// "RV", "P", "Y" get replaced by filter values before evaluation
{ type: 'VARIABLE', formula: 'sum(RV,P,Y)' },
],
});
This is equivalent to:
{ type: 'VARIABLE', formula: `sum($${revenue.id},$${plan.id},$${y2024.id})` }
Evaluating Multiple Cells at Once
You can pass multiple queries in a single request. Each query produces one EvaluateCell in the response array, in the same order:
const allMonths = await client.getDimensionMembersForTable('Months');
const monthQueries: farseer.CellQuery[] = allMonths.map(month => ({
type: 'VARIABLE',
formula: `sum(RV,P,Y,$${month.id})`,
}));
const cells = await client.evaluator.evaluate({
filters: [
['RV', `$${revenue.id}`],
['P', `$${plan.id}`],
['Y', `$${y2024.id}`],
],
cellQueries: [
{ type: 'VARIABLE', formula: 'sum(RV,P,Y)' }, // Total
...monthQueries, // Per-month breakdown
],
});
const total = cells[0].value;
const monthValues = cells.slice(1).map((c, i) => ({
month: allMonths[i].name,
value: c.value,
}));
EvaluateRequest
interface EvaluateRequest {
filters: Array<[string, string]>; // Alias-to-value pairs
cellQueries: Array<CellQuery>; // Queries to evaluate
}
| Field | Type | Description |
|---|---|---|
filters | string[][] | Array of [alias, value] pairs for formula substitution |
cellQueries | CellQuery[] | Array of cell queries to evaluate |
EvaluateCell (Response)
Each query returns one EvaluateCell:
interface EvaluateCell {
value: any | null; // Computed value (number, string, etc.)
readonly: boolean; // Whether the cell is read-only
calculated: boolean; // Whether it's a calculated field
frozen: boolean; // Whether the cell is frozen
goal?: number; // Goal/target value if set
}
Calculate On-Demand Cells
If your model has on-demand calculated fields, trigger calculation before evaluating or exporting:
// Simple
await client.cells.calculateOnDemandCells();
// With timeout (recommended for large models)
const calc = client.cells.calculateOnDemandCells();
const timeout = new Promise(resolve =>
setTimeout(() => resolve('timeout'), 10 * 60 * 1000)
);
const result = await Promise.race([calc, timeout]);
if (result === 'timeout') {
console.log('Calculation timed out, continuing...');
}
Call calculateOnDemandCells() before evaluator.evaluate() or export.exportFormula() to ensure all calculated fields are up to date.
Complete Example
A full workflow that evaluates Revenue for Plan across all months:
import * as farseer from 'farseer-client';
async function main() {
const client = new farseer.FarseerClient();
// Get tag references
const revenue = await client.getVariable('Revenue');
const plan = await client.getDimensionMember('Versions', 'Plan');
const y2024 = await client.getDimensionMember('Years', 'Y2024');
const allMonths = await client.getDimensionMembersForTable('Months');
// Build queries for each month
const monthQueries: farseer.CellQuery[] = allMonths.map(m => ({
type: 'VARIABLE',
formula: `sum(RV,P,Y,$${m.id})`,
}));
// Evaluate
const cells = await client.evaluator.evaluate({
filters: [
['RV', `$${revenue.id}`],
['P', `$${plan.id}`],
['Y', `$${y2024.id}`],
],
cellQueries: [
{ type: 'VARIABLE', formula: 'sum(RV,P,Y)' },
...monthQueries,
],
});
// Process results
console.log(`Total Revenue (Plan, 2024): ${cells[0].value}`);
cells.slice(1).forEach((cell, i) => {
console.log(` ${allMonths[i].name}: ${cell.value}`);
});
}
main().catch(farseer.handleUnknownError);