@xeplr/expression-handler 2.1.0 → 2.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/lib/compile.js +1 -1
- package/lib/functions.js +11 -6
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -171,7 +171,7 @@ const f = xf.compile('sumif(fytd(order_date), revenue)', {
|
|
|
171
171
|
| `fiscalYearStart` | `4` (April) | `fytd` and any financial-year function |
|
|
172
172
|
| `today` | now | `ytd`, `fytd` — pin it for tests and scheduled runs |
|
|
173
173
|
| `missing` | `'null'` | `'error'` makes a name the row lacks an error |
|
|
174
|
-
| `ignoreCase` | `false` | `true` makes `=`, `!=`, `in
|
|
174
|
+
| `ignoreCase` | `false` | `true` makes `=`, `!=`, `in`, the text operators (`contains`, `startsWith`, `endsWith`, `notIn`, …) and the functions `contains()`, `startsWith()`, `endsWith()` ignore case. Numbers and null compare exactly as before. BI's report formulas set it, so a formula and the SQL it is translated into agree whatever the database's collation; Workflow's conditions leave it off |
|
|
175
175
|
| `maxListItems` · `maxDistinct` · `maxSteps` · `maxLevels` | see [Limits](#limits) | |
|
|
176
176
|
| `aggregate(name, args, { compile, level })` | none | supplies the rows for the group form of a total (BI) |
|
|
177
177
|
|
package/lib/compile.js
CHANGED
|
@@ -50,7 +50,7 @@ function compileTree(tree, options) {
|
|
|
50
50
|
var fy = Number(opts.fiscalYearStart);
|
|
51
51
|
if (!(fy >= 1 && fy <= 12 && Math.floor(fy) === fy)) fail('fiscalYearStart must be a month, 1–12');
|
|
52
52
|
|
|
53
|
-
var ctx = { now: dates.todayFor(opts), fiscalYearStart: fy };
|
|
53
|
+
var ctx = { now: dates.todayFor(opts), fiscalYearStart: fy, ignoreCase: Boolean(opts.ignoreCase) };
|
|
54
54
|
// Equality for `=`, `in` and the word operators: exact, or folded when the
|
|
55
55
|
// caller asked (opts.ignoreCase) — see operators.IGNORE_CASE.
|
|
56
56
|
var eq = opts.ignoreCase ? operators.looseEqIgnoreCase : operators.looseEq;
|
package/lib/functions.js
CHANGED
|
@@ -28,6 +28,9 @@ var asDate = dates.asDate;
|
|
|
28
28
|
|
|
29
29
|
var CATEGORIES = ['Math', 'Text', 'Date', 'Logic', 'Aggregate'];
|
|
30
30
|
|
|
31
|
+
// Text for a comparison: lower-cased when the formula ignores case.
|
|
32
|
+
function fold(c, v) { var s = str(v); return c && c.ignoreCase ? s.toLowerCase() : s; }
|
|
33
|
+
|
|
31
34
|
function str(v) { return v === null || v === undefined ? '' : String(v); }
|
|
32
35
|
function num(v) { var n = Number(v); return isNaN(n) ? null : n; }
|
|
33
36
|
function isEmpty(v) { return v === null || v === undefined || v === ''; }
|
|
@@ -121,15 +124,17 @@ var LIST = [
|
|
|
121
124
|
{ name: 'replace', category: 'Text', args: 'text, find, replace with', min: 3, max: 3,
|
|
122
125
|
description: 'Swap one piece of text for another', example: 'replace(phone, "-", "")',
|
|
123
126
|
fn: function (v, find, into) { return str(v).split(str(find)).join(str(into)); } },
|
|
124
|
-
|
|
127
|
+
// context: the compile option ignoreCase folds both sides, as it does for
|
|
128
|
+
// = and in — one rule for every text comparison in a formula.
|
|
129
|
+
{ name: 'contains', category: 'Text', args: 'text, find', min: 2, max: 2, context: true,
|
|
125
130
|
description: 'True when the text contains it', example: 'contains(notes, "urgent")',
|
|
126
|
-
fn: function (v, sub) { return
|
|
127
|
-
{ name: 'startsWith', category: 'Text', args: 'text, find', min: 2, max: 2,
|
|
131
|
+
fn: function (c, v, sub) { return fold(c, v).indexOf(fold(c, sub)) !== -1; } },
|
|
132
|
+
{ name: 'startsWith', category: 'Text', args: 'text, find', min: 2, max: 2, context: true,
|
|
128
133
|
description: 'True when the text begins with it', example: 'startsWith(sku, "IN")',
|
|
129
|
-
fn: function (v, sub) { return
|
|
130
|
-
{ name: 'endsWith', category: 'Text', args: 'text, find', min: 2, max: 2,
|
|
134
|
+
fn: function (c, v, sub) { return fold(c, v).startsWith(fold(c, sub)); } },
|
|
135
|
+
{ name: 'endsWith', category: 'Text', args: 'text, find', min: 2, max: 2, context: true,
|
|
131
136
|
description: 'True when the text ends with it', example: 'endsWith(file, ".pdf")',
|
|
132
|
-
fn: function (v, sub) { return
|
|
137
|
+
fn: function (c, v, sub) { return fold(c, v).endsWith(fold(c, sub)); } },
|
|
133
138
|
{ name: 'length', category: 'Text', args: 'text', min: 1, max: 1,
|
|
134
139
|
description: 'How many characters', example: 'length(name)',
|
|
135
140
|
fn: function (v) { return Array.isArray(v) ? v.length : str(v).length; } },
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@xeplr/expression-handler",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.2.0",
|
|
4
4
|
"description": "The one formula engine for xeplr: its own parser (no eval, no dependencies), one function list, compiled-once formulas, totals over lists, AND/OR conditions. Runs in Node and the browser.",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"files": [
|