diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index ee0357ea12..3d5cd9c87e 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -1342,10 +1342,10 @@ jobs: # Install artifact - name: Install wheel (system) - run: python -m pip install -U --no-dependencies *manylinux2014*.whl + run: python -m pip install --force-reinstall -U --no-dependencies *manylinux2014*.whl - name: Install wheel (local) - run: python -m pip install -U --no-dependencies *manylinux2014*.whl --target python/perspective + run: python -m pip install --force-reinstall -U --no-dependencies *manylinux2014*.whl --target python/perspective - name: Check Installed labextensions run: jupyter labextension list diff --git a/docs/build.js b/docs/build.js index 18a2d504ca..d97b006153 100644 --- a/docs/build.js +++ b/docs/build.js @@ -60,7 +60,7 @@ async function run_with_theme(page, is_dark = false) { { plugin: "Datagrid", group_by: [], - expressions: [], + expressions: {}, split_by: [], sort: [], aggregates: {}, diff --git a/docs/docs/expressions.md b/docs/docs/expressions.md index bd41330c9a..e2d7231b27 100644 --- a/docs/docs/expressions.md +++ b/docs/docs/expressions.md @@ -89,7 +89,7 @@ quotes_.
@@ -110,7 +110,7 @@ if ("Profit" > 0) {
@@ -153,7 +153,7 @@ half

@@ -171,7 +171,7 @@ percentDisplay

@@ -197,6 +197,6 @@ finalPrice + additionalModifier
diff --git a/docs/docs/js.md b/docs/docs/js.md index 8ed47bbf67..58603d46bc 100644 --- a/docs/docs/js.md +++ b/docs/docs/js.md @@ -696,11 +696,11 @@ await elem.restore({ settings: true }); // Create an expression await elem.restore({ columns: ['"Sales" + 100'], - expressions: ['"Sales" + 100'], + expressions: { "New Column": '"Sales" + 100' }, }); // ERROR if the column does not exist in the schema or expressions -// await elem.restore({columns: ["\"Sales\" + 100"], expressions: []}); +// await elem.restore({columns: ["\"Sales\" + 100"], expressions: {}}); // Add a filter await elem.restore({ filter: [["Sales", "<", 100]] }); diff --git a/docs/docs/view.md b/docs/docs/view.md index ca4efe6135..3c1d08dc2c 100644 --- a/docs/docs/view.md +++ b/docs/docs/view.md @@ -480,7 +480,7 @@ A custom name can be added to an expression by making the first line a comment: ```javascript const view = await table.view({ - expressions: ['"a" + "b"'], + expressions: { '"a" + "b"': '"a" + "b"' }, }); ``` @@ -503,7 +503,9 @@ view = table.view(expressions=['"a" + "b"']) const elem = document.querySelector("perspective-viewer"); await elem.restore({ columns: ["new expression"], - expressions: ['//new expression\n"Sales" + "Profit" * 50 / sqrt("Sales")'], + expressions: { + "new expression": '"Sales" + "Profit" * 50 / sqrt("Sales")', + }, }); ``` @@ -514,7 +516,7 @@ await elem.restore({ widget = PerspectiveWidget() widget.restore( columns=["new_expression"], - expressions=["//new expression\n\"Sales\" + \"Profit\" * 50 / sqrt(\"Sales\")"] + expressions={"new expression": "\"Sales\" + \"Profit\" * 50 / sqrt(\"Sales\")"} ) ``` @@ -522,7 +524,7 @@ widget.restore(
- +
diff --git a/docs/src/components/Demo/layouts.js b/docs/src/components/Demo/layouts.js index 81d720f72e..217ecb9f5e 100644 --- a/docs/src/components/Demo/layouts.js +++ b/docs/src/components/Demo/layouts.js @@ -29,10 +29,10 @@ export const LAYOUTS = { columns: ["chg (-)", "chg", "chg (+)"], filter: [], sort: [["chg", "desc"]], - expressions: [ - '//chg (-)\nif("chg"<0){"chg"}else{0}', - '//chg (+)\nif("chg">0){"chg"}else{0}', - ], + expressions: { + "chg (-)": 'if("chg"<0){"chg"}else{0}', + "chg (+)": 'if("chg">0){"chg"}else{0}', + }, aggregates: { "chg (+)": "avg", chg: "avg", "chg (-)": "avg" }, }, datagrid: { @@ -91,7 +91,10 @@ export const LAYOUTS = { title: "Spread Heatmap", columns: ["name"], plugin: "Heatmap", - expressions: [`bucket("bid",2)`, `bucket("ask",2)`], + expressions: { + 'bucket("bid",2)': 'bucket("bid",2)', + 'bucket("ask",2)': `bucket("ask",2)`, + }, group_by: [`bucket("bid",2)`], split_by: [`bucket("ask",2)`], sort: [], diff --git a/docs/src/components/ExampleGallery/features.js b/docs/src/components/ExampleGallery/features.js index 2f0f27d5a5..79419c093a 100644 --- a/docs/src/components/ExampleGallery/features.js +++ b/docs/src/components/ExampleGallery/features.js @@ -295,10 +295,10 @@ exports.default = [ }, }, }, - expressions: [ - `//Profit (-)\nif("Profit"<0){"Profit"}else{0}`, - `//Profit (+)\nif("Profit">0){"Profit"}else{0}`, - ], + expressions: { + [`Profit (-)`]: `if("Profit"<0){"Profit"}else{0}`, + [`Profit (+)`]: `if("Profit">0){"Profit"}else{0}`, + }, }, }, @@ -381,7 +381,7 @@ exports.default = [ group_by: ["State"], split_by: ["Profit (-/+)"], columns: ["Profit"], - expressions: [`//Profit (-/+)\nif("Profit"<0){1}else{0}`], + expressions: { "Profit (-/+)": `if("Profit"<0){1}else{0}` }, sort: [["Profit", "desc"]], }, viewport: { width: 600, height: 450 }, @@ -502,7 +502,9 @@ exports.default = [ config: { plugin: "Y Line", group_by: ["bucket(\"Order Date\", 'M')"], - expressions: ["bucket(\"Order Date\", 'M')"], + expressions: { + "bucket(\"Order Date\", 'M')": "bucket(\"Order Date\", 'M')", + }, columns: ["Sales"], }, }, @@ -514,10 +516,10 @@ exports.default = [ plugin: "Y Line", group_by: ["bucket(\"Order Date\", 'M')"], split_by: ["bucket(\"Order Date\", 'Y')"], - expressions: [ - "bucket(\"Order Date\", 'M')", - "bucket(\"Order Date\", 'Y')", - ], + expressions: { + "bucket(\"Order Date\", 'M')": "bucket(\"Order Date\", 'M')", + "bucket(\"Order Date\", 'Y')": "bucket(\"Order Date\", 'Y')", + }, columns: ["Sales"], }, }, @@ -529,7 +531,9 @@ exports.default = [ plugin: "Y Line", group_by: ["bucket(\"Order Date\", 'M')"], split_by: ["Region"], - expressions: ["bucket(\"Order Date\", 'M')"], + expressions: { + "bucket(\"Order Date\", 'M')": "bucket(\"Order Date\", 'M')", + }, columns: ["Sales"], }, }, @@ -605,7 +609,9 @@ exports.default = [ config: { plugin: "Y Area", group_by: ["bucket(\"Order Date\", 'M')"], - expressions: ["bucket(\"Order Date\", 'M')"], + expressions: { + "bucket(\"Order Date\", 'M')": "bucket(\"Order Date\", 'M')", + }, columns: ["Sales"], }, }, @@ -617,10 +623,10 @@ exports.default = [ plugin: "Y Area", group_by: ["bucket(\"Order Date\", 'M')"], split_by: ["bucket(\"Order Date\", 'Y')"], - expressions: [ - "bucket(\"Order Date\", 'M')", - "bucket(\"Order Date\", 'Y')", - ], + expressions: { + "bucket(\"Order Date\", 'M')": "bucket(\"Order Date\", 'M')", + "bucket(\"Order Date\", 'Y')": "bucket(\"Order Date\", 'Y')", + }, columns: ["Sales"], }, }, @@ -632,7 +638,9 @@ exports.default = [ plugin: "Y Area", group_by: ["bucket(\"Order Date\", 'M')"], split_by: ["Region"], - expressions: ["bucket(\"Order Date\", 'M')"], + expressions: { + "bucket(\"Order Date\", 'M')": "bucket(\"Order Date\", 'M')", + }, columns: ["Sales"], }, }, @@ -1052,7 +1060,9 @@ exports.default = [ config: { columns: ["Discount"], plugin: "Heatmap", - expressions: ["bucket(\"Order Date\", 'M')"], + expressions: { + "bucket(\"Order Date\", 'M')": "bucket(\"Order Date\", 'M')", + }, aggregates: { "Order Date": "dominant", Sales: "avg", @@ -1070,10 +1080,10 @@ exports.default = [ config: { plugin: "Heatmap", columns: ["Profit"], - expressions: [ - 'bucket("Profit", 100)', - "bucket(\"Order Date\", 'M')", - ], + expressions: { + 'bucket("Profit", 100)': 'bucket("Profit", 100)', + "bucket(\"Order Date\", 'M')": "bucket(\"Order Date\", 'M')", + }, group_by: ["bucket(\"Order Date\", 'M')"], split_by: ['bucket("Profit", 100)'], plugin_config: {}, diff --git a/docs/src/components/ExampleGallery/index.js b/docs/src/components/ExampleGallery/index.js index 49f9a0ce3e..4cfef2628c 100644 --- a/docs/src/components/ExampleGallery/index.js +++ b/docs/src/components/ExampleGallery/index.js @@ -91,7 +91,7 @@ function OverlayDemo(props) { convert({ plugin: "Datagrid", group_by: [], - expressions: [], + expressions: {}, split_by: [], sort: [], aggregates: {}, diff --git a/examples/blocks/src/citibike/layout.json b/examples/blocks/src/citibike/layout.json index c791341a92..1efb9884f5 100644 --- a/examples/blocks/src/citibike/layout.json +++ b/examples/blocks/src/citibike/layout.json @@ -34,7 +34,7 @@ "columns": ["lon", "lat", "num_bikes_available", null, null], "filter": [["lon", "!=", 0.0]], "sort": [["num_bikes_available", "asc"]], - "expressions": [], + "expressions": {}, "aggregates": {} }, "PERSPECTIVE_GENERATED_ID_1": { @@ -65,9 +65,9 @@ "columns": ["Available (%)", "name"], "filter": [], "sort": [["last_reported", "desc"]], - "expressions": [ - "// Available (%)\n\"num_bikes_available\" / \"capacity\"" - ], + "expressions": { + "Available (%)": "\"num_bikes_available\" / \"capacity\"" + }, "aggregates": {} } } diff --git a/examples/blocks/src/covid/index.html b/examples/blocks/src/covid/index.html index 2106915fea..028281a89f 100644 --- a/examples/blocks/src/covid/index.html +++ b/examples/blocks/src/covid/index.html @@ -20,12 +20,10 @@ @@ -22,11 +19,7 @@ - + @@ -42,15 +35,13 @@ elem.load(client.open_table("data_source_one")); elem.restore({ plugin: "Y Area", - group_by: [ - "var d := 1000 * float(\"Date\");\nbucket(datetime(d), 'M')", - ], + group_by: ["var d := 1000 * float(\"Date\");\nbucket(datetime(d), 'M')"], split_by: ["Name"], columns: ["Hash"], sort: [["Hash", "col desc"]], - expressions: [ - "var d := 1000 * float(\"Date\");\nbucket(datetime(d), 'M')", - ], + expressions: { + "var d := 1000 * float(\"Date\");\nbucket(datetime(d), 'M')": "var d := 1000 * float(\"Date\");\nbucket(datetime(d), 'M')", + }, }); }); diff --git a/packages/perspective-viewer-datagrid/src/js/style_handlers/column_header.js b/packages/perspective-viewer-datagrid/src/js/style_handlers/column_header.js index a1931225a7..0d61cb613f 100644 --- a/packages/perspective-viewer-datagrid/src/js/style_handlers/column_header.js +++ b/packages/perspective-viewer-datagrid/src/js/style_handlers/column_header.js @@ -39,8 +39,9 @@ export function style_selected_column(regularTable, selectedColumn) { const settings_open = regularTable.parentElement.parentElement.hasAttribute("settings"); - if (settings_open) { - // if settings_open, you will never have less than 2 trs but possibly more e.g. with group-by. + if (settings_open && len >= 2) { + // if settings_open, you will never have less than 2 trs unless the + // table is empty, but possibly more e.g. with group-by. // edit and title are guaranteed to be the last two rows let titles = Array.from(group_header_trs[len - 2].children); let editBtns = Array.from(group_header_trs[len - 1].children); diff --git a/packages/perspective-workspace/test/js/restore.spec.js b/packages/perspective-workspace/test/js/restore.spec.js index b1c9d84cb3..892311ace5 100644 --- a/packages/perspective-workspace/test/js/restore.spec.js +++ b/packages/perspective-workspace/test/js/restore.spec.js @@ -167,7 +167,7 @@ function tests(context, compare) { columns: ["Sales", "Profit"], filter: [], sort: [], - expressions: [], + expressions: {}, aggregates: {}, master: false, table: "superstore", @@ -210,7 +210,7 @@ function tests(context, compare) { ], filter: [], sort: [], - expressions: [], + expressions: {}, aggregates: {}, master: false, table: "superstore", diff --git a/packages/perspective-workspace/test/js/visibility.spec.js b/packages/perspective-workspace/test/js/visibility.spec.js index eb8afda435..57ddcca3ee 100644 --- a/packages/perspective-workspace/test/js/visibility.spec.js +++ b/packages/perspective-workspace/test/js/visibility.spec.js @@ -40,7 +40,7 @@ const BAD_LAYOUT = { columns: ["Quantity", null, null], filter: [], sort: [], - expressions: [], + expressions: {}, aggregates: {}, master: false, name: "one", @@ -57,7 +57,7 @@ const BAD_LAYOUT = { columns: ["Sales", null, null], filter: [], sort: [], - expressions: [], + expressions: {}, aggregates: {}, master: false, name: "two", diff --git a/packages/perspective/README.md b/packages/perspective/README.md index 6c9e3592ab..63d8f764bf 100644 --- a/packages/perspective/README.md +++ b/packages/perspective/README.md @@ -176,7 +176,7 @@ columns in the underlying [table](#module_perspective..table). ```js // Create a view with expressions const view = table.view({ - expressions: ['"x" + "y" - 100'] + expressions: {'"x" + "y" - 100': '"x" + "y" - 100'} }); await view.expression_schema(); // {'"x" + "y" - 100': "float"} diff --git a/packages/perspective/src/js/perspective.js b/packages/perspective/src/js/perspective.js index fa5f1708cb..6c70d5dc43 100644 --- a/packages/perspective/src/js/perspective.js +++ b/packages/perspective/src/js/perspective.js @@ -376,7 +376,7 @@ export default function (Module) { * @example * // Create a view with expressions * const view = table.view({ - * expressions: ['"x" + "y" - 100'] + * expressions: {'"x" + "y" - 100': '"x" + "y" - 100'} * }); * * await view.expression_schema(); // {'"x" + "y" - 100': "float"} @@ -1420,8 +1420,12 @@ export default function (Module) { function parse_expression_strings(expressions) { let validated_expressions = []; const expression_idx_map = {}; - const is_new_format = !Array.isArray(expressions); + if (!is_new_format) { + console.warn( + "Legacy `expressions` format: " + JSON.stringify(expressions) + ); + } for (let expression of is_new_format ? Object.keys(expressions) @@ -2208,14 +2212,12 @@ export default function (Module) { */ async init(msg) { let wasmBinary = msg.buffer; - try { - const mod = await WebAssembly.instantiate(wasmBinary); - const exports = mod.instance.exports; - const size = exports.size(); - const offset = exports.offset(); - const array = new Uint8Array(exports.memory.buffer); - wasmBinary = array.slice(offset, offset + size); - } catch {} + const mod = await WebAssembly.instantiate(wasmBinary); + const exports = mod.instance.exports; + const size = exports.size(); + const offset = exports.offset(); + const array = new Uint8Array(exports.memory.buffer); + wasmBinary = array.slice(offset, offset + size); __MODULE__ = await __MODULE__({ wasmBinary, wasmJSMethod: "native-wasm", diff --git a/packages/perspective/src/js/perspective.node.js b/packages/perspective/src/js/perspective.node.js index d8aa3e484b..cfaa16ddd4 100644 --- a/packages/perspective/src/js/perspective.node.js +++ b/packages/perspective/src/js/perspective.node.js @@ -34,14 +34,12 @@ const SYNC_SERVER = new (class extends Server { init(msg) { this._loaded_promise = (async () => { let wasmBinary = await buffer; - try { - const mod = await WebAssembly.instantiate(wasmBinary); - const exports = mod.instance.exports; - const size = exports.size(); - const offset = exports.offset(); - const array = new Uint8Array(exports.memory.buffer); - wasmBinary = array.slice(offset, offset + size); - } catch {} + const mod = await WebAssembly.instantiate(wasmBinary); + const exports = mod.instance.exports; + const size = exports.size(); + const offset = exports.offset(); + const array = new Uint8Array(exports.memory.buffer); + wasmBinary = array.slice(offset, offset + size); const core = await load_perspective({ wasmBinary, wasmJSMethod: "native-wasm", diff --git a/packages/perspective/test/js/expressions/conversions.spec.js b/packages/perspective/test/js/expressions/conversions.spec.js index 1ff5654253..66734f1ede 100644 --- a/packages/perspective/test/js/expressions/conversions.spec.js +++ b/packages/perspective/test/js/expressions/conversions.spec.js @@ -30,15 +30,15 @@ const perspective = require("@finos/perspective"); }); const view = await table.view({ - expressions: [ - '// computed\n string("a")', - '// computed2\n string("b")', - '// computed3\n string("c")', - '// computed4\n string("d")', - '// computed5\n string("e")', - '// computed6\n string("f")', - "// computed7\n string(1234.5678)", - ], + expressions: { + computed: ' string("a")', + computed2: ' string("b")', + computed3: ' string("c")', + computed4: ' string("d")', + computed5: ' string("e")', + computed6: ' string("f")', + computed7: " string(1234.5678)", + }, }); table.update({ @@ -92,11 +92,11 @@ const perspective = require("@finos/perspective"); computed2: "last", computed3: "last", }, - expressions: [ - "// computed\n string('abcdefg')", - "// computed2\n string(1234)", - "// computed3\n string(1234.5678)", - ], + expressions: { + computed: " string('abcdefg')", + computed2: " string(1234)", + computed3: " string(1234.5678)", + }, }); expect(await view.expression_schema()).toEqual({ @@ -120,22 +120,22 @@ const perspective = require("@finos/perspective"); test("Should create integers from scalars", async () => { const table = await perspective.table({ x: [1] }); const view = await table.view({ - expressions: [ - `//computed\ninteger(999999.999)`, - `//computed2\ninteger(2147483648)`, - `//computed3\ninteger(-2147483649)`, - `//computed4\ninteger(123456789)`, - `//computed5\ninteger(0.00001)`, - `//computed6\ninteger(1.9999999999)`, - `//computed7\ninteger(2147483647)`, - `//computed8\ninteger(-2147483647)`, - `//computed9\ninteger('957187281')`, - `//computed10\ninteger('1928.2817')`, - `//computed11\ninteger('1234abcd')`, - `//computed12\ninteger('abcdefg1234')`, - `//computed13\ninteger('2147483648')`, - `//computed14\ninteger('-2147483649')`, - ], + expressions: { + [`computed`]: `integer(999999.999)`, + [`computed2`]: `integer(2147483648)`, + [`computed3`]: `integer(-2147483649)`, + [`computed4`]: `integer(123456789)`, + [`computed5`]: `integer(0.00001)`, + [`computed6`]: `integer(1.9999999999)`, + [`computed7`]: `integer(2147483647)`, + [`computed8`]: `integer(-2147483647)`, + [`computed9`]: `integer('957187281')`, + [`computed10`]: `integer('1928.2817')`, + [`computed11`]: `integer('1234abcd')`, + [`computed12`]: `integer('abcdefg1234')`, + [`computed13`]: `integer('2147483648')`, + [`computed14`]: `integer('-2147483649')`, + }, }); const result = await view.to_columns(); @@ -166,7 +166,7 @@ const perspective = require("@finos/perspective"); const table = await perspective.table({ x: "integer" }); const view = await table.view({ - expressions: [`//computed\ninteger("x")`], + expressions: { computed: `integer("x")` }, }); table.update({ @@ -191,7 +191,7 @@ const perspective = require("@finos/perspective"); const table = await perspective.table({ x: "float" }); const view = await table.view({ - expressions: [`//computed\ninteger("x")`], + expressions: { computed: `integer("x")` }, }); table.update({ @@ -223,7 +223,7 @@ const perspective = require("@finos/perspective"); const table = await perspective.table({ x: "date" }); const view = await table.view({ - expressions: [`//computed\ninteger("x")`], + expressions: { computed: `integer("x")` }, }); const value = new Date(2020, 5, 30); @@ -256,7 +256,7 @@ const perspective = require("@finos/perspective"); const table = await perspective.table({ x: "datetime" }); const view = await table.view({ - expressions: [`//computed\ninteger("x")`], + expressions: { computed: `integer("x")` }, }); // first will not overflow, second will @@ -288,7 +288,7 @@ const perspective = require("@finos/perspective"); }); const view = await table.view({ - expressions: [`//computed\ninteger("x")`], + expressions: { computed: `integer("x")` }, }); const result = await view.to_columns(); @@ -303,26 +303,26 @@ const perspective = require("@finos/perspective"); test("Should create float from scalars", async () => { const table = await perspective.table({ x: [1] }); const view = await table.view({ - expressions: [ - `//computed\n float(999999.999)`, - `//computed2\n float(2147483648)`, - `//computed3\n float(-2147483649)`, - `//computed4\n float(123456789)`, - `//computed5\n float(0.00001)`, - `//computed6\n float(1.9999999992)`, - `//computed7\n float(2147483647.1234567)`, - `//computed8\n float(-2147483647)`, - `//computed9\n float('957187281.00000001')`, - `//computed10\n float('1928.2817')`, - `//computed11\n float('abcdefg')`, - `//computed12\n float('abcdefg1234.123125')`, - `//computed13\n float('2147483648.1234566')`, - `//computed14\n float('-2147483649')`, - `//computed15\n float('inf')`, - `//computed16\n float('-inf')`, - `//computed17\n float(inf)`, - `//computed18\n float(-inf)`, - ], + expressions: { + [`computed`]: `float(999999.999)`, + [`computed2`]: `float(2147483648)`, + [`computed3`]: `float(-2147483649)`, + [`computed4`]: `float(123456789)`, + [`computed5`]: `float(0.00001)`, + [`computed6`]: `float(1.9999999992)`, + [`computed7`]: `float(2147483647.1234567)`, + [`computed8`]: `float(-2147483647)`, + [`computed9`]: `float('957187281.00000001')`, + [`computed10`]: `float('1928.2817')`, + [`computed11`]: `float('abcdefg')`, + [`computed12`]: `float('abcdefg1234.123125')`, + [`computed13`]: `float('2147483648.1234566')`, + [`computed14`]: `float('-2147483649')`, + [`computed15`]: `float('inf')`, + [`computed16`]: `float('-inf')`, + [`computed17`]: `float(inf)`, + [`computed18`]: `float(-inf)`, + }, }); expect(await view.expression_schema()).toEqual({ @@ -374,7 +374,7 @@ const perspective = require("@finos/perspective"); const table = await perspective.table({ x: "integer" }); const view = await table.view({ - expressions: [`//computed\nfloat("x")`], + expressions: { computed: `float("x")` }, }); table.update({ @@ -399,7 +399,7 @@ const perspective = require("@finos/perspective"); const table = await perspective.table({ x: "float" }); const view = await table.view({ - expressions: [`//computed\n float("x")`], + expressions: { computed: ` float("x")` }, }); table.update({ @@ -431,7 +431,7 @@ const perspective = require("@finos/perspective"); const table = await perspective.table({ x: "date" }); const view = await table.view({ - expressions: [`//computed\nfloat("x")`], + expressions: { computed: `float("x")` }, }); const value = new Date(2020, 5, 30); @@ -464,7 +464,7 @@ const perspective = require("@finos/perspective"); const table = await perspective.table({ x: "datetime" }); const view = await table.view({ - expressions: [`//computed\nfloat("x")`], + expressions: { computed: `float("x")` }, }); table.update({ @@ -495,7 +495,7 @@ const perspective = require("@finos/perspective"); }); const view = await table.view({ - expressions: [`//computed\n float("x")`], + expressions: { computed: ` float("x")` }, }); const result = await view.to_columns(); @@ -521,7 +521,7 @@ const perspective = require("@finos/perspective"); }); const view = await table.view({ - expressions: ['boolean("x")'], + expressions: { 'boolean("x")': 'boolean("x")' }, }); expect(await view.expression_schema()).toEqual({ @@ -543,7 +543,7 @@ const perspective = require("@finos/perspective"); }); const view = await table.view({ - expressions: ['boolean("x")'], + expressions: { 'boolean("x")': 'boolean("x")' }, }); expect(await view.expression_schema()).toEqual({ @@ -569,7 +569,7 @@ const perspective = require("@finos/perspective"); ); const view = await table.view({ - expressions: ['boolean("x")'], + expressions: { 'boolean("x")': 'boolean("x")' }, }); expect(await view.expression_schema()).toEqual({ @@ -607,7 +607,7 @@ const perspective = require("@finos/perspective"); ); const view = await table.view({ - expressions: ['boolean("x")'], + expressions: { 'boolean("x")': 'boolean("x")' }, }); expect(await view.expression_schema()).toEqual({ @@ -640,11 +640,11 @@ const perspective = require("@finos/perspective"); }); const view = await table.view({ - expressions: [ - `//computed \n date(2020, 7, 15)`, - `//computed2 \n date(1970, 10, 29)`, - `//computed3\n date(2020, 1, "x")`, - ], + expressions: { + [`computed`]: `date(2020, 7, 15)`, + [`computed2`]: `date(1970, 10, 29)`, + [`computed3`]: `date(2020, 1, "x")`, + }, }); const result = await view.to_columns(); @@ -674,7 +674,7 @@ const perspective = require("@finos/perspective"); }); const view = await table.view({ - expressions: [`//computed \n date("y", "m", "d")`], + expressions: { computed: ` date("y", "m", "d")` }, }); table.update({ @@ -711,7 +711,7 @@ const perspective = require("@finos/perspective"); }); const view = await table.view({ - expressions: [`//computed \n date("y", "m", "d")`], + expressions: { computed: ` date("y", "m", "d")` }, }); table.update({ @@ -749,7 +749,7 @@ const perspective = require("@finos/perspective"); }); const view = await table.view({ - expressions: [`//computed \n date("y", "m", "d")`], + expressions: { computed: ` date("y", "m", "d")` }, }); const result = await view.to_columns(); @@ -769,9 +769,9 @@ const perspective = require("@finos/perspective"); }); const view = await table.view({ - expressions: [ - `//computed \n var year := floor("x" / 10000); var month := floor("x" % 10000 / 100); var day := floor("x" % 100); date(year, month, day)`, - ], + expressions: { + [`computed`]: `var year := floor("x" / 10000); var month := floor("x" % 10000 / 100); var day := floor("x" % 100); date(year, month, day)`, + }, }); const result = await view.to_columns(); @@ -794,11 +794,11 @@ const perspective = require("@finos/perspective"); d: [1, 10, 32, 10], }); - const validated = await table.validate_expressions([ - `//computed \n date()`, - `//computed2 \n date('abc', 'def', '123')`, - `//computed3\ndate("y", "m", "d")`, - ]); + const validated = await table.validate_expressions({ + [`computed`]: `date()`, + [`computed2`]: `date('abc', 'def', '123')`, + [`computed3`]: `date("y", "m", "d")`, + }); expect(validated.expression_schema).toEqual({ computed3: "date", @@ -806,10 +806,10 @@ const perspective = require("@finos/perspective"); expect(validated.errors).toEqual({ computed: { - column: 7, + column: 6, error_message: "Zero parameter call to generic function: date not allowed", - line: 1, + line: 0, }, computed2: { column: 0, @@ -833,10 +833,10 @@ const perspective = require("@finos/perspective"); const b = new Date(2005, 6, 31, 11, 59, 32).getTime(); const view = await table.view({ - expressions: [ - `//computed \n datetime(${a})`, - `//computed2 \n datetime(${b})`, - ], + expressions: { + [`computed`]: `datetime(${a})`, + [`computed2`]: `datetime(${b})`, + }, }); expect(await view.expression_schema()).toEqual({ @@ -857,7 +857,7 @@ const perspective = require("@finos/perspective"); }); const view = await table.view({ - expressions: [`//computed \n datetime(float("x"))`], + expressions: { computed: ` datetime(float("x"))` }, }); expect(await view.expression_schema()).toEqual({ @@ -878,7 +878,7 @@ const perspective = require("@finos/perspective"); }); const view = await table.view({ - expressions: [`//computed \n datetime("x")`], + expressions: { computed: ` datetime("x")` }, }); const data = [ @@ -909,7 +909,7 @@ const perspective = require("@finos/perspective"); }); const view = await table.view({ - expressions: [`//computed \n datetime("x")`], + expressions: { computed: ` datetime("x")` }, }); const data = [ @@ -937,10 +937,10 @@ const perspective = require("@finos/perspective"); }); const view = await table.view({ - expressions: [ - `//computed1 \n datetime(-1)`, - `//computed2 \n datetime(0)`, - `//computed3 \n datetime(${new Date( + expressions: { + [`computed1`]: `datetime(-1)`, + [`computed2`]: `datetime(0)`, + computed3: `datetime(${new Date( 2002, 11, 12, @@ -948,7 +948,7 @@ const perspective = require("@finos/perspective"); 14, 15 ).getTime()})`, - ], + }, }); expect(await view.expression_schema()).toEqual({ @@ -973,22 +973,22 @@ const perspective = require("@finos/perspective"); x: [1], }); - const validated = await table.validate_expressions([ - `//computed1 \n datetime()`, - `//computed2 \n datetime('abcd')`, - `//computed3 \n datetime(today())`, - `//computed4 \n datetime(now())`, - `//computed5 \n datetime(123456, 7)`, - ]); + const validated = await table.validate_expressions({ + [`computed1`]: `datetime()`, + [`computed2`]: `datetime('abcd')`, + [`computed3`]: `datetime(today())`, + [`computed4`]: `datetime(now())`, + [`computed5`]: `datetime(123456, 7)`, + }); expect(validated.expression_schema).toEqual({}); expect(validated.errors).toEqual({ computed1: { - column: 11, + column: 10, error_message: "Zero parameter call to generic function: datetime not allowed", - line: 1, + line: 0, }, computed2: { error_message: @@ -1011,8 +1011,8 @@ const perspective = require("@finos/perspective"); computed5: { error_message: "Failed parameter type check for function 'datetime', Expected 'T' call set: 'TT'", - column: 20, - line: 1, + column: 19, + line: 0, }, }); diff --git a/packages/perspective/test/js/expressions/datetime.spec.js b/packages/perspective/test/js/expressions/datetime.spec.js index 57498d0505..4b3f748346 100644 --- a/packages/perspective/test/js/expressions/datetime.spec.js +++ b/packages/perspective/test/js/expressions/datetime.spec.js @@ -28,11 +28,11 @@ const perspective = require("@finos/perspective"); }); const view = await table.view({ - expressions: [ - '"a" == "b"', - '"a" != "b"', - '"a" == "b" ? 100 : 0', - ], + expressions: { + '"a" == "b"': '"a" == "b"', + '"a" != "b"': '"a" != "b"', + '"a" == "b" ? 100 : 0': '"a" == "b" ? 100 : 0', + }, }); table.update({ @@ -81,11 +81,11 @@ const perspective = require("@finos/perspective"); }); const view = await table.view({ - expressions: [ - '"a" > "b"', - '"a" >= "b"', - '"a" >= "b" ? 100 : 0', - ], + expressions: { + '"a" > "b"': '"a" > "b"', + '"a" >= "b"': '"a" >= "b"', + '"a" >= "b" ? 100 : 0': '"a" >= "b" ? 100 : 0', + }, }); table.update({ @@ -134,11 +134,11 @@ const perspective = require("@finos/perspective"); }); const view = await table.view({ - expressions: [ - '"a" < "b"', - '"a" <= "b"', - '"a" <= "b" ? 100 : 0', - ], + expressions: { + '"a" < "b"': '"a" < "b"', + '"a" <= "b"': '"a" <= "b"', + '"a" <= "b" ? 100 : 0': '"a" <= "b" ? 100 : 0', + }, }); table.update({ @@ -188,7 +188,7 @@ const perspective = require("@finos/perspective"); }); const view = await table.view({ - expressions: ['today() == "a"'], + expressions: { 'today() == "a"': 'today() == "a"' }, }); table.update({ @@ -207,7 +207,7 @@ const perspective = require("@finos/perspective"); }); const view = await table.view({ - expressions: ['hour_of_day("a")'], + expressions: { 'hour_of_day("a")': 'hour_of_day("a")' }, }); table.update({ @@ -226,7 +226,7 @@ const perspective = require("@finos/perspective"); }); const view = await table.view({ - expressions: ['hour_of_day("a")'], + expressions: { 'hour_of_day("a")': 'hour_of_day("a")' }, }); table.update({ @@ -245,7 +245,7 @@ const perspective = require("@finos/perspective"); }); const view = await table.view({ - expressions: ['day_of_week("a")'], + expressions: { 'day_of_week("a")': 'day_of_week("a")' }, }); table.update({ @@ -272,7 +272,7 @@ const perspective = require("@finos/perspective"); }); const view = await table.view({ - expressions: ['day_of_week("a")'], + expressions: { 'day_of_week("a")': 'day_of_week("a")' }, }); table.update({ @@ -301,7 +301,7 @@ const perspective = require("@finos/perspective"); }); const view = await table.view({ - expressions: ['month_of_year("a")'], + expressions: { 'month_of_year("a")': 'month_of_year("a")' }, }); table.update({ @@ -332,7 +332,7 @@ const perspective = require("@finos/perspective"); }); const view = await table.view({ - expressions: ['month_of_year("a")'], + expressions: { 'month_of_year("a")': 'month_of_year("a")' }, }); table.update({ @@ -363,7 +363,7 @@ const perspective = require("@finos/perspective"); }); const view = await table.view({ - expressions: ["bucket(\"a\", 's')"], + expressions: { "bucket(\"a\", 's')": "bucket(\"a\", 's')" }, }); table.update({ @@ -389,18 +389,18 @@ const perspective = require("@finos/perspective"); x: [1], }); - const validated = await table.validate_expressions([ - `//bucket0\nbucket("x", '2W')`, - `//bucket1\nbucket("x", '3W')`, - `//bucket2\nbucket("x", '4W')`, - `//bucket3\nbucket("x", '5W')`, - `//bucket4\nbucket("x", '2D')`, - `//bucket5\nbucket("x", '3D')`, - `//bucket6\nbucket("x", '7D')`, - `//bucket7\nbucket("x", '10D')`, - `//bucket8\nbucket("x", '15D')`, - `//bucket9\nbucket("x", '30D')`, - ]); + const validated = await table.validate_expressions({ + [`bucket0`]: `bucket("x", '2W')`, + [`bucket1`]: `bucket("x", '3W')`, + [`bucket2`]: `bucket("x", '4W')`, + [`bucket3`]: `bucket("x", '5W')`, + [`bucket4`]: `bucket("x", '2D')`, + [`bucket5`]: `bucket("x", '3D')`, + [`bucket6`]: `bucket("x", '7D')`, + [`bucket7`]: `bucket("x", '10D')`, + [`bucket8`]: `bucket("x", '15D')`, + [`bucket9`]: `bucket("x", '30D')`, + }); expect(validated.expression_schema).toEqual({}); @@ -475,7 +475,7 @@ const perspective = require("@finos/perspective"); a: "date", }); const view = await table.view({ - expressions: ["bucket(\"a\", 's')"], + expressions: { "bucket(\"a\", 's')": "bucket(\"a\", 's')" }, }); table.update({ @@ -504,7 +504,7 @@ const perspective = require("@finos/perspective"); }); const view = await table.view({ - expressions: ["bucket(\"a\", 'm')"], + expressions: { "bucket(\"a\", 'm')": "bucket(\"a\", 'm')" }, }); table.update({ @@ -531,7 +531,7 @@ const perspective = require("@finos/perspective"); }); const view = await table.view({ - expressions: ["bucket(\"a\", 'm')"], + expressions: { "bucket(\"a\", 'm')": "bucket(\"a\", 'm')" }, }); table.update({ @@ -560,7 +560,7 @@ const perspective = require("@finos/perspective"); }); const view = await table.view({ - expressions: ["bucket(\"a\", 'h')"], + expressions: { "bucket(\"a\", 'h')": "bucket(\"a\", 'h')" }, }); table.update({ @@ -587,7 +587,7 @@ const perspective = require("@finos/perspective"); }); const view = await table.view({ - expressions: ["bucket(\"a\", 'h')"], + expressions: { "bucket(\"a\", 'h')": "bucket(\"a\", 'h')" }, }); table.update({ @@ -616,7 +616,7 @@ const perspective = require("@finos/perspective"); }); const view = await table.view({ - expressions: ["bucket(\"a\", 'D')"], + expressions: { "bucket(\"a\", 'D')": "bucket(\"a\", 'D')" }, }); table.update({ @@ -643,7 +643,7 @@ const perspective = require("@finos/perspective"); }); const view = await table.view({ - expressions: ["bucket(\"a\", 'D')"], + expressions: { "bucket(\"a\", 'D')": "bucket(\"a\", 'D')" }, }); table.update({ @@ -672,7 +672,7 @@ const perspective = require("@finos/perspective"); }); const view = await table.view({ - expressions: ["bucket(\"a\", 'W')"], + expressions: { "bucket(\"a\", 'W')": "bucket(\"a\", 'W')" }, }); table.update({ @@ -702,7 +702,7 @@ const perspective = require("@finos/perspective"); }); const view = await table.view({ - expressions: ["bucket(\"a\", 'W')"], + expressions: { "bucket(\"a\", 'W')": "bucket(\"a\", 'W')" }, }); table.update({ @@ -732,7 +732,7 @@ const perspective = require("@finos/perspective"); }); const view = await table.view({ - expressions: ["bucket(\"a\", 'W')"], + expressions: { "bucket(\"a\", 'W')": "bucket(\"a\", 'W')" }, }); table.update({ @@ -754,7 +754,7 @@ const perspective = require("@finos/perspective"); }); const view = await table.view({ - expressions: ["bucket(\"a\", 'M')"], + expressions: { "bucket(\"a\", 'M')": "bucket(\"a\", 'M')" }, }); table.update({ @@ -784,7 +784,7 @@ const perspective = require("@finos/perspective"); }); const view = await table.view({ - expressions: ["bucket(\"a\", 'M')"], + expressions: { "bucket(\"a\", 'M')": "bucket(\"a\", 'M')" }, }); table.update({ @@ -816,7 +816,7 @@ const perspective = require("@finos/perspective"); const col_name = "bucket(\"a\", '3M')"; const view = await table.view({ - expressions: [col_name], + expressions: { [col_name]: col_name }, }); table.update({ @@ -850,7 +850,7 @@ const perspective = require("@finos/perspective"); }); const view = await table.view({ - expressions: ["bucket(\"a\", 'Y')"], + expressions: { "bucket(\"a\", 'Y')": "bucket(\"a\", 'Y')" }, }); table.update({ @@ -880,7 +880,7 @@ const perspective = require("@finos/perspective"); }); const view = await table.view({ - expressions: ["bucket(\"a\", 'Y')"], + expressions: { "bucket(\"a\", 'Y')": "bucket(\"a\", 'Y')" }, }); table.update({ @@ -912,7 +912,7 @@ const perspective = require("@finos/perspective"); const col_name = "bucket(\"a\", '7Y')"; const view = await table.view({ - expressions: [col_name], + expressions: { [col_name]: col_name }, }); table.update({ @@ -948,7 +948,7 @@ const perspective = require("@finos/perspective"); }); const view = await table.view({ - expressions: [`hour_of_day("a")`], + expressions: { 'hour_of_day("a")': 'hour_of_day("a")' }, }); table.update({ @@ -969,7 +969,7 @@ const perspective = require("@finos/perspective"); }); const view = await table.view({ - expressions: [`hour_of_day("a")`], + expressions: { 'hour_of_day("a")': 'hour_of_day("a")' }, }); table.update({ @@ -990,7 +990,7 @@ const perspective = require("@finos/perspective"); }); const view = await table.view({ - expressions: [`day_of_week("a")`], + expressions: { 'day_of_week("a")': 'day_of_week("a")' }, }); table.update({ @@ -1019,7 +1019,7 @@ const perspective = require("@finos/perspective"); }); const view = await table.view({ - expressions: [`day_of_week("a")`], + expressions: { 'day_of_week("a")': 'day_of_week("a")' }, }); table.update({ @@ -1048,7 +1048,7 @@ const perspective = require("@finos/perspective"); }); const view = await table.view({ - expressions: [`month_of_year("a")`], + expressions: { 'month_of_year("a")': 'month_of_year("a")' }, }); table.update({ @@ -1079,7 +1079,7 @@ const perspective = require("@finos/perspective"); }); const view = await table.view({ - expressions: [`month_of_year("a")`], + expressions: { 'month_of_year("a")': 'month_of_year("a")' }, }); table.update({ @@ -1110,7 +1110,7 @@ const perspective = require("@finos/perspective"); }); const view = await table.view({ - expressions: [`bucket("a", 's')`], + expressions: { "bucket(\"a\", 's')": "bucket(\"a\", 's')" }, }); table.update({ @@ -1137,7 +1137,7 @@ const perspective = require("@finos/perspective"); }); const view = await table.view({ - expressions: [`bucket("a", 's')`], + expressions: { "bucket(\"a\", 's')": "bucket(\"a\", 's')" }, }); table.update({ @@ -1168,7 +1168,7 @@ const perspective = require("@finos/perspective"); const col_name = `bucket("a", '20s')`; const view = await table.view({ - expressions: [col_name], + expressions: { [col_name]: col_name }, }); table.update({ @@ -1197,7 +1197,7 @@ const perspective = require("@finos/perspective"); }); const view = await table.view({ - expressions: [`bucket("a", 'm')`], + expressions: { "bucket(\"a\", 'm')": "bucket(\"a\", 'm')" }, }); table.update({ @@ -1224,7 +1224,7 @@ const perspective = require("@finos/perspective"); }); const view = await table.view({ - expressions: [`bucket("a", 'm')`], + expressions: { "bucket(\"a\", 'm')": "bucket(\"a\", 'm')" }, }); table.update({ @@ -1255,7 +1255,7 @@ const perspective = require("@finos/perspective"); const col_name = `bucket("a", '15m')`; const view = await table.view({ - expressions: [col_name], + expressions: { [col_name]: col_name }, }); table.update({ @@ -1283,7 +1283,7 @@ const perspective = require("@finos/perspective"); }); const view = await table.view({ - expressions: [`bucket("a", 'h')`], + expressions: { "bucket(\"a\", 'h')": "bucket(\"a\", 'h')" }, }); table.update({ @@ -1310,7 +1310,7 @@ const perspective = require("@finos/perspective"); }); const view = await table.view({ - expressions: [`bucket("a", 'h')`], + expressions: { "bucket(\"a\", 'h')": "bucket(\"a\", 'h')" }, }); table.update({ @@ -1339,7 +1339,7 @@ const perspective = require("@finos/perspective"); const col_name = `bucket("a", '6h')`; const view = await table.view({ - expressions: [col_name], + expressions: { [col_name]: col_name }, }); table.update({ @@ -1368,7 +1368,7 @@ const perspective = require("@finos/perspective"); }); const view = await table.view({ - expressions: [`bucket("a", 'D')`], + expressions: { "bucket(\"a\", 'D')": "bucket(\"a\", 'D')" }, }); table.update({ @@ -1395,7 +1395,7 @@ const perspective = require("@finos/perspective"); }); const view = await table.view({ - expressions: [`bucket("a", 'D')`], + expressions: { "bucket(\"a\", 'D')": "bucket(\"a\", 'D')" }, }); table.update({ @@ -1422,7 +1422,7 @@ const perspective = require("@finos/perspective"); }); const view = await table.view({ - expressions: [`bucket("a", 'D')`], + expressions: { "bucket(\"a\", 'D')": "bucket(\"a\", 'D')" }, }); table.update({ @@ -1449,7 +1449,7 @@ const perspective = require("@finos/perspective"); }); const view = await table.view({ - expressions: [`bucket("a", 'W')`], + expressions: { "bucket(\"a\", 'W')": "bucket(\"a\", 'W')" }, }); table.update({ @@ -1477,7 +1477,7 @@ const perspective = require("@finos/perspective"); }); const view = await table.view({ - expressions: [`bucket("a", 'W')`], + expressions: { "bucket(\"a\", 'W')": "bucket(\"a\", 'W')" }, }); table.update({ @@ -1505,7 +1505,7 @@ const perspective = require("@finos/perspective"); }); const view = await table.view({ - expressions: [`bucket("a", 'W')`], + expressions: { "bucket(\"a\", 'W')": "bucket(\"a\", 'W')" }, }); table.update({ @@ -1527,7 +1527,7 @@ const perspective = require("@finos/perspective"); }); const view = await table.view({ - expressions: [`bucket("a", 'M')`], + expressions: { "bucket(\"a\", 'M')": "bucket(\"a\", 'M')" }, }); table.update({ @@ -1555,7 +1555,7 @@ const perspective = require("@finos/perspective"); }); const view = await table.view({ - expressions: [`bucket("a", 'M')`], + expressions: { "bucket(\"a\", 'M')": "bucket(\"a\", 'M')" }, }); table.update({ @@ -1583,7 +1583,7 @@ const perspective = require("@finos/perspective"); }); const view = await table.view({ - expressions: [`bucket("a", 'Y')`], + expressions: { "bucket(\"a\", 'Y')": "bucket(\"a\", 'Y')" }, }); table.update({ @@ -1611,7 +1611,7 @@ const perspective = require("@finos/perspective"); }); const view = await table.view({ - expressions: [`bucket("a", 'Y')`], + expressions: { "bucket(\"a\", 'Y')": "bucket(\"a\", 'Y')" }, }); table.update({ diff --git a/packages/perspective/test/js/expressions/deltas.spec.js b/packages/perspective/test/js/expressions/deltas.spec.js index 4e7fd3402a..14f178e4c5 100644 --- a/packages/perspective/test/js/expressions/deltas.spec.js +++ b/packages/perspective/test/js/expressions/deltas.spec.js @@ -48,8 +48,12 @@ function it_old_behavior(name, capture) { x: [1, 2, 3, 4], y: ["A", "B", "C", "D"], }); + const view = await table.view({ - expressions: ['lower("y")', '-"x"'], + expressions: { + 'lower("y")': 'lower("y")', + '-"x"': '-"x"', + }, }); view.on_update( @@ -92,7 +96,7 @@ function it_old_behavior(name, capture) { y: "string", }); const view = await table.view({ - expressions: ['upper("y")'], + expressions: { 'upper("y")': 'upper("y")' }, }); view.on_update( @@ -133,7 +137,7 @@ function it_old_behavior(name, capture) { { index: "x" } ); const view = await table.view({ - expressions: ['lower("y")'], + expressions: { 'lower("y")': 'lower("y")' }, }); view.on_update( @@ -172,7 +176,7 @@ function it_old_behavior(name, capture) { y: ["A", "B", "C", "D"], }); const view = await table.view({ - expressions: ['lower("y")'], + expressions: { 'lower("y")': 'lower("y")' }, }); view.on_update( @@ -214,7 +218,7 @@ function it_old_behavior(name, capture) { }); const view = await table.view({ group_by: ['lower("y")'], - expressions: ['lower("y")'], + expressions: { 'lower("y")': 'lower("y")' }, }); view.on_update( @@ -249,7 +253,7 @@ function it_old_behavior(name, capture) { }); const view = await table.view({ group_by: ['lower("y")'], - expressions: ['lower("y")'], + expressions: { 'lower("y")': 'lower("y")' }, sort: [['lower("y")', "desc"]], columns: ["x"], }); @@ -288,7 +292,7 @@ function it_old_behavior(name, capture) { }, group_by: ['lower("y")'], split_by: ["y"], - expressions: ['lower("y")'], + expressions: { 'lower("y")': 'lower("y")' }, }); view.on_update( @@ -381,7 +385,7 @@ function it_old_behavior(name, capture) { y: ["A", "B", "C", "D"], }); const view = await table.view({ - expressions: ['lower("y")'], + expressions: { 'lower("y")': 'lower("y")' }, }); const pre_update = await view.to_columns(); @@ -430,7 +434,7 @@ function it_old_behavior(name, capture) { { index: "x" } ); const view = await table.view({ - expressions: ['lower("y")'], + expressions: { 'lower("y")': 'lower("y")' }, }); const pre_update = await view.to_columns(); @@ -483,11 +487,11 @@ function it_old_behavior(name, capture) { ); const view = await table.view({ - expressions: ['lower("y")'], + expressions: { 'lower("y")': 'lower("y")' }, }); const view2 = await table.view({ - expressions: ['-"x"'], + expressions: { '-"x"': '-"x"' }, }); view.on_update( @@ -550,7 +554,7 @@ function it_old_behavior(name, capture) { const view = await table.view(); const view2 = await table.view({ - expressions: ['lower("y")'], + expressions: { 'lower("y")': 'lower("y")' }, }); expect(await view2.to_columns()).toEqual({ diff --git a/packages/perspective/test/js/expressions/functionality.spec.js b/packages/perspective/test/js/expressions/functionality.spec.js index 1e359ac90a..2626675267 100644 --- a/packages/perspective/test/js/expressions/functionality.spec.js +++ b/packages/perspective/test/js/expressions/functionality.spec.js @@ -62,7 +62,10 @@ const perspective = require("@finos/perspective"); expressions_common.int_float_data ); const view = await table.view({ - expressions: ["if (\"w\" == 1.5) 5; else 'abc'"], + expressions: { + "if (\"w\" == 1.5) 5; else 'abc'": + "if (\"w\" == 1.5) 5; else 'abc'", + }, }); const results = await view.to_columns(); @@ -87,7 +90,7 @@ const perspective = require("@finos/perspective"); expressions: [ 'if ("w" > 2, 5, 10);', 'if ("w" == 1.5, 5, 10)', - ], + ].reduce((x, y) => Object.assign(x, { [y]: y }), {}), }); const results = await view.to_columns(); @@ -107,7 +110,7 @@ const perspective = require("@finos/perspective"); expressions: [ "if (\"y\" == 'a', 5, 10);", "if (\"y\" != 'a', 5, 10)", - ], + ].reduce((x, y) => Object.assign(x, { [y]: y }), {}), }); const results = await view.to_columns(); @@ -129,7 +132,7 @@ const perspective = require("@finos/perspective"); expressions: [ 'if ("z" == true, 5, 10);', 'if ("z" != true, 5, 10)', - ], + ].reduce((x, y) => Object.assign(x, { [y]: y }), {}), }); const results = await view.to_columns(); @@ -153,7 +156,7 @@ const perspective = require("@finos/perspective"); expressions: [ 'if ("a" == "b", 5, 10);', 'if ("a" < "b", 5, 10)', - ], + ].reduce((x, y) => Object.assign(x, { [y]: y }), {}), }); table.update({ @@ -193,7 +196,7 @@ const perspective = require("@finos/perspective"); expressions: [ 'if ("a" >= "b", 5, 10);', 'if ("a" < "b", 5, 10)', - ], + ].reduce((x, y) => Object.assign(x, { [y]: y }), {}), }); table.update({ @@ -227,7 +230,10 @@ const perspective = require("@finos/perspective"); expressions_common.int_float_data ); const view = await table.view({ - expressions: ['"w" > 2 ? 5 : 10;', '"w" == 1.5 ? 5 : 10'], + expressions: { + '"w" > 2 ? 5 : 10;': '"w" > 2 ? 5 : 10;', + '"w" == 1.5 ? 5 : 10': '"w" == 1.5 ? 5 : 10', + }, }); const results = await view.to_columns(); @@ -245,7 +251,7 @@ const perspective = require("@finos/perspective"); expressions: [ "\"y\" == 'a' ? 5 : 10;", "\"y\" != 'a' ? 5 : 10", - ], + ].reduce((x, y) => Object.assign(x, { [y]: y }), {}), }); const results = await view.to_columns(); @@ -265,7 +271,7 @@ const perspective = require("@finos/perspective"); expressions: [ '"z" == true ? 5 : 10;', '"z" != true ? 5 : 10', - ], + ].reduce((x, y) => Object.assign(x, { [y]: y }), {}), }); const results = await view.to_columns(); @@ -282,7 +288,10 @@ const perspective = require("@finos/perspective"); }); const view = await table.view({ - expressions: ['"a" == "b" ? 5 : 10;', '"a" < "b" ? 5 : 10'], + expressions: { + '"a" == "b" ? 5 : 10;': '"a" == "b" ? 5 : 10;', + '"a" < "b" ? 5 : 10': '"a" < "b" ? 5 : 10', + }, }); table.update({ @@ -315,7 +324,10 @@ const perspective = require("@finos/perspective"); }); const view = await table.view({ - expressions: ['"a" >= "b" ? 5 : 10;', '"a" < "b" ? 5 : 10'], + expressions: { + '"a" >= "b" ? 5 : 10;': '"a" >= "b" ? 5 : 10;', + '"a" < "b" ? 5 : 10': '"a" < "b" ? 5 : 10', + }, }); table.update({ @@ -349,7 +361,7 @@ const perspective = require("@finos/perspective"); 'if ("w" > 2) 5; else 10', 'if ("w" > 2) 5; else 10;', 'if ("w" > 2) { \n5; } else { \n10;}', - ], + ].reduce((x, y) => Object.assign(x, { [y]: y }), {}), }); const results = await view.to_columns(); @@ -375,7 +387,7 @@ const perspective = require("@finos/perspective"); 'if ("w" == 3.5) 15; else if ("w" > 2) 5; else 10', 'if ("w" == 3.5) 15; else if ("w" > 2) 5; else 10;', 'if ("w" == 3.5) { \n15; } else if ("w" > 2) { \n5; } else { \n10;}', - ], + ].reduce((x, y) => Object.assign(x, { [y]: y }), {}), }); const results = await view.to_columns(); @@ -403,7 +415,7 @@ const perspective = require("@finos/perspective"); const view = await table.view({ expressions: [ `switch { case "w" > 2: sqrt(144); case "y" == 'a': sqrt(121); default: 0; }`, - ], + ].reduce((x, y) => Object.assign(x, { [y]: y }), {}), }); const results = await view.to_columns(); @@ -427,7 +439,7 @@ const perspective = require("@finos/perspective"); table.update(expressions_common.int_float_data); const expr = `switch { case "w" > 2: (sqrt(144) * "w"); case "y" == 'a': (sqrt(121) * "w"); default: "x"; }`; const view = await table.view({ - expressions: [expr], + expressions: { [expr]: expr }, }); const results = await view.to_columns(); @@ -450,7 +462,7 @@ const perspective = require("@finos/perspective"); const view = await table.view({ expressions: [ `var x := 1; var y := 0; while (y < 10) { x := x * 2; y += 1; }; x`, - ], + ].reduce((x, y) => Object.assign(x, { [y]: y }), {}), }); const results = await view.to_columns(); @@ -473,7 +485,7 @@ const perspective = require("@finos/perspective"); const view = await table.view({ expressions: [ `var x := 2; var y := 0; while (y < 3) { x := x * x; y += 1; }; x`, - ], + ].reduce((x, y) => Object.assign(x, { [y]: y }), {}), }); const results = await view.to_columns(); @@ -492,14 +504,14 @@ const perspective = require("@finos/perspective"); expressions_common.int_float_data ); - const schema = await table.validate_expressions([ - `//expr\nvar x := 2; var y := 0; while (y < 3) { x := x * x; y += 1; } x`, - ]); + const schema = await table.validate_expressions({ + [`expr`]: `var x := 2; var y := 0; while (y < 3) { x := x * x; y += 1; } x`, + }); expect(schema.expression_schema["expr"]).toBeUndefined(); expect(schema.errors["expr"]).toEqual({ column: 62, - line: 1, + line: 0, error_message: "Invalid expression encountered", }); @@ -513,7 +525,7 @@ const perspective = require("@finos/perspective"); const view = await table.view({ expressions: [ `var x := 0; var y := 0; while (y < 10) { x := "w" * 2; y += 1; }; x`, - ], + ].reduce((x, y) => Object.assign(x, { [y]: y }), {}), }); const results = await view.to_columns(); @@ -534,7 +546,7 @@ const perspective = require("@finos/perspective"); const view = await table.view({ expressions: [ `var x := 0; var y := 0; while (y < 10) { if (y % 2 == 0) { break; }; x := "w" * 2; y += 1; }; x`, - ], + ].reduce((x, y) => Object.assign(x, { [y]: y }), {}), }); const results = await view.to_columns(); @@ -551,12 +563,12 @@ const perspective = require("@finos/perspective"); test.describe("For loop", function () { test("For loop", async () => { - const expr = `// expression\nvar x := 0; for (var i := 0; i < 3; i += 1) { x += 1; }; x`; + const expr = `var x := 0; for (var i := 0; i < 3; i += 1) { x += 1; }; x`; const table = await perspective.table( expressions_common.int_float_data ); const view = await table.view({ - expressions: [expr], + expressions: { expression: expr }, }); const results = await view.to_columns(); @@ -573,7 +585,7 @@ const perspective = require("@finos/perspective"); const view = await table.view({ expressions: [ `for (var x := 0; x < 10; x += 1) { var y := x + 1; y}`, - ], + ].reduce((x, y) => Object.assign(x, { [y]: y }), {}), }); const results = await view.to_columns(); @@ -594,7 +606,7 @@ const perspective = require("@finos/perspective"); const view = await table.view({ expressions: [ `for (var x := 0; x < 10; x += 1) { var y := "w" + 1; y}`, - ], + ].reduce((x, y) => Object.assign(x, { [y]: y }), {}), }); const results = await view.to_columns(); @@ -634,7 +646,7 @@ const perspective = require("@finos/perspective"); const view = await table.view({ expressions: [ `var x := 1; var y := 1; repeat x := x + (y * 2) until (x > 10)`, - ], + ].reduce((x, y) => Object.assign(x, { [y]: y }), {}), }); const results = await view.to_columns(); @@ -655,7 +667,7 @@ const perspective = require("@finos/perspective"); const view = await table.view({ expressions: [ `var x := 1; repeat x := x + ("x" * 2) until (x > 10)`, - ], + ].reduce((x, y) => Object.assign(x, { [y]: y }), {}), }); const results = await view.to_columns(); @@ -677,7 +689,7 @@ const perspective = require("@finos/perspective"); expressions_common.int_float_data ); const view = await table.view({ - expressions: ["1", "100.0000001"], + expressions: { 1: "1", [`100.0000001`]: "100.0000001" }, }); const results = await view.to_columns(); @@ -698,7 +710,7 @@ const perspective = require("@finos/perspective"); expressions: [ "'hello'", "'very long string that is very long and has many characters'", - ], + ].reduce((x, y) => Object.assign(x, { [y]: y }), {}), }); const results = await view.to_columns(); @@ -732,7 +744,7 @@ const perspective = require("@finos/perspective"); expressions: [ "'hello'", "'very long string that is very long and has many characters'", - ], + ].reduce((x, y) => Object.assign(x, { [y]: y }), {}), }); const results = await view.to_columns(); @@ -762,7 +774,7 @@ const perspective = require("@finos/perspective"); expressions: [ "'hello'", "'another very long string with many characters'", - ], + ].reduce((x, y) => Object.assign(x, { [y]: y }), {}), }); const results2 = await view2.to_columns(); @@ -782,7 +794,7 @@ const perspective = require("@finos/perspective"); const view = await table.view({ expressions: [ `var x := 10; var y := -100.00005; var z := x + y; abs(z)`, - ], + ].reduce((x, y) => Object.assign(x, { [y]: y }), {}), }); const results = await view.to_columns(); @@ -829,7 +841,7 @@ const perspective = require("@finos/perspective"); expressions: [ `var x := 'long literal string here'; var y := 'long literal string here'; x == y ? concat('strings: ', x, ', ', y) : 'nope'`, `var x := 'hello'; var y := upper(x); lower(y);`, - ], + ].reduce((x, y) => Object.assign(x, { [y]: y }), {}), }); const results = await view.to_columns(); @@ -858,7 +870,7 @@ const perspective = require("@finos/perspective"); const view = await table.view({ expressions: [ `var x := 'abcdefghijklmnop'; var y := '123'; concat(x, y)`, - ], + ].reduce((x, y) => Object.assign(x, { [y]: y }), {}), }); const results = await view.to_columns(); @@ -882,7 +894,10 @@ const perspective = require("@finos/perspective"); expressions_common.int_float_data ); const view = await table.view({ - expressions: [`var x := 'abc'; var y := '123'; concat(x, y)`], + expressions: { + "var x := 'abc'; var y := '123'; concat(x, y)": + "var x := 'abc'; var y := '123'; concat(x, y)", + }, }); const results = await view.to_columns(); @@ -901,7 +916,7 @@ const perspective = require("@finos/perspective"); const view = await table.view({ expressions: [ "var x := 'Eabcdefghijklmn'; var y := '0123456789'; var z := 'ABCD'; var zz := 'EFG'; concat(x, y, z, zz)", - ], + ].reduce((x, y) => Object.assign(x, { [y]: y }), {}), }); const results = await view.to_columns(); @@ -926,7 +941,7 @@ const perspective = require("@finos/perspective"); expressions: [ "var x := 'Eabcdefghijklmn'; var y := '0123456789'; var z := 'ABCD'; var zz := 'EFG'; concat(x, y, z, zz)", "var x := 'abcdefghijklmn'; var y := 'ABCDEFG123456789'; x", - ], + ].reduce((x, y) => Object.assign(x, { [y]: y }), {}), }); const results2 = await view2.to_columns(); @@ -953,7 +968,7 @@ const perspective = require("@finos/perspective"); const view = await table.view({ expressions: [ `var x := today(); var y := today(); x == y ? 1 : 0;`, - ], + ].reduce((x, y) => Object.assign(x, { [y]: y }), {}), }); const results = await view.to_columns(); @@ -971,7 +986,7 @@ const perspective = require("@finos/perspective"); ); const now = new Date().getTime(); const view = await table.view({ - expressions: ["now()"], + expressions: { "now()": "now()" }, }); const results = await view.to_columns(); @@ -992,7 +1007,7 @@ const perspective = require("@finos/perspective"); expressions: [ `var x := true; var y := false; x and y ? 1 : 0`, `var x := true; var y := false; x or y`, - ], + ].reduce((x, y) => Object.assign(x, { [y]: y }), {}), }); const results = await view.to_columns(); @@ -1015,7 +1030,10 @@ const perspective = require("@finos/perspective"); expressions_common.int_float_data ); const view = await table.view({ - expressions: [`var x := "w"; var y := 1.5; x > 2.5 ? x : y`], + expressions: { + 'var x := "w"; var y := 1.5; x > 2.5 ? x : y': + 'var x := "w"; var y := 1.5; x > 2.5 ? x : y', + }, }); const results = await view.to_columns(); @@ -1035,7 +1053,7 @@ const perspective = require("@finos/perspective"); expressions: [ `var x := 'w'; var y := 1.5; x := y; x`, `var x := 'w'; var y := 1.5; y := x; y`, - ], + ].reduce((x, y) => Object.assign(x, { [y]: y }), {}), }); const results = await view.to_columns(); @@ -1061,7 +1079,7 @@ const perspective = require("@finos/perspective"); expressions: [ `var x := 100; var y := 200.5; x := y; x`, `var x := 10; var y := pow(x, 3); y := x; y`, - ], + ].reduce((x, y) => Object.assign(x, { [y]: y }), {}), }); const results = await view.to_columns(); @@ -1083,7 +1101,7 @@ const perspective = require("@finos/perspective"); const view = await table.view({ expressions: [ `var x := 'long literal string here'; var y := lower('ANOTHER VERY LONG STRING HERE'); x := y`, - ], + ].reduce((x, y) => Object.assign(x, { [y]: y }), {}), }); const results = await view.to_columns(); @@ -1109,7 +1127,7 @@ const perspective = require("@finos/perspective"); const view = await table.view({ expressions: [ `var x := today(); var y := today(); var z := "a"; (x > z) and (y > z) ? 1 : 0`, - ], + ].reduce((x, y) => Object.assign(x, { [y]: y }), {}), }); const tomorrow = new Date(); @@ -1142,7 +1160,7 @@ const perspective = require("@finos/perspective"); const view = await table.view({ expressions: [ `var x := now(); var y := now(); var z := "a"; (x > z) and (y > z) ? 1 : 0`, - ], + ].reduce((x, y) => Object.assign(x, { [y]: y }), {}), }); const tomorrow = new Date(); @@ -1179,7 +1197,7 @@ const perspective = require("@finos/perspective"); expressions: [ `var x := "w"; var y := x; x := "y"; x;`, `var x := "z"; var y := x; x := "w"; x;`, - ], + ].reduce((x, y) => Object.assign(x, { [y]: y }), {}), }); const results = await view.to_columns(); @@ -1204,7 +1222,7 @@ const perspective = require("@finos/perspective"); expressions_common.int_float_data ); const view = await table.view({ - expressions: ['"w" + "x"'], + expressions: { '"w" + "x"': '"w" + "x"' }, }); const result = await view.to_columns(); expect(result['"w" + "x"']).toEqual([2.5, 4.5, 6.5, 8.5]); @@ -1217,7 +1235,7 @@ const perspective = require("@finos/perspective"); const view = await table.view({ expressions: [ "var x := 1 + 2;\n// another comment\nx + 3 + 4 # comment", - ], + ].reduce((x, y) => Object.assign(x, { [y]: y }), {}), }); const schema = await view.expression_schema(); expect(schema).toEqual({ @@ -1261,7 +1279,9 @@ const perspective = require("@finos/perspective"); expressions_common.int_float_data ); const view = await table.view({ - expressions: ["1 + 2 + 3 + 4 + 5 + 6"], + expressions: { + "1 + 2 + 3 + 4 + 5 + 6": "1 + 2 + 3 + 4 + 5 + 6", + }, }); const result = await view.to_columns(); expect(result["1 + 2 + 3 + 4 + 5 + 6"]).toEqual([21, 21, 21, 21]); @@ -1275,7 +1295,7 @@ const perspective = require("@finos/perspective"); ); const view = await table.view({ group_by: ["'abc'"], // checks that the strings are interned - expressions: ["'abc'"], + expressions: { "'abc'": "'abc'" }, }); const result = await view.to_columns(); expect(result["__ROW_PATH__"]).toEqual([[], ["abc"]]); @@ -1294,7 +1314,7 @@ const perspective = require("@finos/perspective"); "0 or 1", "true and true", "false or true", - ], + ].reduce((x, y) => Object.assign(x, { [y]: y }), {}), }); expect(await view.schema()).toEqual({ w: "float", @@ -1325,7 +1345,7 @@ const perspective = require("@finos/perspective"); "0 or 1", "true and true", "false or true", - ], + ].reduce((x, y) => Object.assign(x, { [y]: y }), {}), }); expect(await view.schema()).toEqual({ w: "float", @@ -1352,7 +1372,7 @@ const perspective = require("@finos/perspective"); ); const view = await table.view({ columns: ['("w" + "x") ^ 2'], - expressions: ['("w" + "x") ^ 2'], + expressions: { '("w" + "x") ^ 2': '("w" + "x") ^ 2' }, }); const result = await view.to_columns(); expect(result['("w" + "x") ^ 2']).toEqual( @@ -1374,7 +1394,7 @@ const perspective = require("@finos/perspective"); '"x" and false', '"x" or true', '"x" or false', - ], + ].reduce((x, y) => Object.assign(x, { [y]: y }), {}), }); expect(await view.schema()).toEqual({ w: "float", @@ -1412,7 +1432,7 @@ const perspective = require("@finos/perspective"); z: "boolean", }); const view = await table.view({ - expressions: ['("w" + "x") * 10'], + expressions: { '("w" + "x") * 10': '("w" + "x") * 10' }, }); const result = await view.to_columns(); @@ -1445,7 +1465,7 @@ const perspective = require("@finos/perspective"); expressions_common.int_float_data ); const view = await table.view({ - expressions: ['// new column\n"w" + "x"'], + expressions: { "new column": '"w" + "x"' }, }); const result = await view.to_columns(); expect(result["new column"]).toEqual([2.5, 4.5, 6.5, 8.5]); @@ -1458,14 +1478,14 @@ const perspective = require("@finos/perspective"); expressions_common.int_float_data ); const view = await table.view({ - expressions: ['// new column\n"w" + "x"'], + expressions: { "new column": '"w" + "x"' }, }); const result = await view.to_columns(); expect(result["new column"]).toEqual([2.5, 4.5, 6.5, 8.5]); const view2 = await table.view({ - expressions: ['// new column\n"w" - "x"'], + expressions: { "new column": '"w" - "x"' }, }); const result2 = await view2.to_columns(); @@ -1481,9 +1501,10 @@ const perspective = require("@finos/perspective"); expressions_common.int_float_data ); const view = await table.view({ - expressions: [ - '// new column with lots of different strings and names and lots of good characters\n"w" + "x"', - ], + expressions: { + "new column with lots of different strings and names and lots of good characters": + '"w" + "x"', + }, }); const result = await view.to_columns(); expect( @@ -1500,9 +1521,9 @@ const perspective = require("@finos/perspective"); expressions_common.int_float_data ); const view = await table.view({ - expressions: [ - '// alias\nvar x := "w" + "x"\n// a comment\n# another comment\n/* another comment here */\n', - ], + expressions: { + alias: 'var x := "w" + "x"\n// a comment\n# another comment\n/* another comment here */\n', + }, }); const result = await view.to_columns(); expect(result["alias"]).toEqual([2.5, 4.5, 6.5, 8.5]); @@ -1516,13 +1537,13 @@ const perspective = require("@finos/perspective"); ); const view = await table.view({ - expressions: [ - '// new column\n"w" + "x"', - '// new column 1\n"w" - "x"', - "// new column\n100 * 10", - '// new column 1\n"w" + "x"', - '// new column\n"w" - "x"', - ], + expressions: { + "new column": '"w" + "x"', + "new column 1": '"w" - "x"', + "new column": "100 * 10", + "new column 1": '"w" + "x"', + "new column": '"w" - "x"', + }, }); expect(await view.expression_schema()).toEqual({ @@ -1545,13 +1566,13 @@ const perspective = require("@finos/perspective"); ); const view = await table.view({ - expressions: [ - '// new column\n"w" + "x"', - '// new column 1\n"w" - "x"', - "// new column\n100", - "// new column\nupper('abc')", - '// new column 1\n"w" + "x"', - ], + expressions: { + "new column": '"w" + "x"', + "new column 1": '"w" - "x"', + "new column": "100", + "new column": "upper('abc')", + "new column 1": '"w" + "x"', + }, }); expect(await view.expression_schema()).toEqual({ @@ -1573,14 +1594,14 @@ const perspective = require("@finos/perspective"); expressions_common.int_float_data ); const view = await table.view({ - expressions: ['// new column\n"w" + "x"'], + expressions: { "new column": '"w" + "x"' }, }); let result = await view.to_columns(); expect(result["new column"]).toEqual([2.5, 4.5, 6.5, 8.5]); const view2 = await table.view({ - expressions: ["// new column\n100 * 10"], + expressions: { "new column": "100 * 10" }, }); expect(await view.expression_schema()).toEqual( @@ -1593,7 +1614,7 @@ const perspective = require("@finos/perspective"); expect(result2["new column"]).toEqual([1000, 1000, 1000, 1000]); const view3 = await table.view({ - expressions: ["// new column\n100 * 100"], + expressions: { "new column": "100 * 100" }, group_by: ["y"], }); @@ -1620,7 +1641,7 @@ const perspective = require("@finos/perspective"); table .view({ - expressions: ["// w\nupper('abc')"], + expressions: { w: "upper('abc')" }, }) .catch((e) => { expect(e.message).toMatch( @@ -1638,14 +1659,14 @@ const perspective = require("@finos/perspective"); expressions_common.int_float_data ); const view = await table.view({ - expressions: ['// new column\n"w" + "x"'], + expressions: { "new column": '"w" + "x"' }, }); let result = await view.to_columns(); expect(result["new column"]).toEqual([2.5, 4.5, 6.5, 8.5]); const view2 = await table.view({ - expressions: ["// new column\n upper('abc')"], + expressions: { "new column": " upper('abc')" }, }); const result2 = await view2.to_columns(); @@ -1662,7 +1683,7 @@ const perspective = require("@finos/perspective"); expressions_common.int_float_data ); const view = await table.view({ - expressions: ['"w" + "x"'], + expressions: { '"w" + "x"': '"w" + "x"' }, }); const result = await view.to_columns(); expect(result['"w" + "x"']).toEqual([2.5, 4.5, 6.5, 8.5]); @@ -1691,7 +1712,12 @@ const perspective = require("@finos/perspective"); expressions_common.int_float_data ); const view = await table.view({ - expressions: ['"w"', '"x"', '"y"', '"z"'], + expressions: { + '"w"': '"w"', + '"x"': '"x"', + '"y"': '"y"', + '"z"': '"z"', + }, }); expect(await view.expression_schema()).toEqual({ '"w"': "float", @@ -1751,7 +1777,10 @@ const perspective = require("@finos/perspective"); ); const view = await table.view({ - expressions: ['"w" + "x"', 'upper("y")'], + expressions: { + '"w" + "x"': '"w" + "x"', + 'upper("y")': 'upper("y")', + }, }); expect(await view.expression_schema()).toEqual({ @@ -1771,10 +1800,17 @@ const perspective = require("@finos/perspective"); expressions_common.int_float_data ); const view = await table.view({ - expressions: ['"w" + "x"', '"w" - "x"', '"w" * "x"'], + expressions: { + '"w" + "x"': '"w" + "x"', + '"w" - "x"': '"w" - "x"', + '"w" * "x"': '"w" * "x"', + }, }); const view2 = await table.view({ - expressions: ['"w" / "x"', '"x" * "w"'], + expressions: { + '"w" / "x"': '"w" / "x"', + '"x" * "w"': '"x" * "w"', + }, }); const schema = await view.schema(); @@ -1831,10 +1867,10 @@ const perspective = require("@finos/perspective"); expressions_common.int_float_data ); const view = await table.view({ - expressions: ['"w" + "x"'], + expressions: { '"w" + "x"': '"w" + "x"' }, }); const view2 = await table.view({ - expressions: ['"w" - "x"'], + expressions: { '"w" - "x"': '"w" - "x"' }, }); const schema = await view.schema(); @@ -1890,11 +1926,14 @@ const perspective = require("@finos/perspective"); expressions_common.int_float_data ); const view = await table.view({ - expressions: ['"w" + "x"'], + expressions: { '"w" + "x"': '"w" + "x"' }, }); const view2 = await table.view({ - expressions: ['"w" + "x"', '"w" - "x"'], + expressions: { + '"w" + "x"': '"w" + "x"', + '"w" - "x"': '"w" - "x"', + }, }); const schema = await view.schema(); @@ -1953,7 +1992,7 @@ const perspective = require("@finos/perspective"); ); const view = await table.view({ - expressions: ['"w" + "x"'], + expressions: { '"w" + "x"': '"w" + "x"' }, }); expect(await view.schema()).toEqual({ @@ -1965,7 +2004,10 @@ const perspective = require("@finos/perspective"); }); const view2 = await table.view({ - expressions: ['"w" + "x"', '"w" - "x"'], + expressions: { + '"w" + "x"': '"w" + "x"', + '"w" - "x"': '"w" - "x"', + }, }); expect(await view2.schema()).toEqual({ @@ -1978,7 +2020,11 @@ const perspective = require("@finos/perspective"); }); const view3 = await table.view({ - expressions: ['"w" + "x"', '"w" - "x"', '("w" - "x") + "x"'], + expressions: { + '"w" + "x"': '"w" + "x"', + '"w" - "x"': '"w" - "x"', + '("w" - "x") + "x"': '("w" - "x") + "x"', + }, }); expect(await view3.schema()).toEqual({ @@ -2005,7 +2051,7 @@ const perspective = require("@finos/perspective"); expressions_common.int_float_data ); const view = await table.view({ - expressions: ['"w" + "x"'], + expressions: { '"w" + "x"': '"w" + "x"' }, }); const result = await view.to_columns(); @@ -2013,7 +2059,7 @@ const perspective = require("@finos/perspective"); view.delete(); const view2 = await table.view({ - expressions: ['"w" - "x"'], + expressions: { '"w" - "x"': '"w" - "x"' }, }); const result2 = await view2.to_columns(); @@ -2028,7 +2074,7 @@ const perspective = require("@finos/perspective"); expressions_common.int_float_data ); const view = await table.view({ - expressions: ['"w" + "x"'], + expressions: { '"w" + "x"': '"w" + "x"' }, }); const result = await view.to_columns(); @@ -2054,12 +2100,24 @@ const perspective = require("@finos/perspective"); const table = await perspective.table( expressions_common.int_float_data ); - const view = await table.view({ expressions: [expressions[0]] }); + const view = await table.view({ + expressions: [expressions[0]].reduce( + (x, y) => Object.assign(x, { [y]: y }), + {} + ), + }); const view2 = await table.view({ - expressions: [expressions[0], expressions[1]], + expressions: [expressions[0], expressions[1]].reduce( + (x, y) => Object.assign(x, { [y]: y }), + {} + ), }); const view3 = await table.view({ - expressions: [expressions[0], expressions[1], expressions[2]], + expressions: [ + expressions[0], + expressions[1], + expressions[2], + ].reduce((x, y) => Object.assign(x, { [y]: y }), {}), }); const view4 = await table.view({ expressions: expressions }); @@ -2118,10 +2176,10 @@ const perspective = require("@finos/perspective"); expressions_common.int_float_data ); const view = await table.view({ - expressions: ['"w" + "x"'], + expressions: { '"w" + "x"': '"w" + "x"' }, }); const view2 = await table.view({ - expressions: ['"w" + "x"'], + expressions: { '"w" + "x"': '"w" + "x"' }, }); const schema = await view.schema(); @@ -2194,7 +2252,7 @@ const perspective = require("@finos/perspective"); expressions_common.int_float_data ); const view = await table.view({ - expressions: ['"w" + "x"'], + expressions: { '"w" + "x"': '"w" + "x"' }, }); const result = await view.to_columns(); expect(result['"w" + "x"']).toEqual([2.5, 4.5, 6.5, 8.5]); @@ -2218,7 +2276,7 @@ const perspective = require("@finos/perspective"); expressions_common.int_float_data ); const view = await table.view({ - expressions: ['"w" + "x"'], + expressions: { '"w" + "x"': '"w" + "x"' }, }); const result = await view.to_columns(); expect(result['"w" + "x"']).toEqual([2.5, 4.5, 6.5, 8.5]); @@ -2238,7 +2296,7 @@ const perspective = require("@finos/perspective"); ); const view = await table.view({ columns: ['"w" + "x"'], - expressions: ['"w" + "x"'], + expressions: { '"w" + "x"': '"w" + "x"' }, }); const result = await view.to_columns(); expect(result['"w" + "x"']).toEqual([2.5, 4.5, 6.5, 8.5]); @@ -2252,7 +2310,7 @@ const perspective = require("@finos/perspective"); ); const view = await table.view({ columns: ["x"], - expressions: ['"w" + "x"'], + expressions: { '"w" + "x"': '"w" + "x"' }, }); expect(await view.schema()).toEqual({ x: "integer", @@ -2273,7 +2331,7 @@ const perspective = require("@finos/perspective"); ); const view = await table.view({ group_by: ["w"], - expressions: ['"w" + "x"'], + expressions: { '"w" + "x"': '"w" + "x"' }, }); const result = await view.to_columns(); expect(result).toEqual({ @@ -2294,7 +2352,7 @@ const perspective = require("@finos/perspective"); ); const view = await table.view({ group_by: ['"w" + "x"'], - expressions: ['"w" + "x"'], + expressions: { '"w" + "x"': '"w" + "x"' }, }); const result = await view.to_columns(); expect(result).toEqual({ @@ -2317,7 +2375,7 @@ const perspective = require("@finos/perspective"); // default order let view = await table.view({ group_by: ["y"], - expressions: ['// column\n"w" + "x"'], + expressions: { column: '"w" + "x"' }, }); let paths = await view.column_paths(); @@ -2343,7 +2401,7 @@ const perspective = require("@finos/perspective"); output.unshift("__ROW_PATH__"); view = await table.view({ group_by: ["y"], - expressions: ['// column\n"w" + "x"'], + expressions: { column: '"w" + "x"' }, columns: expected, }); paths = await view.column_paths(); @@ -2356,7 +2414,7 @@ const perspective = require("@finos/perspective"); output.unshift("__ROW_PATH__"); view = await table.view({ group_by: ["column"], - expressions: ['// column\n"w" + "x"'], + expressions: { column: '"w" + "x"' }, columns: expected, }); paths = await view.column_paths(); @@ -2373,7 +2431,7 @@ const perspective = require("@finos/perspective"); ); const config = { group_by: ["y"], - expressions: ["1234"], + expressions: { 1234: "1234" }, aggregates: { x: "sum", y: "count", @@ -2427,7 +2485,7 @@ const perspective = require("@finos/perspective"); ); const view = await table.view({ split_by: ['"w" + "x"'], - expressions: ['"w" + "x"'], + expressions: { '"w" + "x"': '"w" + "x"' }, }); const result = await view.to_columns(); expect(result).toEqual({ @@ -2463,7 +2521,7 @@ const perspective = require("@finos/perspective"); const view = await table.view({ group_by: ['"w" + "x"'], split_by: ['"w" + "x"'], - expressions: ['"w" + "x"'], + expressions: { '"w" + "x"': '"w" + "x"' }, }); const result = await view.to_columns(); expect(result).toEqual({ @@ -2505,7 +2563,7 @@ const perspective = require("@finos/perspective"); '"x" + "z"': "median", x: "median", }, - expressions: ['"x" + "z"'], + expressions: { '"x" + "z"': '"x" + "z"' }, }); const result = await view.to_columns(); expect(result).toEqual({ @@ -2531,7 +2589,7 @@ const perspective = require("@finos/perspective"); aggregates: { '"x" + "z"': ["weighted mean", "y"], }, - expressions: ['"x" + "z"'], + expressions: { '"x" + "z"': '"x" + "z"' }, }); const result = await view.to_columns(); expect(result).toEqual({ @@ -2554,7 +2612,7 @@ const perspective = require("@finos/perspective"); '"x"': "median", x: "median", }, - expressions: ['"x"'], + expressions: { '"x"': '"x"' }, }); const result = await view.to_columns(); expect(result).toEqual({ @@ -2578,7 +2636,7 @@ const perspective = require("@finos/perspective"); aggregates: { 'upper("x")': "distinct count", }, - expressions: ['upper("x")'], + expressions: { 'upper("x")': 'upper("x")' }, }); const result = await view.to_columns(); expect(result).toEqual({ @@ -2604,7 +2662,7 @@ const perspective = require("@finos/perspective"); aggregates: { "bucket(\"x\", 'M')": "distinct count", }, - expressions: [`bucket("x", 'M')`], + expressions: { "bucket(\"x\", 'M')": "bucket(\"x\", 'M')" }, }); const result = await view.to_columns(); expect(result).toEqual({ @@ -2628,7 +2686,7 @@ const perspective = require("@finos/perspective"); x: ["weighted mean", '"x" + "z"'], '"x" + "z"': ["weighted mean", "y"], }, - expressions: ['"x" + "z"'], + expressions: { '"x" + "z"': '"x" + "z"' }, }); const result = await view.to_columns(); expect(result).toEqual({ @@ -2648,7 +2706,7 @@ const perspective = require("@finos/perspective"); ); const view = await table.view({ filter: [['"w" + "x"', ">", 6.5]], - expressions: ['"w" + "x"'], + expressions: { '"w" + "x"': '"w" + "x"' }, }); const result = await view.to_columns(); expect(result).toEqual({ @@ -2668,7 +2726,7 @@ const perspective = require("@finos/perspective"); ); const view = await table.view({ sort: [['"w" + "x"', "desc"]], - expressions: ['"w" + "x"'], + expressions: { '"w" + "x"': '"w" + "x"' }, }); const result = await view.to_columns(); expect(result).toEqual({ @@ -2689,7 +2747,7 @@ const perspective = require("@finos/perspective"); const view = await table.view({ columns: ["w"], sort: [['"w" + "x"', "desc"]], - expressions: ['"w" + "x"'], + expressions: { '"w" + "x"': '"w" + "x"' }, }); const result = await view.to_columns(); expect(result).toEqual({ @@ -2737,13 +2795,13 @@ const perspective = require("@finos/perspective"); c: ["a", "b", "c", "d"], d: [1.5, 2.5, 3.5, 4.5], }); - const expressions = [ - "// abc\n 123 + 345", //valid - "// a\n sqrt(144)", // invalid - "// b\n upper('abc')", // invalid - "// c\n concat('a', 'b')", // invalid - "// d\n today()", // invalid - ]; + const expressions = { + abc: " 123 + 345", //valid + a: " sqrt(144)", // invalid + b: " upper('abc')", // invalid + c: " concat('a', 'b')", // invalid + d: " today()", // invalid + }; const results = await table.validate_expressions(expressions); expect(results.expression_schema).toEqual({ abc: "float", @@ -2760,16 +2818,16 @@ const perspective = require("@finos/perspective"); }); const view = await table.view({ - expressions: [ - "// abc\n 123 + 345", - "//def \n lower('ABC')", - ], + expressions: { + abc: " 123 + 345", + def: " lower('ABC')", + }, }); - const results = await table.validate_expressions([ - "// abc\n upper('abc')", - "// def\n 1 + 2", - ]); + const results = await table.validate_expressions({ + abc: " upper('abc')", + def: " 1 + 2", + }); expect(results.expression_schema).toEqual({ abc: "string", def: "float", @@ -2786,19 +2844,19 @@ const perspective = require("@finos/perspective"); c: ["a", "b", "c", "d"], d: [1.5, 2.5, 3.5, 4.5], }); - const expressions = [ - '// abc \n"a" + "d"', // valid - '// def\n"c" + "b"', // invalid - '"c"', // valid - "// new column \nbucket(\"b\", 'M')", // valid - "bucket(\"b\", 'abcde')", // invalid - 'concat("c", "a")', // invalid - "// more columns\nconcat(\"c\", ' ', \"c\", 'abc')", // valid - 'upper("c")', // valid - 'lower("a")', // invalid, - 'min("a", "c")', // invalid, - 'max(100, "a")', // valid - ]; + const expressions = { + abc: '"a" + "d"', // valid + def: '"c" + "b"', // invalid + '"c"': '"c"', // valid + "new column": "bucket(\"b\", 'M')", // valid + "bucket(\"b\", 'abcde')": "bucket(\"b\", 'abcde')", // invalid + 'concat("c", "a")': 'concat("c", "a")', // invalid + "more columns": "concat(\"c\", ' ', \"c\", 'abc')", // valid + 'upper("c")': 'upper("c")', // valid + 'lower("a")': 'lower("a")', // invalid, + 'min("a", "c")': 'min("a", "c")', // invalid, + 'max(100, "a")': 'max(100, "a")', // valid + }; const results = await table.validate_expressions(expressions); expect(results.expression_schema).toEqual({ abc: "float", @@ -2809,24 +2867,10 @@ const perspective = require("@finos/perspective"); "more columns": "string", }); - let i = 0; - for (const alias of [ - "abc", - "def", - expressions[2], - "new column", - expressions[4], - expressions[5], - "more columns", - expressions[7], - expressions[8], - expressions[9], - expressions[10], - ]) { + for (const alias of Object.keys(expressions)) { expect(results.expression_alias[alias]).toEqual( - expressions[i] + expressions[alias] ); - i++; } table.delete(); }); @@ -2939,7 +2983,7 @@ const perspective = require("@finos/perspective"); 'upper("c")', `bucket("b", 'M')`, `bucket("b", 's')`, - ], + ].reduce((x, y) => Object.assign(x, { [y]: y }), {}), }); const schema = await view.schema(); expect(schema).toEqual({ @@ -2975,7 +3019,7 @@ const perspective = require("@finos/perspective"); 'upper("c")', `bucket("b", 'M')`, `bucket("b", 's')`, - ], + ].reduce((x, y) => Object.assign(x, { [y]: y }), {}), }); const schema = await view.expression_schema(); expect(schema).toEqual({ @@ -3013,7 +3057,7 @@ const perspective = require("@finos/perspective"); `bucket("b",'s')`, `bucket("b", 'M')`, `bucket("b", 's')`, - ], + ].reduce((x, y) => Object.assign(x, { [y]: y }), {}), }); const schema = await view.expression_schema(); expect(schema).toEqual({ @@ -3051,7 +3095,7 @@ const perspective = require("@finos/perspective"); 'upper("c")', `bucket("b", 'M')`, `bucket("b", 's')`, - ], + ].reduce((x, y) => Object.assign(x, { [y]: y }), {}), }); const schema = await view.expression_schema(); expect(schema).toEqual({ @@ -3090,7 +3134,7 @@ const perspective = require("@finos/perspective"); 'upper("c")', `bucket("b", 'M')`, `bucket("b", 's')`, - ], + ].reduce((x, y) => Object.assign(x, { [y]: y }), {}), }); const schema = await view.expression_schema(); expect(schema).toEqual({ @@ -3125,7 +3169,7 @@ const perspective = require("@finos/perspective"); 'upper("c")', `bucket("b", 'M')`, `bucket("b", 's')`, - ], + ].reduce((x, y) => Object.assign(x, { [y]: y }), {}), }); const schema = await view.expression_schema(); expect(schema).toEqual({ @@ -3165,7 +3209,7 @@ const perspective = require("@finos/perspective"); 'upper("c")', `bucket("b", 'M')`, `bucket("b", 's')`, - ], + ].reduce((x, y) => Object.assign(x, { [y]: y }), {}), }); const schema = await view.expression_schema(); expect(schema).toEqual({ @@ -3189,7 +3233,10 @@ const perspective = require("@finos/perspective"); }); const expr = "vlookup('b', integer(0))"; const view = await table.view({ - expressions: [expr], + expressions: [expr].reduce( + (x, y) => Object.assign(x, { [y]: y }), + {} + ), }); const result = await view.to_columns(); expect(result).toEqual({ @@ -3207,7 +3254,7 @@ const perspective = require("@finos/perspective"); b: ["a", "b", "c", "d"], }); const view = await table.view({ - expressions: ["col('b')"], + expressions: { "col('b')": "col('b')" }, }); const result = await view.to_columns(); expect(result).toEqual({ @@ -3224,7 +3271,7 @@ const perspective = require("@finos/perspective"); a: [1, 2, 3, 4], }); const view = await table.view({ - expressions: ["index()"], + expressions: { "index()": "index()" }, }); const result = await view.to_columns(); expect(result).toEqual({ @@ -3243,7 +3290,7 @@ const perspective = require("@finos/perspective"); { index: "a" } ); const view = await table.view({ - expressions: ["index()"], + expressions: { "index()": "index()" }, }); const result = await view.to_columns(); expect(result).toEqual({ @@ -3260,16 +3307,16 @@ const perspective = require("@finos/perspective"); b: [1, 2, 3, 4], }); - const str_exprs = [ - '// x\n"a"', - '// y\n"b" * 0.5', - "// c\n'abcdefg'", - "// d\ntrue and false", - '// e\nfloat("a") > 2 ? null : 1', - "// f\ntoday()", - "// g\nnow()", - "// h\nlength(123)", - ]; + const str_exprs = { + x: '"a"', + y: '"b" * 0.5', + c: "'abcdefg'", + d: "true and false", + e: 'float("a") > 2 ? null : 1', + f: "today()", + g: "now()", + h: "length(123)", + }; const dict_exprs = { x: '"a"', @@ -3291,8 +3338,8 @@ const perspective = require("@finos/perspective"); for (let [key, val] of Object.entries(vstrs.expression_alias)) { let dict_val = vdicts.expression_alias[key]; - let parsed_val = `// ${key}\n${dict_val}`; - expect(val).toEqual(parsed_val); + // let parsed_val = `${dict_val}`; + expect(val).toEqual(dict_val); } }); }); diff --git a/packages/perspective/test/js/expressions/invariant.js b/packages/perspective/test/js/expressions/invariant.js index 350c2dd762..3dbec14998 100644 --- a/packages/perspective/test/js/expressions/invariant.js +++ b/packages/perspective/test/js/expressions/invariant.js @@ -42,7 +42,7 @@ const generator = function (length = 100, has_zero = true) { const table = await perspective.table(data); const view = await table.view({ - expressions: ['("a" - "b") + "b"'], + expressions: { '("a" - "b") + "b"': '("a" - "b") + "b"' }, }); const result = await view.to_columns(); const expected = array_equals( @@ -58,7 +58,9 @@ const generator = function (length = 100, has_zero = true) { const table = await perspective.table(data); const view = await table.view({ - expressions: ['("a" + "b") - "a" - "b"'], + expressions: { + '("a" + "b") - "a" - "b"': '("a" + "b") - "a" - "b"', + }, }); const result = await view.to_columns(); const expected = array_equals( @@ -77,7 +79,10 @@ const generator = function (length = 100, has_zero = true) { const table = await perspective.table(data); const view = await table.view({ - expressions: ['("a" + "b") - ("a" + "b")'], + expressions: { + '("a" + "b") - ("a" + "b")': + '("a" + "b") - ("a" + "b")', + }, }); const result = await view.to_columns(); const expected = array_equals( @@ -94,7 +99,7 @@ const generator = function (length = 100, has_zero = true) { const table = await perspective.table(data); const view = await table.view({ - expressions: ['"a" - "a"'], + expressions: { '"a" - "a"': '"a" - "a"' }, }); const result = await view.to_columns(); const expected = array_equals( @@ -110,7 +115,7 @@ const generator = function (length = 100, has_zero = true) { const table = await perspective.table(data); const view = await table.view({ - expressions: ['"a" / "a"'], + expressions: { '"a" / "a"': '"a" / "a"' }, }); const result = await view.to_columns(); const expected = array_equals( @@ -126,7 +131,9 @@ const generator = function (length = 100, has_zero = true) { const table = await perspective.table(data); const view = await table.view({ - expressions: ['("a" + "a") - "a" - "a"'], + expressions: { + '("a" + "a") - "a" - "a"': '("a" + "a") - "a" - "a"', + }, }); const result = await view.to_columns(); const expected = array_equals( @@ -145,7 +152,9 @@ const generator = function (length = 100, has_zero = true) { const table = await perspective.table(data); const view = await table.view({ - expressions: ['sqrt(pow("a", 2))'], + expressions: { + 'sqrt(pow("a", 2))': 'sqrt(pow("a", 2))', + }, }); const result = await view.to_columns(); const expected = array_equals( @@ -165,7 +174,7 @@ const generator = function (length = 100, has_zero = true) { // const table = await perspective.table(data); // const view = await table.view({ - // expressions: ['pow("a", 2)', '"a" * "a"'], + // expressions: ['pow("a", 2)', '"a" * "a"'].reduce((x, y) => Object.assign(x, {[y]: y}), {}), // }); // const result = await view.to_columns(); // const expected = array_equals( @@ -185,7 +194,9 @@ const generator = function (length = 100, has_zero = true) { const table = await perspective.table(data); const view = await table.view({ - expressions: ['percent_of("a", "a")'], + expressions: { + 'percent_of("a", "a")': 'percent_of("a", "a")', + }, }); const result = await view.to_columns(); const expected = array_equals( @@ -205,7 +216,7 @@ const generator = function (length = 100, has_zero = true) { const table = await perspective.table(data); const view = await table.view({ - expressions: ['abs("a")'], + expressions: { 'abs("a")': 'abs("a")' }, }); const result = await view.to_columns(); const expected = array_equals( @@ -227,7 +238,7 @@ const generator = function (length = 100, has_zero = true) { const table = await perspective.table(data); const view = await table.view({ - expressions: ['"a" == "a"'], + expressions: { '"a" == "a"': '"a" == "a"' }, }); const result = await view.to_columns(); @@ -248,7 +259,7 @@ const generator = function (length = 100, has_zero = true) { const table = await perspective.table(data); const view = await table.view({ - expressions: ['"a" > "a"'], + expressions: { '"a" > "a"': '"a" > "a"' }, }); const result = await view.to_columns(); @@ -269,7 +280,7 @@ const generator = function (length = 100, has_zero = true) { const table = await perspective.table(data); const view = await table.view({ - expressions: ['"a" < "a"'], + expressions: { '"a" < "a"': '"a" < "a"' }, }); const result = await view.to_columns(); @@ -298,7 +309,7 @@ const generator = function (length = 100, has_zero = true) { table.update(data); const view = await table.view({ - expressions: ['"c" == "c"'], + expressions: { '"c" == "c"': '"c" == "c"' }, }); const result = await view.to_columns(); @@ -327,7 +338,7 @@ const generator = function (length = 100, has_zero = true) { table.update(data); const view = await table.view({ - expressions: ['"d" == "d"'], + expressions: { '"d" == "d"': '"d" == "d"' }, }); const result = await view.to_columns(); @@ -356,7 +367,7 @@ const generator = function (length = 100, has_zero = true) { table.update(data); const view = await table.view({ - expressions: ['"d" == "d"'], + expressions: { '"d" == "d"': '"d" == "d"' }, }); const result = await view.to_columns(); diff --git a/packages/perspective/test/js/expressions/multiple_views.spec.js b/packages/perspective/test/js/expressions/multiple_views.spec.js index 4611b06633..8d11ef5d26 100644 --- a/packages/perspective/test/js/expressions/multiple_views.spec.js +++ b/packages/perspective/test/js/expressions/multiple_views.spec.js @@ -40,10 +40,10 @@ const expressions_common = require("./common.js"); }); const v1 = await table.view({ - expressions: [`// column \n"x" + 10`], + expressions: { column: `"x" + 10` }, }); const v2 = await table.view({ - expressions: [`// column \n upper("y")`], + expressions: { column: `upper("y")` }, }); let result = await v1.to_columns(); @@ -73,11 +73,11 @@ const expressions_common = require("./common.js"); }); const v1 = await table.view({ - expressions: [`// column \n"x" * 2`], + expressions: { column: `"x" * 2` }, }); const v2 = await table.view({ - expressions: [`// column \n upper(concat("y", 'bcd'))`], + expressions: { column: `upper(concat("y", 'bcd'))` }, }); let result = await v1.to_columns(); @@ -118,12 +118,12 @@ const expressions_common = require("./common.js"); }); const v1 = await table.view({ - expressions: [`// column \n"x" * 2`], + expressions: { column: `"x" * 2` }, filter: [["column", ">", 5]], }); const v2 = await table.view({ - expressions: [`// column \n upper(concat("y", 'bcd'))`], + expressions: { column: `upper(concat("y", 'bcd'))` }, filter: [["column", "contains", "A"]], }); @@ -174,12 +174,12 @@ const expressions_common = require("./common.js"); }); const v1 = await table.view({ - expressions: [`// column \n"x" * 2`], + expressions: { column: `"x" * 2` }, sort: [["column", "desc"]], }); const v2 = await table.view({ - expressions: [`// column \n upper(concat("y", 'bcd'))`], + expressions: { column: `upper(concat("y", 'bcd'))` }, sort: [["column", "asc"]], }); @@ -226,10 +226,10 @@ const expressions_common = require("./common.js"); ); const v1 = await table.view({ - expressions: [`// column \n"x" * 2`], + expressions: { column: `"x" * 2` }, }); const v2 = await table.view({ - expressions: [`// column \n upper("y")`], + expressions: { column: `upper("y")` }, }); let result = await v1.to_columns(); @@ -271,11 +271,11 @@ const expressions_common = require("./common.js"); ); const v1 = await table.view({ - expressions: [`// column \n"x" * 2`], + expressions: { column: `"x" * 2` }, filter: [["column", "==", 8]], }); const v2 = await table.view({ - expressions: [`// column \n upper("y")`], + expressions: { column: `upper("y")` }, filter: [["column", "==", "Z"]], }); @@ -312,11 +312,11 @@ const expressions_common = require("./common.js"); ); const v1 = await table.view({ - expressions: [`// column \n"x" * 2`], + expressions: { column: `"x" * 2` }, sort: [["column", "desc"]], }); const v2 = await table.view({ - expressions: [`// column \n upper("y")`], + expressions: { column: `upper("y")` }, sort: [["column", "desc"]], }); @@ -377,10 +377,10 @@ const expressions_common = require("./common.js"); ); const v1 = await table.view({ - expressions: [`// column \n"x" * 2`], + expressions: { column: `"x" * 2` }, }); const v2 = await table.view({ - expressions: [`// column \n upper("y")`], + expressions: { column: `upper("y")` }, }); let result = await v1.to_columns(); @@ -411,10 +411,10 @@ const expressions_common = require("./common.js"); const table = await perspective.table(expressions_common.data); const v1 = await table.view({ - expressions: [`// column \n"x" + 10`], + expressions: { column: `"x" + 10` }, }); const v2 = await table.view({ - expressions: [`// column \n upper("y")`], + expressions: { column: `upper("y")` }, }); let result = await v1.to_columns(); @@ -451,11 +451,11 @@ const expressions_common = require("./common.js"); const table = await perspective.table(expressions_common.data); const v1 = await table.view({ - expressions: [`// column \n"x" * 2`], + expressions: { column: `"x" * 2` }, }); const v2 = await table.view({ - expressions: [`// column \n upper(concat("y", 'bcd'))`], + expressions: { column: `upper(concat("y", 'bcd'))` }, }); let result = await v1.to_columns(); @@ -497,12 +497,12 @@ const expressions_common = require("./common.js"); const table = await perspective.table(expressions_common.data); const v1 = await table.view({ - expressions: [`// column \n"x" * 2`], + expressions: { column: `"x" * 2` }, filter: [["column", ">", 5]], }); const v2 = await table.view({ - expressions: [`// column \n upper(concat("y", 'bcd'))`], + expressions: { column: `upper(concat("y", 'bcd'))` }, filter: [["column", "contains", "A"]], }); @@ -538,12 +538,12 @@ const expressions_common = require("./common.js"); const table = await perspective.table(expressions_common.data); const v1 = await table.view({ - expressions: [`// column \n"x" * 2`], + expressions: { column: `"x" * 2` }, sort: [["column", "desc"]], }); const v2 = await table.view({ - expressions: [`// column \n upper(concat("y", 'bcd'))`], + expressions: { column: `upper(concat("y", 'bcd'))` }, sort: [["column", "asc"]], }); @@ -585,10 +585,10 @@ const expressions_common = require("./common.js"); }); const v1 = await table.view({ - expressions: [`// column \n"x" * 2`], + expressions: { column: `"x" * 2` }, }); const v2 = await table.view({ - expressions: [`// column \n upper("y")`], + expressions: { column: `upper("y")` }, }); let result = await v1.to_columns(); @@ -626,11 +626,11 @@ const expressions_common = require("./common.js"); }); const v1 = await table.view({ - expressions: [`// column \n"x" * 2`], + expressions: { column: `"x" * 2` }, filter: [["column", "==", 8]], }); const v2 = await table.view({ - expressions: [`// column \n upper("y")`], + expressions: { column: `upper("y")` }, filter: [["column", "==", "B"]], }); @@ -664,11 +664,11 @@ const expressions_common = require("./common.js"); }); const v1 = await table.view({ - expressions: [`// column \n"x" * 2`], + expressions: { column: `"x" * 2` }, sort: [["column", "desc"]], }); const v2 = await table.view({ - expressions: [`// column \n upper("y")`], + expressions: { column: `upper("y")` }, sort: [["column", "desc"]], }); @@ -726,10 +726,10 @@ const expressions_common = require("./common.js"); }); const v1 = await table.view({ - expressions: [`// column \n"x" * 2`], + expressions: { column: `"x" * 2` }, }); const v2 = await table.view({ - expressions: [`// column \n upper("y")`], + expressions: { column: `upper("y")` }, }); let result = await v1.to_columns(); @@ -762,11 +762,11 @@ const expressions_common = require("./common.js"); const table = await perspective.table(expressions_common.data); const v1 = await table.view({ - expressions: [`// column \n"x" + 10`], + expressions: { column: `"x" + 10` }, }); const v2 = await table.view({ - expressions: [`// column \n upper("y")`], + expressions: { column: `upper("y")` }, }); const result = await v1.to_columns(); @@ -833,11 +833,11 @@ const expressions_common = require("./common.js"); }); const v1 = await table.view({ - expressions: [`// column \n"x" * 2`], + expressions: { column: `"x" * 2` }, }); const v2 = await table.view({ - expressions: [`// column \n upper("y")`], + expressions: { column: `upper("y")` }, }); const result = await v1.to_columns(); @@ -912,11 +912,11 @@ const expressions_common = require("./common.js"); }); const v1 = await table.view({ - expressions: [`// column \n"x" * 2`], + expressions: { column: `"x" * 2` }, sort: [["column", "desc"]], }); const v2 = await table.view({ - expressions: [`// column \n upper("y")`], + expressions: { column: `upper("y")` }, sort: [["column", "desc"]], }); @@ -998,17 +998,17 @@ const expressions_common = require("./common.js"); const v1 = await table.view({ columns: ["column", "column2"], - expressions: [ - `// column \n"x" + 10`, - `// column2 \n concat('a', 'b', 'c')`, - ], + expressions: { + [`column`]: `"x" + 10`, + [`column2`]: `concat('a', 'b', 'c')`, + }, }); const v2 = await table.view({ columns: ["column2", "column"], - expressions: [ - `// column \n upper("y")`, - `// column2 \n bucket("z", 'Y')`, - ], + expressions: { + [`column`]: `upper("y")`, + [`column2`]: `bucket("z", 'Y')`, + }, }); expect(await v1.expression_schema()).toEqual({ @@ -1056,17 +1056,17 @@ const expressions_common = require("./common.js"); const v1 = await table.view({ columns: ["column", "column2"], - expressions: [ - `// column \n"x" + 10`, - `// column2 \n concat('a', 'b', 'c')`, - ], + expressions: { + [`column`]: `"x" + 10`, + [`column2`]: `concat('a', 'b', 'c')`, + }, }); const v2 = await table.view({ columns: ["column2", "column"], - expressions: [ - `// column \n upper("y")`, - `// column2 \n bucket("z", 'Y')`, - ], + expressions: { + [`column`]: `upper("y")`, + [`column2`]: `bucket("z", 'Y')`, + }, }); expect(await v1.expression_schema()).toEqual({ @@ -1125,17 +1125,17 @@ const expressions_common = require("./common.js"); const v1 = await table.view({ columns: ["column", "column2"], - expressions: [ - `// column \n"x" + 10`, - `// column2 \n concat('a', 'b', 'c')`, - ], + expressions: { + [`column`]: `"x" + 10`, + [`column2`]: `concat('a', 'b', 'c')`, + }, }); const v2 = await table.view({ columns: ["column2", "column"], - expressions: [ - `// column \n upper("y")`, - `// column2 \n bucket("z", 'Y')`, - ], + expressions: { + [`column`]: `upper("y")`, + [`column2`]: `bucket("z", 'Y')`, + }, }); expect(await v1.expression_schema()).toEqual({ @@ -1188,17 +1188,17 @@ const expressions_common = require("./common.js"); const v1 = await table.view({ columns: ["column", "column2"], - expressions: [ - `// column \n"x" + 10`, - `// column2 \n concat('a', 'b', 'c')`, - ], + expressions: { + [`column`]: `"x" + 10`, + [`column2`]: `concat('a', 'b', 'c')`, + }, }); const v2 = await table.view({ columns: ["column2", "column"], - expressions: [ - `// column \n upper("y")`, - `// column2 \n bucket("z", 'Y')`, - ], + expressions: { + [`column`]: `upper("y")`, + [`column2`]: `bucket("z", 'Y')`, + }, }); expect(await v1.expression_schema()).toEqual({ @@ -1228,10 +1228,10 @@ const expressions_common = require("./common.js"); const table = await perspective.table(expressions_common.data); const v1 = await table.view({ - expressions: [`// column \n"x" + 10`], + expressions: { column: `"x" + 10` }, }); const v2 = await table.view({ - expressions: [`// column \n upper("y")`], + expressions: { column: `upper("y")` }, }); let result = await v1.to_columns(); @@ -1265,11 +1265,11 @@ const expressions_common = require("./common.js"); const table = await perspective.table(expressions_common.data); const v1 = await table.view({ - expressions: [`// column \n"x" + 10`], + expressions: { column: `"x" + 10` }, filter: [["column", "==", 12]], }); const v2 = await table.view({ - expressions: [`// column \n upper("y")`], + expressions: { column: `upper("y")` }, filter: [["column", "==", "D"]], }); @@ -1290,12 +1290,12 @@ const expressions_common = require("./common.js"); const v1 = await table.view({ group_by: ["y"], split_by: ["x"], - expressions: [`// column \n"x" + 10`], + expressions: { column: `"x" + 10` }, }); const v2 = await table.view({ group_by: ["x"], split_by: ["y"], - expressions: [`// column \n upper("y")`], + expressions: { column: `upper("y")` }, aggregates: { column: "last", }, @@ -1328,7 +1328,7 @@ const expressions_common = require("./common.js"); const v1 = await table.view({ group_by: ["x"], - expressions: [`// column \n"z" + 10`], + expressions: { column: `"z" + 10` }, aggregates: { column: "avg", }, @@ -1336,7 +1336,7 @@ const expressions_common = require("./common.js"); const v2 = await table.view({ group_by: ["x"], - expressions: [`// column \n upper("y")`], + expressions: { column: `upper("y")` }, aggregates: { column: "last", }, @@ -1344,7 +1344,7 @@ const expressions_common = require("./common.js"); const v3 = await table.view({ group_by: ["x"], - expressions: [`// column \n 2 * "z"`], + expressions: { column: `2 * "z"` }, aggregates: { column: ["weighted mean", "z"], }, @@ -1374,7 +1374,7 @@ const expressions_common = require("./common.js"); const v1 = await table.view({ group_by: ["x"], split_by: ["y"], - expressions: [`// column \n"z" + 10`], + expressions: { column: `"z" + 10` }, aggregates: { column: "avg", }, @@ -1383,7 +1383,7 @@ const expressions_common = require("./common.js"); const v2 = await table.view({ group_by: ["x"], split_by: ["y"], - expressions: [`// column \n upper("y")`], + expressions: { column: `upper("y")` }, aggregates: { column: "last", }, @@ -1392,7 +1392,7 @@ const expressions_common = require("./common.js"); const v3 = await table.view({ group_by: ["x"], split_by: ["y"], - expressions: [`// column \n 2 * "z"`], + expressions: { column: `2 * "z"` }, aggregates: { column: ["weighted mean", "z"], }, diff --git a/packages/perspective/test/js/expressions/numeric.spec.js b/packages/perspective/test/js/expressions/numeric.spec.js index ecf3880bc9..bed7369c55 100644 --- a/packages/perspective/test/js/expressions/numeric.spec.js +++ b/packages/perspective/test/js/expressions/numeric.spec.js @@ -151,7 +151,7 @@ function validate_binary_operations(output, expressions, operator) { }); const view = await table.view({ - expressions: ['"a"'], + expressions: { '"a"': '"a"' }, }); table.update({ @@ -177,7 +177,7 @@ function validate_binary_operations(output, expressions, operator) { a: "sum", b: "sum", }, - expressions: ['"a"'], + expressions: { '"a"': '"a"' }, }); table.update({ @@ -564,7 +564,10 @@ function validate_binary_operations(output, expressions, operator) { d: "float", }); const view = await table.view({ - expressions: ['"a" == "b"', '"a" != "b"'], + expressions: { + '"a" == "b"': '"a" == "b"', + '"a" != "b"': '"a" != "b"', + }, }); table.update({ a: [1, 2, 3, 4], @@ -602,7 +605,7 @@ function validate_binary_operations(output, expressions, operator) { '"a" != "c"', '"b" == "d"', '"b" != "d"', - ], + ].reduce((x, y) => Object.assign(x, { [y]: y }), {}), }); table.update({ a: [1, 2, 3, 4], @@ -647,7 +650,10 @@ function validate_binary_operations(output, expressions, operator) { }); const view = await table.view({ - expressions: ['"a" / "b"', '"c" / "b"'], + expressions: { + '"a" / "b"': '"a" / "b"', + '"c" / "b"': '"c" / "b"', + }, }); const result = await view.to_columns(); @@ -675,7 +681,10 @@ function validate_binary_operations(output, expressions, operator) { }); const view = await table.view({ - expressions: ['"a" % "b"', '"c" % "b"'], + expressions: { + '"a" % "b"': '"a" % "b"', + '"c" % "b"': '"c" % "b"', + }, }); const result = await view.to_columns(); @@ -803,7 +812,9 @@ function validate_binary_operations(output, expressions, operator) { // works for floats only const view = await table.view({ - expressions: ['inrange(9, "b", 20)'], + expressions: { + 'inrange(9, "b", 20)': 'inrange(9, "b", 20)', + }, }); table.update({ @@ -829,7 +840,10 @@ function validate_binary_operations(output, expressions, operator) { }); const view = await table.view({ - expressions: ['iclamp(10, "a", 20)', 'iclamp(10, "b", 20)'], + expressions: { + 'iclamp(10, "a", 20)': 'iclamp(10, "a", 20)', + 'iclamp(10, "b", 20)': 'iclamp(10, "b", 20)', + }, }); table.update({ @@ -853,7 +867,10 @@ function validate_binary_operations(output, expressions, operator) { }); const view = await table.view({ - expressions: ['pow("a", 1)', 'pow("b", 3)'], + expressions: { + 'pow("a", 1)': 'pow("a", 1)', + 'pow("b", 3)': 'pow("b", 3)', + }, }); table.update({ @@ -877,7 +894,10 @@ function validate_binary_operations(output, expressions, operator) { }); const view = await table.view({ - expressions: ['logn("a", 5)', 'logn("b", 3)'], + expressions: { + 'logn("a", 5)': 'logn("a", 5)', + 'logn("b", 3)': 'logn("b", 3)', + }, }); table.update({ @@ -906,7 +926,10 @@ function validate_binary_operations(output, expressions, operator) { }); const view = await table.view({ - expressions: ['root("a", 5)', 'root("b", 3)'], + expressions: { + 'root("a", 5)': 'root("a", 5)', + 'root("b", 3)': 'root("b", 3)', + }, }); table.update({ @@ -943,7 +966,7 @@ function validate_binary_operations(output, expressions, operator) { expressions: [ 'avg("a", 10, 20, 30, 40, "a")', 'avg("b", 3, 4, 5, "b")', - ], + ].reduce((x, y) => Object.assign(x, { [y]: y }), {}), }); table.update({ @@ -972,7 +995,7 @@ function validate_binary_operations(output, expressions, operator) { expressions: [ 'sum("a", 10, 20, 30, 40, "a")', 'sum("b", 3, 4, 5, "b")', - ], + ].reduce((x, y) => Object.assign(x, { [y]: y }), {}), }); table.update({ @@ -998,7 +1021,10 @@ function validate_binary_operations(output, expressions, operator) { }); const view = await table.view({ - expressions: ['trunc("a")', 'trunc("b")'], + expressions: { + 'trunc("a")': 'trunc("a")', + 'trunc("b")': 'trunc("b")', + }, }); table.update({ @@ -1020,7 +1046,10 @@ function validate_binary_operations(output, expressions, operator) { }); const view = await table.view({ - expressions: ['deg2rad("a")', 'deg2rad("b")'], + expressions: { + 'deg2rad("a")': 'deg2rad("a")', + 'deg2rad("b")': 'deg2rad("b")', + }, }); table.update({ @@ -1048,7 +1077,10 @@ function validate_binary_operations(output, expressions, operator) { }); const view = await table.view({ - expressions: ['rad2deg("a")', 'rad2deg("b")'], + expressions: { + 'rad2deg("a")': 'rad2deg("a")', + 'rad2deg("b")': 'rad2deg("b")', + }, }); table.update({ @@ -1081,7 +1113,7 @@ function validate_binary_operations(output, expressions, operator) { 'is_null("b")', 'if(is_null("a")) 100; else 0;', 'if(is_null("b")) 100; else 0;', - ], + ].reduce((x, y) => Object.assign(x, { [y]: y }), {}), }); table.update({ @@ -1124,7 +1156,7 @@ function validate_binary_operations(output, expressions, operator) { 'is_not_null("b")', 'if(is_not_null("a")) 100; else 0;', 'if(is_not_null("b")) 100; else 0;', - ], + ].reduce((x, y) => Object.assign(x, { [y]: y }), {}), }); table.update({ @@ -1166,7 +1198,7 @@ function validate_binary_operations(output, expressions, operator) { 'percent_of("a", 500)', 'percent_of("a", "b")', "percent_of(1, 3)", - ], + ].reduce((x, y) => Object.assign(x, { [y]: y }), {}), }); table.update({ @@ -1203,7 +1235,7 @@ function validate_binary_operations(output, expressions, operator) { 'bucket("a", 5)', 'bucket("b", 2.5)', `bucket("b", 10)`, - ], + ].reduce((x, y) => Object.assign(x, { [y]: y }), {}), }); table.update({ @@ -1234,7 +1266,7 @@ function validate_binary_operations(output, expressions, operator) { } table.update({ x: data }); const view = await table.view({ - expressions: ["random()"], + expressions: { "random()": "random()" }, }); const schema = await view.expression_schema(); expect(schema).toEqual({ "random()": "float" }); @@ -1262,7 +1294,7 @@ function validate_binary_operations(output, expressions, operator) { "1 and 1", "1 and 100", "true and false", - ], + ].reduce((x, y) => Object.assign(x, { [y]: y }), {}), }); const result = await view.to_columns(); expect(result['"u" and "u"']).toEqual([ @@ -1303,7 +1335,7 @@ function validate_binary_operations(output, expressions, operator) { 'mand("u" and "u", "u" and "z", "z" and "z")', "mand(true, true, true, true)", "mand(is_null(null), is_not_null(null))", - ], + ].reduce((x, y) => Object.assign(x, { [y]: y }), {}), }); const result = await view.to_columns(); @@ -1364,15 +1396,15 @@ function validate_binary_operations(output, expressions, operator) { f: [true, true, true, true], }); const view = await table.view({ - expressions: [ - '"a" or "b"', - '"c" or "d"', - '"e" or "f"', - "0 or 1", - "true or false", - "false or false", - '// filtered\n"a" > 0.5 or "d" < 0.5', - ], + expressions: { + '"a" or "b"': '"a" or "b"', + '"c" or "d"': '"c" or "d"', + '"e" or "f"': '"e" or "f"', + "0 or 1": "0 or 1", + "true or false": "true or false", + "false or false": "false or false", + filtered: '"a" > 0.5 or "d" < 0.5', + }, }); const result = await view.to_columns(); expect(result['"a" or "b"']).toEqual([ @@ -1408,7 +1440,7 @@ function validate_binary_operations(output, expressions, operator) { 'mor("u" and "u", "u" and "z", "z" and "z")', "mor(false, false, false)", "mor(false, true, false)", - ], + ].reduce((x, y) => Object.assign(x, { [y]: y }), {}), }); const result = await view.to_columns(); expect( @@ -1476,7 +1508,7 @@ function validate_binary_operations(output, expressions, operator) { "1 nand 1", "1 nand 100", "true nand true", - ], + ].reduce((x, y) => Object.assign(x, { [y]: y }), {}), }); const result = await view.to_columns(); expect(result['"u" nand "u"']).toEqual([ @@ -1536,7 +1568,7 @@ function validate_binary_operations(output, expressions, operator) { '"e" nor "f"', "0 nor 1", "false nor false", - ], + ].reduce((x, y) => Object.assign(x, { [y]: y }), {}), }); const result = await view.to_columns(); expect(result['"a" nor "b"']).toEqual([ @@ -1584,7 +1616,7 @@ function validate_binary_operations(output, expressions, operator) { '"e" xor "f"', "0 xor 1", "false xor false", - ], + ].reduce((x, y) => Object.assign(x, { [y]: y }), {}), }); const result = await view.to_columns(); expect(result['"a" xor "b"']).toEqual([ diff --git a/packages/perspective/test/js/expressions/parsing.spec.js b/packages/perspective/test/js/expressions/parsing.spec.js index 9015b50fb9..6a8981b887 100644 --- a/packages/perspective/test/js/expressions/parsing.spec.js +++ b/packages/perspective/test/js/expressions/parsing.spec.js @@ -41,7 +41,10 @@ const common = require("./common.js"); }); const view = await table.view({ - expressions: [expression], + expressions: [expression].reduce( + (x, y) => Object.assign(x, { [y]: y }), + {} + ), }); table.update({ diff --git a/packages/perspective/test/js/expressions/string.spec.js b/packages/perspective/test/js/expressions/string.spec.js index 7aecbbb384..fd43b5c680 100644 --- a/packages/perspective/test/js/expressions/string.spec.js +++ b/packages/perspective/test/js/expressions/string.spec.js @@ -52,14 +52,14 @@ const random_string = ( y: ["ABC", "DEF", "EfG", "HIjK", "lMNoP"], }); const view = await table.view({ - expressions: [ - `// a \n 'abcdefghijklmnopqrstuvwxyz' == 'abcdefghijklmnopqrstuvwxyz'`, - `// b \n "x" == lower("y")`, - `// c \n if("x" == 'abc', 100, 0)`, - `// d \n if("x" != 'abc', 'new string 1', 'new string 2')`, - `// e \n 'd' > 'a'`, - `// f \n 'efz' > 'efy'`, // lexicographic - ], + expressions: { + [`a`]: `'abcdefghijklmnopqrstuvwxyz' == 'abcdefghijklmnopqrstuvwxyz'`, + [`b`]: `"x" == lower("y")`, + [`c`]: `if("x" == 'abc', 100, 0)`, + [`d`]: `if("x" != 'abc', 'new string 1', 'new string 2')`, + [`e`]: `'d' > 'a'`, + [`f`]: `'efz' > 'efy'`, // lexicographic + }, }); let result = await view.to_columns(); @@ -90,9 +90,9 @@ const random_string = ( const view = await table.view({ aggregates: { column: "last" }, group_by: ["column"], - expressions: [ - `//column\nconcat("a", ', ', 'here is a long string, ', "b")`, - ], + expressions: { + [`column`]: `concat("a", ', ', 'here is a long string, ', "b")`, + }, }); let result = await view.to_columns(); @@ -117,9 +117,9 @@ const random_string = ( }); const view = await table.view({ filter: [["column", "==", "hhs, here is a long string, HIjK"]], - expressions: [ - `//column\nconcat("a", ', ', 'here is a long string, ', "b")`, - ], + expressions: { + [`column`]: `concat("a", ', ', 'here is a long string, ', "b")`, + }, }); let result = await view.to_columns(); expect(result["column"]).toEqual([ @@ -134,7 +134,7 @@ const random_string = ( a: ["abc", "deeeeef", "fg", "hhs", "abcdefghijk"], }); const view = await table.view({ - expressions: ['length("a")'], + expressions: { 'length("a")': 'length("a")' }, }); let result = await view.to_columns(); expect(result['length("a")']).toEqual( @@ -149,7 +149,7 @@ const random_string = ( a: ["abc", "deeeeef", null, undefined, "abcdefghijk"], }); const view = await table.view({ - expressions: ['length("a")'], + expressions: { 'length("a")': 'length("a")' }, }); let result = await view.to_columns(); expect(result['length("a")']).toEqual( @@ -164,9 +164,9 @@ const random_string = ( a: ["abc", "deeeeef", "fg", "hhs", "abcdefghijk"], }); - const validate = await table.validate_expressions([ - `// col\norder("a", 'deeeeef', 'fg', 'abcdefghijk', 'hhs', 'abc')`, - ]); + const validate = await table.validate_expressions({ + [`col`]: `order("a", 'deeeeef', 'fg', 'abcdefghijk', 'hhs', 'abc')`, + }); expect(validate.expression_schema).toEqual({ col: "float", @@ -175,7 +175,7 @@ const random_string = ( const view = await table.view({ expressions: [ `order("a", 'deeeeef', 'fg', 'abcdefghijk', 'hhs', 'abc')`, - ], + ].reduce((x, y) => Object.assign(x, { [y]: y }), {}), }); let result = await view.to_columns(); @@ -195,12 +195,12 @@ const random_string = ( b: [1, 2, 3, 4, 5], }); - const validate = await table.validate_expressions([ - `// col\norder("a", 'deeeeef', 'fg', 'abcdefghijk', 'hhs', 'abc')`, - `//col2\norder('a', 'b', today())`, - `//col3\norder("b")`, - `//col4\norder()`, - ]); + const validate = await table.validate_expressions({ + [`col`]: `order("a", 'deeeeef', 'fg', 'abcdefghijk', 'hhs', 'abc')`, + [`col2`]: `order('a', 'b', today())`, + [`col3`]: `order("b")`, + [`col4`]: `order()`, + }); expect(validate.expression_schema).toEqual({ col: "float", @@ -223,7 +223,7 @@ const random_string = ( column: 7, error_message: "Zero parameter call to generic function: order not allowed", - line: 1, + line: 0, }, }); @@ -235,7 +235,10 @@ const random_string = ( a: ["abc", "deeeeef", "fg", "hhs", "abcdefghijk"], }); const view = await table.view({ - expressions: [`order("a", 'deeeeef', 'fg')`], + expressions: { + "order(\"a\", 'deeeeef', 'fg')": + "order(\"a\", 'deeeeef', 'fg')", + }, }); let result = await view.to_columns(); expect(result[`order("a", 'deeeeef', 'fg')`]).toEqual([ @@ -250,7 +253,9 @@ const random_string = ( a: ["abc", "deeeeef", null, undefined, "abcdefghijk"], }); const view = await table.view({ - expressions: [`order("a", 'deeeeef', 'abcdefghijk', 'abc')`], + expressions: { + [`order("a", 'deeeeef', 'abcdefghijk', 'abc')`]: `order("a", 'deeeeef', 'abcdefghijk', 'abc')`, + }, }); let result = await view.to_columns(); expect( @@ -265,7 +270,7 @@ const random_string = ( a: ["abc", "deeeeef", "fg", "hhs", "abcdefghijk"], }); const view = await table.view({ - expressions: ['upper("a")'], + expressions: { 'upper("a")': 'upper("a")' }, }); let result = await view.to_columns(); expect(result['upper("a")']).toEqual( @@ -280,7 +285,7 @@ const random_string = ( a: ["abc", "deeeeef", null, undefined, "abcdefghijk"], }); const view = await table.view({ - expressions: ['upper("a")'], + expressions: { 'upper("a")': 'upper("a")' }, }); let result = await view.to_columns(); expect(result['upper("a")']).toEqual( @@ -296,7 +301,10 @@ const random_string = ( b: ["𝕙ḗľᶅở щṏᵲɭⅾ", "𝑢ⱴⱳẍ𝘺𝘇ӑṣᶑᵴ", "EfG"], }); const view = await table.view({ - expressions: ['upper("a")', 'upper("b")'], + expressions: { + 'upper("a")': 'upper("a")', + 'upper("b")': 'upper("b")', + }, }); let result = await view.to_columns(); expect(result['upper("a")']).toEqual( @@ -314,7 +322,7 @@ const random_string = ( a: ["ABC", "DEF", "EfG", "HIjK", "lMNoP"], }); const view = await table.view({ - expressions: ['lower("a")'], + expressions: { 'lower("a")': 'lower("a")' }, }); let result = await view.to_columns(); expect(result['lower("a")']).toEqual( @@ -329,7 +337,7 @@ const random_string = ( a: ["ABC", "DEF", null, undefined, "lMNoP"], }); const view = await table.view({ - expressions: ['lower("a")'], + expressions: { 'lower("a")': 'lower("a")' }, }); let result = await view.to_columns(); expect(result['lower("a")']).toEqual( @@ -346,7 +354,10 @@ const random_string = ( }); const view = await table.view({ - expressions: ['lower("a")', 'lower("b")'], + expressions: { + 'lower("a")': 'lower("a")', + 'lower("b")': 'lower("b")', + }, }); let result = await view.to_columns(); @@ -368,7 +379,7 @@ const random_string = ( const view = await table.view({ expressions: [ `concat("a", ', ', 'here is a long string, ', "b")`, - ], + ].reduce((x, y) => Object.assign(x, { [y]: y }), {}), }); let result = await view.to_columns(); expect( @@ -390,7 +401,7 @@ const random_string = ( const view = await table.view({ expressions: [ `concat("a", ', ', 'here is a long string, ', "b")`, - ], + ].reduce((x, y) => Object.assign(x, { [y]: y }), {}), }); let result = await view.to_columns(); let expected = result.a.map( @@ -419,7 +430,7 @@ const random_string = ( const view = await table.view({ expressions: [ `concat("a", ', ', 'here is a long string, ', "b")`, - ], + ].reduce((x, y) => Object.assign(x, { [y]: y }), {}), }); let result = await view.to_columns(); let expected = result.a.map( @@ -442,7 +453,7 @@ const random_string = ( const view = await table.view({ expressions: [ `concat("a", ', ', 'here is a long string, ', "b")`, - ], + ].reduce((x, y) => Object.assign(x, { [y]: y }), {}), }); let result = await view.to_columns(); let expected = result.a.map( @@ -468,7 +479,7 @@ const random_string = ( const view = await table.view({ expressions: [ `upper(concat("a", ', ', 'here is a long string, ', "b"))`, - ], + ].reduce((x, y) => Object.assign(x, { [y]: y }), {}), }); let result = await view.to_columns(); let expected = result[ @@ -500,7 +511,7 @@ const random_string = ( const view = await table.view({ expressions: [ `lower(concat("a", ', ', 'HERE is a long string, ', "b"))`, - ], + ].reduce((x, y) => Object.assign(x, { [y]: y }), {}), }); let result = await view.to_columns(); let expected = result[ @@ -524,7 +535,7 @@ const random_string = ( const view = await table.view({ expressions: [ `order(lower(concat("a", ', ', 'HERE is a long string, ', "b")), 'very long string here, here is a long string, another long string is here')`, - ], + ].reduce((x, y) => Object.assign(x, { [y]: y }), {}), }); let result = await view.to_columns(); expect( @@ -544,7 +555,7 @@ const random_string = ( const view = await table.view({ expressions: [ `upper(concat("a", ', ', 'here is a long string, ', "b"))`, - ], + ].reduce((x, y) => Object.assign(x, { [y]: y }), {}), }); let result = await view.to_columns(); let expected = result[ @@ -568,7 +579,7 @@ const random_string = ( const view = await table.view({ expressions: [ `lower(concat("a", ', ', 'HERE is a long string, ', "b"))`, - ], + ].reduce((x, y) => Object.assign(x, { [y]: y }), {}), }); let result = await view.to_columns(); let expected = result[ @@ -591,7 +602,7 @@ const random_string = ( const view = await table.view({ expressions: [ `length(concat("a", ', ', 'here is a long string, ', "b"))`, - ], + ].reduce((x, y) => Object.assign(x, { [y]: y }), {}), }); let result = await view.to_columns(); let expected = result.a.map( @@ -616,7 +627,7 @@ const random_string = ( const view = await table.view({ expressions: [ `var x := concat("a", ', ', 'here is a long string, ', "b"); order(x, '𝓊⋁ẅ⤫𝛾𝓏, here is a long string, 𝑢ⱴⱳẍ𝘺𝘇ӑṣᶑᵴ', '𝕙ḗľᶅở щṏᵲɭⅾ, here is a long string, 𝕙ḗľᶅở щṏᵲɭⅾ')`, - ], + ].reduce((x, y) => Object.assign(x, { [y]: y }), {}), }); let result = await view.to_columns(); expect( @@ -638,7 +649,7 @@ const random_string = ( }); let view = await table.view({ - expressions: ['"a" == "b"'], + expressions: { '"a" == "b"': '"a" == "b"' }, }); let result = await view.to_columns(); @@ -664,7 +675,7 @@ const random_string = ( let view = await table.view({ expressions: [ `concat("a", ', ', "b") == concat("a", ', ', "b")`, - ], + ].reduce((x, y) => Object.assign(x, { [y]: y }), {}), }); let result = await view.to_columns(); @@ -682,7 +693,7 @@ const random_string = ( }); let view = await table.view({ - expressions: ['"a" == "b"'], + expressions: { '"a" == "b"': '"a" == "b"' }, }); let result = await view.to_columns(); @@ -716,7 +727,7 @@ const random_string = ( }); let view = await table.view({ - expressions: ['"a" == "b"'], + expressions: { '"a" == "b"': '"a" == "b"' }, }); let result = await view.to_columns(); @@ -738,7 +749,7 @@ const random_string = ( }); let view = await table.view({ - expressions: ['"a" == "b"'], + expressions: { '"a" == "b"': '"a" == "b"' }, }); let result = await view.to_columns(); @@ -772,7 +783,7 @@ const random_string = ( }); let view = await table.view({ - expressions: ['"a" == "b"'], + expressions: { '"a" == "b"': '"a" == "b"' }, }); let result = await view.to_columns(); @@ -805,7 +816,7 @@ const random_string = ( ], }); let view = await table.view({ - expressions: ['"a" == "b"'], + expressions: { '"a" == "b"': '"a" == "b"' }, }); let result = await view.to_columns(); expect(result['"a" == "b"']).toEqual([ @@ -837,7 +848,7 @@ const random_string = ( ], }); let view = await table.view({ - expressions: ['"a" == "b"'], + expressions: { '"a" == "b"': '"a" == "b"' }, }); let result = await view.to_columns(); expect(result['"a" == "b"']).toEqual([ @@ -1592,15 +1603,16 @@ const random_string = ( }; const table = await perspective.table({ a: get_data(1000) }); - const expression = `// parsed - var parts[4]; + const parsed = `var parts[4]; parts[0] := search("a", '^([0-9]{4})[ -][0-9]{4}[ -][0-9]{4}[ -][0-9]{4}'); parts[1] := search("a", '^[0-9]{4}[ -]([0-9]{4})[ -][0-9]{4}[ -][0-9]{4}'); parts[2] := search("a", '^[0-9]{4}[ -][0-9]{4}[ -]([0-9]{4})[ -][0-9]{4}'); parts[3] := search("a", '^[0-9]{4}[ -][0-9]{4}[ -][0-9]{4}[ -]([0-9]{4})'); concat(parts[0], parts[1], parts[2], parts[3])`; - const view = await table.view({ expressions: [expression] }); + const view = await table.view({ + expressions: { parsed }, + }); const schema = await view.expression_schema(); expect(schema).toEqual({ parsed: "string" }); const result = await view.to_columns(); @@ -1637,8 +1649,7 @@ const random_string = ( }; const table = await perspective.table({ a: get_data(1000) }); - const expression = `// parsed - var parts[4]; + const expression = `var parts[4]; parts[0] := search("a", '^([0-9]{4})[ -][0-9]{4}[ -][0-9]{4}[ -][0-9]{4}'); parts[1] := search("a", '^[0-9]{4}[ -]([0-9]{4})[ -][0-9]{4}[ -][0-9]{4}'); parts[2] := search("a", '^[0-9]{4}[ -][0-9]{4}[ -]([0-9]{4})[ -][0-9]{4}'); @@ -1646,7 +1657,9 @@ const random_string = ( var z := parts[2]; z`; - const view = await table.view({ expressions: [expression] }); + const view = await table.view({ + expressions: { parsed: expression }, + }); const schema = await view.expression_schema(); expect(schema).toEqual({ parsed: "string" }); const result = await view.to_columns(); @@ -1791,10 +1804,10 @@ const random_string = ( ], }); const view = await table.view({ - expressions: [ - "//c1\nsearch(\"a\", '(\ndef)')", - "//c2\nsearch(\"b\", '(\tworld)')", - ], + expressions: { + c1: "search(\"a\", '(\ndef)')", + c2: "search(\"b\", '(\tworld)')", + }, }); const schema = await view.expression_schema(); @@ -1834,18 +1847,17 @@ const random_string = ( y: ["$300", "$123.58", "$0.99", "$1.99"], }); - const expressions = [ - `// parsed date - var year_vec[2]; + const expressions = { + "parsed date": `var year_vec[2]; indexof("x", '([0-9]{4})$', year_vec); year_vec[1] - year_vec[0]; `, - `// parsed date2 - var year_vec[2]; + "parsed date2": `var year_vec[2]; indexof("x", '([0-9]{4})$', year_vec); `, - "// parsed dollars\nvar dollar_vec[2];indexof(\"y\", '^[$]([0-9.]+)', dollar_vec); dollar_vec[0] + dollar_vec[1];", - ]; + "parsed dollars": + "var dollar_vec[2];indexof(\"y\", '^[$]([0-9.]+)', dollar_vec); dollar_vec[0] + dollar_vec[1];", + }; const view = await table.view({ expressions }); const schema = await view.expression_schema(); @@ -1875,22 +1887,20 @@ const random_string = ( y: ["$300", "$123.58", "$0.99", "$1.99"], }); - const expressions = [ - `// parsed date - var year_vec[1]; // vector too small + const expressions = { + "parsed date": `var year_vec[1]; // vector too small indexof("x", '([0-9]{4})$', year_vec); `, - `// parsed date2 - var year_vec[2]; + "parsed date2": `var year_vec[2]; indexof("x", '([a-z])', year_vec); // should not match `, - "// parsed dollars\nvar dollar_vec[2] := {100, 200};indexof(\"y\", '^[$]([0-9.]+)', dollar_vec); dollar_vec[0] + dollar_vec[1];", - `// parsed dollars2 - var dollar_vec[2] := {100, 200}; + "parsed dollars": + "var dollar_vec[2] := {100, 200};indexof(\"y\", '^[$]([0-9.]+)', dollar_vec); dollar_vec[0] + dollar_vec[1];", + "parsed dollars2": `var dollar_vec[2] := {100, 200}; indexof("y", '^([a-z])', dollar_vec); // should not match dollar_vec[0] + dollar_vec[1]; // should not overwrite vector `, - ]; + }; const view = await table.view({ expressions }); const schema = await view.expression_schema(); @@ -1984,7 +1994,7 @@ const random_string = ( 'substring("y", 1, 2)', 'substring("y", 2, 2)', 'substring("y", 1, 1)', - ], + ].reduce((x, y) => Object.assign(x, { [y]: y }), {}), }); const results = await view.to_columns(); expect(results["substring('abcdef', 0)"]).toEqual( @@ -2058,7 +2068,10 @@ const random_string = ( ); const view = await table.view({ - expressions: ['substring("x", 3)', 'substring("x", 1, 2)'], + expressions: { + 'substring("x", 3)': 'substring("x", 3)', + 'substring("x", 1, 2)': 'substring("x", 1, 2)', + }, }); let results = await view.to_columns(); @@ -2133,7 +2146,10 @@ const random_string = ( }); const view = await table.view({ - expressions: ['substring("x", 3)', 'substring("x", 1, 2)'], + expressions: { + 'substring("x", 3)': 'substring("x", 3)', + 'substring("x", 1, 2)': 'substring("x", 1, 2)', + }, }); let results = await view.to_columns(); @@ -2163,7 +2179,10 @@ const random_string = ( }); const view2 = await table.view({ - expressions: ['substring("x", 3)', 'substring("x", 1, 2)'], + expressions: { + 'substring("x", 3)': 'substring("x", 3)', + 'substring("x", 1, 2)': 'substring("x", 1, 2)', + }, }); results = await view2.to_columns(); @@ -2191,7 +2210,7 @@ const random_string = ( 'substring("x", 100)', 'substring("x", 1, 300)', 'substring("x", -100)', - ], + ].reduce((x, y) => Object.assign(x, { [y]: y }), {}), }); let results = await view.to_columns(); @@ -2303,12 +2322,12 @@ const random_string = ( const table = await perspective.table({ a: "string", b: "string" }); table.update({ a: data, b: index }); - const expressions = [ - `//w\nreplace('abc-def-hijk', '-', '')`, - `//x\nreplace("a", '[0-9]{4}$', "b")`, - `//y\nreplace("a", '[a-z]{4}$', "b")`, - `//z\nvar x := 'long string, very cool!'; replace("a", '^[0-9]{4}', x)`, - ]; + const expressions = { + [`w`]: `replace('abc-def-hijk', '-', '')`, + [`x`]: `replace("a", '[0-9]{4}$', "b")`, + [`y`]: `replace("a", '[a-z]{4}$', "b")`, + [`z`]: `var x := 'long string, very cool!'; replace("a", '^[0-9]{4}', x)`, + }; const validate = await table.validate_expressions(expressions); expect(validate.expression_schema).toEqual({ w: "string", @@ -2340,13 +2359,13 @@ const random_string = ( test("replace invalid", async () => { const table = await perspective.table({ a: "string", b: "string" }); - const expressions = [ - `//v\nreplace('abc-def-hijk', '-', 123)`, - `//w\nreplace('', '-', today())`, - `//x\nreplace("a", '[0-9]{4}$', today())`, - `//y\nreplace("a", '[a-z]{4}$', null)`, - `//z\nvar x := 123; replace("a", '^[0-9]{4}', x)`, - ]; + const expressions = { + [`v`]: `replace('abc-def-hijk', '-', 123)`, + [`w`]: `replace('', '-', today())`, + [`x`]: `replace("a", '[0-9]{4}$', today())`, + [`y`]: `replace("a", '[a-z]{4}$', null)`, + [`z`]: `var x := 123; replace("a", '^[0-9]{4}', x)`, + }; const validate = await table.validate_expressions(expressions); expect(validate.expression_schema).toEqual({}); @@ -2374,12 +2393,12 @@ const random_string = ( const table = await perspective.table({ a: "string", b: "string" }); table.update({ a: data, b: index }); - const expressions = [ - `//w\nreplace_all('abc-def-hijk', '-', '')`, - `//x\nreplace_all("a", '[0-9]{4}', "b")`, - `//y\nreplace_all("a", '[a-z]{4}', "b")`, - `//z\nvar x := 'long string, very cool!'; replace_all("a", '[0-9]{4}', x)`, - ]; + const expressions = { + [`w`]: `replace_all('abc-def-hijk', '-', '')`, + [`x`]: `replace_all("a", '[0-9]{4}', "b")`, + [`y`]: `replace_all("a", '[a-z]{4}', "b")`, + [`z`]: `var x := 'long string, very cool!'; replace_all("a", '[0-9]{4}', x)`, + }; const validate = await table.validate_expressions(expressions); expect(validate.expression_schema).toEqual({ w: "string", @@ -2411,13 +2430,13 @@ const random_string = ( test("replace all invalid", async () => { const table = await perspective.table({ a: "string", b: "string" }); - const expressions = [ - `//v\nreplace_all('abc-def-hijk', '-', 123)`, - `//w\nreplace_all('', '-', today())`, - `//x\nreplace_all("a", '[0-9]{4}$', today())`, - `//y\nreplace_all("a", '[a-z]{4}$', null)`, - `//z\nvar x := 123; replace_all("a", '^[0-9]{4}', x)`, - ]; + const expressions = { + [`v`]: `replace_all('abc-def-hijk', '-', 123)`, + [`w`]: `replace_all('', '-', today())`, + [`x`]: `replace_all("a", '[0-9]{4}$', today())`, + [`y`]: `replace_all("a", '[a-z]{4}$', null)`, + [`z`]: `var x := 123; replace_all("a", '^[0-9]{4}', x)`, + }; const validate = await table.validate_expressions(expressions); expect(validate.expression_schema).toEqual({}); diff --git a/packages/perspective/test/js/expressions/updates.spec.js b/packages/perspective/test/js/expressions/updates.spec.js index 6b3a2dcafd..139cabf916 100644 --- a/packages/perspective/test/js/expressions/updates.spec.js +++ b/packages/perspective/test/js/expressions/updates.spec.js @@ -45,7 +45,7 @@ const pivot_data = [ "10 + 20", "lower('ABC')", "concat('hello', ' ', 'world', ', ', 'here is a long, long, long string with lots of characters')", - ], + ].reduce((x, y) => Object.assign(x, { [y]: y }), {}), }); const before = await view.to_columns(); expect(before["10 + 20"]).toEqual([30, 30, 30, 30]); @@ -106,7 +106,7 @@ const pivot_data = [ expressions: [ 'if ("x" > 4) 10; else 100', `"y" == 'A' ? true : false`, - ], + ].reduce((x, y) => Object.assign(x, { [y]: y }), {}), }); const before = await view.to_columns(); expect(before['if ("x" > 4) 10; else 100']).toEqual([ @@ -152,7 +152,7 @@ const pivot_data = [ "10 + 20", "lower('ABC')", "concat('hello', ' ', 'world', ', ', 'here is a long, long, long string with lots of characters')", - ], + ].reduce((x, y) => Object.assign(x, { [y]: y }), {}), }); const before = await view.to_columns(); @@ -200,12 +200,12 @@ const pivot_data = [ y: ["A", "B", "C", "D"], }); const view = await table.view({ - expressions: [ - "123", - '//c0\n10 + "x"', - '//c1\nlower("y")', - `//c2\nconcat("y", ' ', 'abcd')`, - ], + expressions: { + 123: "123", + c0: '10 + "x"', + c1: 'lower("y")', + c2: `concat("y", ' ', 'abcd')`, + }, }); const before = await view.to_columns(); @@ -305,7 +305,7 @@ const pivot_data = [ expressions: [ 'if ("x" > 4) 10; else 100', `"z" == 'a' ? true : false`, - ], + ].reduce((x, y) => Object.assign(x, { [y]: y }), {}), }); const before = await view.to_columns(); expect(before['if ("x" > 4) 10; else 100']).toEqual([ @@ -343,7 +343,7 @@ const pivot_data = [ y: ["A", "B", "C", "D"], }); const view = await table.view({ - expressions: ['lower("y")'], + expressions: { 'lower("y")': 'lower("y")' }, }); const before = await view.to_columns(); @@ -369,7 +369,7 @@ const pivot_data = [ y: ["A", "B", "C", "D"], }); const view = await table.view({ - expressions: ['lower("y")'], + expressions: { 'lower("y")': 'lower("y")' }, }); const before = await view.to_columns(); @@ -412,7 +412,9 @@ const pivot_data = [ z: [2, 4, 6, 8], }); const view = await table.view({ - expressions: ['"x" + ("y" + 5.5) / "z"'], + expressions: { + '"x" + ("y" + 5.5) / "z"': '"x" + ("y" + 5.5) / "z"', + }, }); const before = await view.to_columns(); @@ -443,7 +445,7 @@ const pivot_data = [ { index: "x" } ); const view = await table.view({ - expressions: ['lower("y")'], + expressions: { 'lower("y")': 'lower("y")' }, }); const before = await view.to_columns(); @@ -463,7 +465,7 @@ const pivot_data = [ z: ["a", "b", "c", "d"], }); const view = await table.view({ - expressions: ['upper("z")'], + expressions: { 'upper("z")': 'upper("z")' }, }); const before = await view.to_columns(); expect(before['upper("z")']).toEqual(["A", "B", "C", "D"]); @@ -489,7 +491,7 @@ const pivot_data = [ }); const view = await table.view({ group_by: ['lower("y")'], - expressions: ['lower("y")'], + expressions: { 'lower("y")': 'lower("y")' }, }); const before = await view.to_columns(); @@ -516,7 +518,7 @@ const pivot_data = [ test("Dependent column appends should notify expression columns, arity 2", async function () { const table = await perspective.table(common.int_float_data); const view = await table.view({ - expressions: ['"w" + "x"'], + expressions: { '"w" + "x"': '"w" + "x"' }, }); const before = await view.to_columns(); expect(before['"w" + "x"']).toEqual([2.5, 4.5, 6.5, 8.5]); @@ -536,7 +538,10 @@ const pivot_data = [ index: "x", }); const view = await table.view({ - expressions: ['"w" + "x"', 'upper("y")'], + expressions: { + '"w" + "x"': '"w" + "x"', + 'upper("y")': 'upper("y")', + }, }); let before = await view.to_columns(); expect(before['"w" + "x"']).toEqual([2.5, 4.5, 6.5, 8.5]); @@ -558,11 +563,11 @@ const pivot_data = [ test("Dependent column appends should notify expression columns on different views, arity 2", async function () { const table = await perspective.table(common.int_float_data); const view = await table.view({ - expressions: ['"w" + "x"'], + expressions: { '"w" + "x"': '"w" + "x"' }, }); const view2 = await table.view({ - expressions: ['"w" - "x"'], + expressions: { '"w" - "x"': '"w" - "x"' }, }); const before = await view.to_columns(); @@ -586,11 +591,14 @@ const pivot_data = [ test("Dependent column appends should notify equivalent expression columns on different views, arity 2", async function () { const table = await perspective.table(common.int_float_data); const view = await table.view({ - expressions: ['"w" + "x"'], + expressions: { '"w" + "x"': '"w" + "x"' }, }); const view2 = await table.view({ - expressions: ['"w" + "x"', '"w" - "x"'], + expressions: { + '"w" + "x"': '"w" + "x"', + '"w" - "x"': '"w" - "x"', + }, }); const before = await view.to_columns(); @@ -620,11 +628,11 @@ const pivot_data = [ index: "x", }); const view = await table.view({ - expressions: ['"w" + "x"'], + expressions: { '"w" + "x"': '"w" + "x"' }, }); const view2 = await table.view({ - expressions: ['"w" - "x"'], + expressions: { '"w" - "x"': '"w" - "x"' }, }); const before = await view.to_columns(); @@ -648,11 +656,14 @@ const pivot_data = [ index: "x", }); const view = await table.view({ - expressions: ['"w" + "x"'], + expressions: { '"w" + "x"': '"w" + "x"' }, }); const view2 = await table.view({ - expressions: ['"w" + "x"', '"w" - "x"'], + expressions: { + '"w" + "x"': '"w" + "x"', + '"w" - "x"': '"w" - "x"', + }, }); const before = await view.to_columns(); @@ -678,11 +689,11 @@ const pivot_data = [ index: "x", }); const view = await table.view({ - expressions: ['"w" + "x"'], + expressions: { '"w" + "x"': '"w" + "x"' }, }); const view2 = await table.view({ - expressions: ['"w" - "x"'], + expressions: { '"w" - "x"': '"w" - "x"' }, }); const before = await view.to_columns(); @@ -706,11 +717,14 @@ const pivot_data = [ index: "x", }); const view = await table.view({ - expressions: ['"w" + "x"'], + expressions: { '"w" + "x"': '"w" + "x"' }, }); const view2 = await table.view({ - expressions: ['"w" + "x"', '"w" - "x"'], + expressions: { + '"w" + "x"': '"w" + "x"', + '"w" - "x"': '"w" - "x"', + }, }); const before = await view.to_columns(); @@ -740,19 +754,17 @@ const pivot_data = [ }, { index: "x" } ); + const view = await table.view({ - expressions: ['"w" + "y"'], + expressions: { '"w" + "y"': '"w" + "y"' }, }); + let before = await view.to_columns(); expect(before['"w" + "y"']).toEqual([6.5, 8.5, 10.5, 12.5]); - table.update({ x: [2, 4], w: [null, 12.5] }); - const after = await view.to_columns(); expect(after['"w" + "y"']).toEqual([6.5, null, 10.5, 20.5]); - table.update({ x: [2, 3], w: [20.5, null] }); - const after2 = await view.to_columns(); expect(after2['"w" + "y"']).toEqual([6.5, 26.5, null, 20.5]); view.delete(); @@ -769,7 +781,7 @@ const pivot_data = [ { index: "x" } ); const view = await table.view({ - expressions: ['"w" + "y"'], + expressions: { '"w" + "y"': '"w" + "y"' }, }); let before = await view.to_columns(); expect(before['"w" + "y"']).toEqual([6.5, 8.5, 10.5, 12.5]); @@ -797,7 +809,7 @@ const pivot_data = [ const table = await perspective.table(meta, { index: "y" }); const view = await table.view({ columns: ["y", '"w" / "x"'], - expressions: ['"w" / "x"'], + expressions: { '"w" / "x"': '"w" / "x"' }, }); table.update(common.int_float_data); @@ -827,7 +839,7 @@ const pivot_data = [ y: "integer", }); const view = await table.view({ - expressions: ['"x" * "y"'], + expressions: { '"x" * "y"': '"x" * "y"' }, }); table.update(data); @@ -850,7 +862,7 @@ const pivot_data = [ test("Partial update without a new value shouldn't change computed output", async function () { const table = await perspective.table(data); const view = await table.view({ - expressions: ['"x" * "y"'], + expressions: { '"x" * "y"': '"x" * "y"' }, }); const json = await view.to_json(); @@ -871,7 +883,7 @@ const pivot_data = [ test("partial update on single computed source column", async function () { const table = await perspective.table(data); const view = await table.view({ - expressions: ['"x" * "y"'], + expressions: { '"x" * "y"': '"x" * "y"' }, }); table.update([{ __INDEX__: 0, x: 10 }]); @@ -889,7 +901,7 @@ const pivot_data = [ test("partial update on non-contiguous computed source columns", async function () { const table = await perspective.table(data); const view = await table.view({ - expressions: ['"x" * "y"'], + expressions: { '"x" * "y"': '"x" * "y"' }, }); table.update([ { __INDEX__: 0, x: 1, y: 10 }, @@ -909,7 +921,7 @@ const pivot_data = [ test("partial update on non-contiguous computed source columns, indexed table", async function () { const table = await perspective.table(data, { index: "x" }); const view = await table.view({ - expressions: ['"x" * "y"'], + expressions: { '"x" * "y"': '"x" * "y"' }, }); table.update([ { x: 1, y: 10 }, @@ -945,7 +957,7 @@ const pivot_data = [ }); const view = await table.view({ - expressions: ['// computed\n"a" + "b"'], + expressions: { computed: '"a" + "b"' }, }); let result = await view.to_columns(); @@ -982,7 +994,7 @@ const pivot_data = [ test("multiple partial update on single computed source column", async function () { const table = await perspective.table(data); const view = await table.view({ - expressions: ['"x" * "y"'], + expressions: { '"x" * "y"': '"x" * "y"' }, }); table.update([ @@ -1012,10 +1024,10 @@ const pivot_data = [ test("multiple expression columns with updates on source columns", async function () { const table = await perspective.table(data); const view = await table.view({ - expressions: ['"x" * "y"'], + expressions: { '"x" * "y"': '"x" * "y"' }, }); const view2 = await table.view({ - expressions: ['"x" + "y"'], + expressions: { '"x" + "y"': '"x" + "y"' }, }); table.update([ @@ -1047,15 +1059,15 @@ const pivot_data = [ const table = await perspective.table(data, { index: "x" }); const view = await table.view({ - expressions: ['"x" * "y"'], + expressions: { '"x" * "y"': '"x" * "y"' }, }); const view2 = await table.view({ - expressions: ['"x" + "y"'], + expressions: { '"x" + "y"': '"x" + "y"' }, }); const view3 = await table.view({ - expressions: ['"x" - "y"'], + expressions: { '"x" - "y"': '"x" - "y"' }, }); table.update({ x: [1, 2, 3, 4], y: [1, 2, 3, 4] }); @@ -1092,15 +1104,15 @@ const pivot_data = [ const table = await perspective.table(data); const view = await table.view({ - expressions: ['"x" * "y"'], + expressions: { '"x" * "y"': '"x" * "y"' }, }); const view2 = await table.view({ - expressions: ['"x" + "y"'], + expressions: { '"x" + "y"': '"x" + "y"' }, }); const view3 = await table.view({ - expressions: ['"x" - "y"'], + expressions: { '"x" - "y"': '"x" - "y"' }, }); table.update({ x: [1, 2, 3, 4], y: [1, 2, 3, 4] }); @@ -1143,7 +1155,7 @@ const pivot_data = [ '"int" + "float"': "sum", }, group_by: ['"int" + "float"'], - expressions: ['"int" + "float"'], + expressions: { '"int" + "float"': '"int" + "float"' }, }); table.update({ int: [4], __INDEX__: [0] }); @@ -1168,7 +1180,7 @@ const pivot_data = [ const view = await table.view({ columns: ['"int" - "float"', "int"], group_by: ['"int" - "float"'], - expressions: ['"int" - "float"'], + expressions: { '"int" - "float"': '"int" - "float"' }, }); table.update([{ int: 4, __INDEX__: 0 }]); @@ -1220,7 +1232,7 @@ const pivot_data = [ const view = await table.view({ columns: ['"int" * "float"', "int"], group_by: ['"int" * "float"'], - expressions: ['"int" * "float"'], + expressions: { '"int" * "float"': '"int" * "float"' }, }); table.update([{ int: 4, __INDEX__: 0 }]); @@ -1272,7 +1284,7 @@ const pivot_data = [ const view = await table.view({ columns: ['"int" / "float"', "int"], group_by: ['"int" / "float"'], - expressions: ['"int" / "float"'], + expressions: { '"int" / "float"': '"int" / "float"' }, }); table.update([{ int: 4, __INDEX__: 0 }]); @@ -1324,7 +1336,7 @@ const pivot_data = [ const table = await perspective.table(pivot_data, { index: "int" }); const view = await table.view({ columns: ['"int" + "float"', "int", "float"], - expressions: ['"int" + "float"'], + expressions: { '"int" + "float"': '"int" + "float"' }, }); table.update([{ int: 2, float: 3.5 }]); @@ -1346,7 +1358,7 @@ const pivot_data = [ const view = await table.view({ columns: ['"int" + "float"', "int", "float"], - expressions: ['"int" + "float"'], + expressions: { '"int" + "float"': '"int" + "float"' }, }); table.update([{ int: 2, float: null }]); @@ -1375,7 +1387,7 @@ const pivot_data = [ ); const view = await table.view({ columns: ['"int" + "float"', "int", "float"], - expressions: ['"int" + "float"'], + expressions: { '"int" + "float"': '"int" + "float"' }, }); table.update([{ int: 2, float: undefined }]); @@ -1404,7 +1416,7 @@ const pivot_data = [ ); const view = await table.view({ - expressions: ['lower("y")'], + expressions: { 'lower("y")': 'lower("y")' }, }); const before = await view.to_columns(); diff --git a/packages/perspective/test/js/expressions/vectors.spec.js b/packages/perspective/test/js/expressions/vectors.spec.js index f972e8ca54..5b8926e909 100644 --- a/packages/perspective/test/js/expressions/vectors.spec.js +++ b/packages/perspective/test/js/expressions/vectors.spec.js @@ -25,7 +25,7 @@ const common = require("./common.js"); test("Create vector and return value", async () => { const table = await perspective.table(common.int_float_data); const view = await table.view({ - expressions: [`// a\nvar vec[3] := {1, "w", 2}; vec[1]`], + expressions: { a: `var vec[3] := {1, "w", 2}; vec[1]` }, }); expect(await view.expression_schema()).toEqual({ a: "float", @@ -39,7 +39,7 @@ const common = require("./common.js"); test("Return from empty vector should be 0", async () => { const table = await perspective.table(common.int_float_data); const view = await table.view({ - expressions: [`// a\nvar vec[3]; vec[1]`], + expressions: { a: `var vec[3]; vec[1]` }, }); expect(await view.expression_schema()).toEqual({ a: "float", @@ -53,12 +53,12 @@ const common = require("./common.js"); test("Dynamic return types from vector", async () => { const table = await perspective.table(common.int_float_data); const view = await table.view({ - expressions: [ - `// a\nvar vec[3] := {'abc', 123, today()}; vec[0]`, - `// b\nvar vec[3] := {'abc', 123, today()}; vec[1]`, - `// c\nvar vec[3] := {'abc', 123, date(2020, 5, 23)}; vec[2]`, - `// d\nvar vec[3] := {'abc', 123, is_null(null)}; vec[2]`, - ], + expressions: { + [`a`]: `var vec[3] := {'abc', 123, today()}; vec[0]`, + [`b`]: `var vec[3] := {'abc', 123, today()}; vec[1]`, + [`c`]: `var vec[3] := {'abc', 123, date(2020, 5, 23)}; vec[2]`, + [`d`]: `var vec[3] := {'abc', 123, is_null(null)}; vec[2]`, + }, }); expect(await view.expression_schema()).toEqual({ @@ -83,9 +83,9 @@ const common = require("./common.js"); test("Use vector items as inputs", async () => { const table = await perspective.table(common.int_float_data); const view = await table.view({ - expressions: [ - `// a\nvar vec[2] := {"w", "x"}; vec[0] * vec[1]`, - ], + expressions: { + [`a`]: `var vec[2] := {"w", "x"}; vec[0] * vec[1]`, + }, }); expect(await view.expression_schema()).toEqual({ a: "float", @@ -101,9 +101,9 @@ const common = require("./common.js"); test("Custom function takes vector item input", async () => { const table = await perspective.table(common.int_float_data); const view = await table.view({ - expressions: [ - `// a\nvar vec[2] := {"w", "x"}; max(vec[0], vec[1])`, - ], + expressions: { + [`a`]: `var vec[2] := {"w", "x"}; max(vec[0], vec[1])`, + }, }); expect(await view.expression_schema()).toEqual({ a: "float", diff --git a/packages/perspective/test/js/leaks.spec.js b/packages/perspective/test/js/leaks.spec.js index 29f3725931..d4aeadd1aa 100644 --- a/packages/perspective/test/js/leaks.spec.js +++ b/packages/perspective/test/js/leaks.spec.js @@ -30,6 +30,7 @@ const arr = fs.readFileSync( async function leak_test(test, num_iterations = 10000) { // warmup await test(); + await test(); // TODO Playwright uses the same host instance so this may have grown by // the time the suite runs. Could fix with a nod eagent (and test in other @@ -51,27 +52,34 @@ async function leak_test(test, num_iterations = 10000) { * generate expressions that use all columns and scalar values. */ function generate_expressions() { - const expressions = ["concat('abcd', \"c\", 'efg')"]; + const expressions = { + "concat('abcd', \"c\", 'efg')": "concat('abcd', \"c\", 'efg')", + }; for (const op of ["+", "-", "*", "/", "^", "%"]) { - expressions.push( + expressions[ `("a" ${op} "b") + ${Math.floor(Math.random() * 100)}` - ); + ] = `("a" ${op} "b") + ${Math.floor(Math.random() * 100)}`; } for (const fn of ["sqrt", "log10", "deg2rad"]) { - expressions.push(`${fn}("b")`); + expressions[`${fn}("b")`] = `${fn}("b")`; } for (const fn of ["upper", "lower", "length"]) { - expressions.push(`${fn}("c")`); + expressions[`${fn}("c")`] = `${fn}("c")`; } for (const unit of ["m", "D"]) { - expressions.push(`bucket("d", '${unit}')`); + expressions[`bucket("d", '${unit}')`] = `bucket("d", '${unit}')`; } - return expressions; + const rand = + Object.keys(expressions)[ + Math.floor(Math.random() * Object.keys(expressions).length) + ]; + + return { [rand]: expressions[rand] }; } test.describe("leaks", function () { @@ -150,14 +158,12 @@ test.describe("leaks", function () { await leak_test(async () => { const view = await table.view({ - expressions: [ - expressions[ - Math.floor(Math.random() * expressions.length) - ], - ], + expressions, }); const expression_schema = await view.expression_schema(); - expect(Object.keys(expression_schema).length).toEqual(1); + expect(Object.keys(expression_schema).length).toEqual( + Object.keys(expressions).length + ); await view.delete(); }); @@ -170,25 +176,23 @@ test.describe("leaks", function () { * and view creation. */ test.skip("0 sided regex does not leak", async () => { - const expressions = [ - "match(\"a\", '.{1}')", - "match_all(\"a\", '[a-z]{1}')", - "search(\"a\", '.')", - ]; + const expressions = { + "match(\"a\", '.{1}')": "match(\"a\", '.{1}')", + "match_all(\"a\", '[a-z]{1}')": "match_all(\"a\", '[a-z]{1}')", + "search(\"a\", '.')": "search(\"a\", '.')", + }; await leak_test(async () => { const table = await perspective.table({ a: "abcdefghijklmnopqrstuvwxyz".split(""), }); const view = await table.view({ - expressions: [ - expressions[ - Math.floor(Math.random() * expressions.length) - ], - ], + expressions, }); const expression_schema = await view.expression_schema(); - expect(Object.keys(expression_schema).length).toEqual(1); + expect(Object.keys(expression_schema).length).toEqual( + Object.keys(expressions).length + ); await view.delete(); await table.delete(); }); @@ -199,21 +203,21 @@ test.describe("leaks", function () { a: "abcdefghijklmnopqrstuvwxyz".split(""), }); - const expressions = [ - "var x := 'abcdefghijklmnopqrstuvwxyz'; concat(\"a\", x, 'abc')", - "var x := 'abcdefghijklmnopqrstuvwxyz'; var y := 'defhijklmnopqrst'; concat(\"a\", x, 'abc', y)", - ]; + const expressions = { + "var x := 'abcdefghijklmnopqrstuvwxyz'; concat(\"a\", x, 'abc')": + "var x := 'abcdefghijklmnopqrstuvwxyz'; concat(\"a\", x, 'abc')", + "var x := 'abcdefghijklmnopqrstuvwxyz'; var y := 'defhijklmnopqrst'; concat(\"a\", x, 'abc', y)": + "var x := 'abcdefghijklmnopqrstuvwxyz'; var y := 'defhijklmnopqrst'; concat(\"a\", x, 'abc', y)", + }; await leak_test(async () => { const view = await table.view({ - expressions: [ - expressions[ - Math.floor(Math.random() * expressions.length) - ], - ], + expressions, }); const expression_schema = await view.expression_schema(); - expect(Object.keys(expression_schema).length).toEqual(1); + expect(Object.keys(expression_schema).length).toEqual( + Object.keys(expressions).length + ); await view.delete(); }); @@ -236,14 +240,12 @@ test.describe("leaks", function () { group_by: [ columns[Math.floor(Math.random() * columns.length)], ], - expressions: [ - expressions[ - Math.floor(Math.random() * expressions.length) - ], - ], + expressions, }); const expression_schema = await view.expression_schema(); - expect(Object.keys(expression_schema).length).toEqual(1); + expect(Object.keys(expression_schema).length).toEqual( + Object.keys(expressions).length + ); await view.delete(); }, 3000); @@ -260,7 +262,6 @@ test.describe("leaks", function () { const columns = ["a", "b", "c", "d"]; const expressions = generate_expressions(); - await leak_test(async () => { const view = await table.view({ group_by: [ @@ -269,14 +270,12 @@ test.describe("leaks", function () { split_by: [ columns[Math.floor(Math.random() * columns.length)], ], - expressions: [ - expressions[ - Math.floor(Math.random() * expressions.length) - ], - ], + expressions, }); const expression_schema = await view.expression_schema(); - expect(Object.keys(expression_schema).length).toEqual(1); + expect(Object.keys(expression_schema).length).toEqual( + Object.keys(expressions).length + ); await view.delete(); }, 3000); diff --git a/python/perspective/perspective/table/_utils.py b/python/perspective/perspective/table/_utils.py index 4304ab5b3e..889e66541b 100644 --- a/python/perspective/perspective/table/_utils.py +++ b/python/perspective/perspective/table/_utils.py @@ -10,7 +10,6 @@ # ┃ of the [Apache License 2.0](https://www.apache.org/licenses/LICENSE-2.0). ┃ # ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ -from operator import itemgetter import re from datetime import date, datetime from functools import partial @@ -156,11 +155,54 @@ def _parse_expression_inputs(expressions): validated_expressions = [] alias_map = {} - for expression in expressions: - if type(expression) is dict: - expr_raw, alias = itemgetter("expr", "name")(expression) - parsed = expr_raw - else: + if isinstance(expressions, dict): + for alias in expressions.keys(): + expression = expressions[alias] + + column_id_map = {} + column_name_map = {} + + # we need to be able to modify the running_cidx inside of every call to + # replacer_fn - must pass by reference unfortunately + running_cidx = [0] + + replacer_fn = partial( + _replace_expression_column_name, + column_name_map, + column_id_map, + running_cidx, + ) + + parsed = expression + parsed = re.sub( + BOOLEAN_LITERAL_REGEX, + lambda match: "True" if match.group(0) == "true" else ("False" if match.group(0) == "false" else match.group(0)), + parsed, + ) + + parsed = re.sub(EXPRESSION_COLUMN_NAME_REGEX, replacer_fn, parsed) + parsed = re.sub( + STRING_LITERAL_REGEX, + lambda match: "intern({0})".format(match.group(0)), + parsed, + ) + + # remove the `intern()` in bucket and regex functions that take + # string literal parameters. TODO this logic should be centralized + # in C++ instead of being duplicated. + parsed = re.sub(FUNCTION_LITERAL_REGEX, _replace_interned_param, parsed) + parsed = re.sub(REPLACE_FN_REGEX, _replace_interned_param, parsed) + + validated = [alias, expression, parsed, column_id_map] + + if alias_map.get(alias) is not None: + idx = alias_map[alias] + validated_expressions[idx] = validated + else: + validated_expressions.append(validated) + alias_map[alias] = len(validated_expressions) - 1 + if isinstance(expressions, list): + for expression in expressions: expr_raw = expression parsed = expr_raw alias_match = re.match(ALIAS_REGEX, expr_raw) @@ -169,49 +211,49 @@ def _parse_expression_inputs(expressions): else: alias = expr_raw - if '""' in expr_raw: - raise ValueError("Cannot reference empty column in expression!") - - column_id_map = {} - column_name_map = {} - - # we need to be able to modify the running_cidx inside of every call to - # replacer_fn - must pass by reference unfortunately - running_cidx = [0] - - replacer_fn = partial( - _replace_expression_column_name, - column_name_map, - column_id_map, - running_cidx, - ) - - parsed = re.sub( - BOOLEAN_LITERAL_REGEX, - lambda match: "True" if match.group(0) == "true" else ("False" if match.group(0) == "false" else match.group(0)), - parsed, - ) - - parsed = re.sub(EXPRESSION_COLUMN_NAME_REGEX, replacer_fn, parsed) - parsed = re.sub( - STRING_LITERAL_REGEX, - lambda match: "intern({0})".format(match.group(0)), - parsed, - ) - - # remove the `intern()` in bucket and regex functions that take - # string literal parameters. TODO this logic should be centralized - # in C++ instead of being duplicated. - parsed = re.sub(FUNCTION_LITERAL_REGEX, _replace_interned_param, parsed) - parsed = re.sub(REPLACE_FN_REGEX, _replace_interned_param, parsed) - - validated = [alias, expr_raw, parsed, column_id_map] - - if alias_map.get(alias) is not None: - idx = alias_map[alias] - validated_expressions[idx] = validated - else: - validated_expressions.append(validated) - alias_map[alias] = len(validated_expressions) - 1 + if '""' in expr_raw: + raise ValueError("Cannot reference empty column in expression!") + + column_id_map = {} + column_name_map = {} + + # we need to be able to modify the running_cidx inside of every call to + # replacer_fn - must pass by reference unfortunately + running_cidx = [0] + + replacer_fn = partial( + _replace_expression_column_name, + column_name_map, + column_id_map, + running_cidx, + ) + + parsed = re.sub( + BOOLEAN_LITERAL_REGEX, + lambda match: "True" if match.group(0) == "true" else ("False" if match.group(0) == "false" else match.group(0)), + parsed, + ) + + parsed = re.sub(EXPRESSION_COLUMN_NAME_REGEX, replacer_fn, parsed) + parsed = re.sub( + STRING_LITERAL_REGEX, + lambda match: "intern({0})".format(match.group(0)), + parsed, + ) + + # remove the `intern()` in bucket and regex functions that take + # string literal parameters. TODO this logic should be centralized + # in C++ instead of being duplicated. + parsed = re.sub(FUNCTION_LITERAL_REGEX, _replace_interned_param, parsed) + parsed = re.sub(REPLACE_FN_REGEX, _replace_interned_param, parsed) + + validated = [alias, expr_raw, parsed, column_id_map] + + if alias_map.get(alias) is not None: + idx = alias_map[alias] + validated_expressions[idx] = validated + else: + validated_expressions.append(validated) + alias_map[alias] = len(validated_expressions) - 1 return validated_expressions diff --git a/python/perspective/perspective/tests/manager/test_manager.py b/python/perspective/perspective/tests/manager/test_manager.py index bff022bd16..c3c272cfe2 100644 --- a/python/perspective/perspective/tests/manager/test_manager.py +++ b/python/perspective/perspective/tests/manager/test_manager.py @@ -444,9 +444,9 @@ def test_manager_table_validate_expressions(self): expected={ "id": 1, "data": { - "expression_schema": {"abc": "float"}, + "expression_schema": {'"a" + "a"': "float"}, "errors": {}, - "expression_alias": {"abc": '// abc \n "a" + "a"'}, + "expression_alias": {'"a" + "a"': '"a" + "a"'}, }, }, ) @@ -456,7 +456,7 @@ def test_manager_table_validate_expressions(self): "name": "table1", "cmd": "table_method", "method": "validate_expressions", - "args": [['// abc \n "a" + "a"']], + "args": [['"a" + "a"']], } manager = PerspectiveManager() table = Table(data) @@ -471,7 +471,7 @@ def test_manager_view_expression_schema(self): "table_name": "table1", "view_name": "view1", "cmd": "view", - "config": {"expressions": ['// abc \n "a" + "a"']}, + "config": {"expressions": {"abc": '"a" + "a"'}}, } message = { "id": 2, diff --git a/python/perspective/perspective/tests/single_threaded/test_single_threaded.py b/python/perspective/perspective/tests/single_threaded/test_single_threaded.py index 899f6ac2be..aaa0396850 100644 --- a/python/perspective/perspective/tests/single_threaded/test_single_threaded.py +++ b/python/perspective/perspective/tests/single_threaded/test_single_threaded.py @@ -31,12 +31,12 @@ def test_threadpool_one_does_not_block_view(self): v = t.view( columns=["symbol", "value", "value3"], - expressions=["""//value3\n"value" + "value2\""""], + expressions={"value3": """"value" + "value2\""""}, ) v_agg = t.view( columns=["symbol", "value", "value3"], - expressions=["""//value3\n"value" + "value2\""""], + expressions={"value3": """"value" + "value2\""""}, group_by=["symbol"], aggregates={"symbol": "first", "value": "sum", "value2": "sum"}, ) diff --git a/python/perspective/perspective/tests/table/test_remove.py b/python/perspective/perspective/tests/table/test_remove.py index e28d2e7a94..252eef8120 100644 --- a/python/perspective/perspective/tests/table/test_remove.py +++ b/python/perspective/perspective/tests/table/test_remove.py @@ -57,9 +57,9 @@ def test_remove_expressions(self): view = table.view( group_by=["business_line"], columns=["delta$", "alias"], - expressions=[ - '// alias\n"delta$"', - ], + expressions={ + "alias": '"delta$"', + }, ) records = view.to_records() @@ -86,9 +86,9 @@ def test_remove_expressions_after_view(self): view = table.view( group_by=["business_line"], columns=["delta$", "alias"], - expressions=[ - '// alias\n"delta$"', - ], + expressions={ + "alias": '"delta$"', + }, ) table.remove(["A"]) diff --git a/python/perspective/perspective/tests/table/test_view.py b/python/perspective/perspective/tests/table/test_view.py index 33e661abb3..a36687a993 100644 --- a/python/perspective/perspective/tests/table/test_view.py +++ b/python/perspective/perspective/tests/table/test_view.py @@ -2097,13 +2097,13 @@ def test_invalid_columns_not_in_expression_should_throw(self): data = [{"a": 1, "b": 2, "c": "a"}, {"a": 3, "b": 4, "c": "b"}] tbl = Table(data) with raises(PerspectiveCppError) as ex: - tbl.view(columns=["abc", "x"], expressions=["// abc \n 1 + 2"]) + tbl.view(columns=["abc", "x"], expressions={"abc": "1 + 2"}) assert str(ex.value) == "Invalid column 'x' found in View columns.\n" def test_should_not_throw_valid_expression(self): data = [{"a": 1, "b": 2, "c": "a"}, {"a": 3, "b": 4, "c": "b"}] tbl = Table(data) - view = tbl.view(columns=["abc"], expressions=["// abc \n 'hello!'"]) + view = tbl.view(columns=["abc"], expressions={"abc": "'hello!'"}) assert view.schema() == {"abc": str} @@ -2117,7 +2117,7 @@ def test_should_not_throw_valid_expression_config(self): filter=[["abc", "==", "A"]], group_by=["abc"], split_by=["abc"], - expressions=["// abc \n 'hello!'"], + expressions={"abc": "'hello!'"}, ) assert view.schema() == {"abc": str} diff --git a/python/perspective/perspective/tests/table/test_view_expression.py b/python/perspective/perspective/tests/table/test_view_expression.py index 8aa67cfc46..5a3290a5c0 100644 --- a/python/perspective/perspective/tests/table/test_view_expression.py +++ b/python/perspective/perspective/tests/table/test_view_expression.py @@ -37,16 +37,18 @@ def test_expression_conversion(self): "// g\nnow()", "// h\nlength(123)", ] - test_expressions_dict = [ - {"name": "x", "expr": '"a"'}, - {"name": "y", "expr": '"b" * 0.5'}, - {"name": "c", "expr": "'abcdefg'"}, - {"name": "d", "expr": "true and false"}, - {"name": "e", "expr": 'float("a") > 2 ? null : 1'}, - {"name": "f", "expr": "today()"}, - {"name": "g", "expr": "now()"}, - {"name": "h", "expr": "length(123)"}, - ] + + test_expressions_dict = { + "x": '"a"', + "y": '"b" * 0.5', + "c": "'abcdefg'", + "d": "true and false", + "e": 'float("a") > 2 ? null : 1', + "f": "today()", + "g": "now()", + "h": "length(123)", + } + str_validated = table.validate_expressions(test_expressions_str) dict_validated = table.validate_expressions(test_expressions_dict) assert str_validated["errors"] == dict_validated["errors"] @@ -72,44 +74,41 @@ def test_view_expression_schema_empty(self): def test_view_validate_expressions_alias_map_errors(self): table = Table({"a": [1, 2, 3, 4], "b": [5, 6, 7, 8]}) - expressions = [ - '// x\n"a"', - '// y\n"b" * 0.5', - "// c\n'abcdefg'", - "// d\ntrue and false", - '// e\nfloat("a") > 2 ? null : 1', - "// f\ntoday()", - "// g\nnow()", - "// h\nlength(123)", - ] + expressions = { + "x": '"a"', + "y": '"b" * 0.5', + "c": "'abcdefg'", + "d": "true and false", + "e": 'float("a") > 2 ? null : 1', + "f": "today()", + "g": "now()", + "h": "length(123)", + } validated = table.validate_expressions(expressions) - aliases = ["x", "y", "c", "d", "e", "f", "g", "h"] # Errored should also be in aliases - for idx, alias in enumerate(aliases): - assert validated["expression_alias"][alias] == expressions[idx] + for alias in aliases: + assert validated["expression_alias"][alias] == expressions[alias] def test_view_validate_expressions_alias_map(self): table = Table({"a": [1, 2, 3, 4], "b": [5, 6, 7, 8]}) - expressions = [ - '// x\n"a"', - '// y\n"b" * 0.5', - "// c\n'abcdefg'", - "// d\ntrue and false", - '// e\nfloat("a") > 2 ? null : 1', - "// f\ntoday()", - "// g\nnow()", - "// h\nlength('abcd')", - ] + expressions = { + "x": '"a"', + "y": '"b" * 0.5', + "c": "'abcdefg'", + "d": "true and false", + "e": 'float("a") > 2 ? null : 1', + "f": "today()", + "g": "now()", + "h": "length('abcd')", + } validated = table.validate_expressions(expressions) - aliases = ["x", "y", "c", "d", "e", "f", "g", "h"] - - for idx, alias in enumerate(aliases): - assert validated["expression_alias"][alias] == expressions[idx] + for alias in aliases: + assert validated["expression_alias"][alias] == expressions[alias] def test_view_expression_schema_all_types(self): table = Table({"a": [1, 2, 3, 4], "b": [5, 6, 7, 8]}) @@ -188,7 +187,7 @@ def test_table_validate_expressions_with_errors(self): def test_view_expression_create(self): table = Table({"a": [1, 2, 3, 4], "b": [5, 6, 7, 8]}) - view = table.view(expressions=['// computed \n "a" + "b"']) + view = table.view(expressions={"computed": ' "a" + "b"'}) assert view.to_columns() == { "a": [1, 2, 3, 4], "b": [5, 6, 7, 8], @@ -199,7 +198,7 @@ def test_view_expression_create(self): def test_view_expression_string_per_page(self): table = Table({"a": [i for i in range(100)]}) big_strings = [randstr(6400) for _ in range(4)] - view = table.view(expressions=["//computed{}\nvar x := '{}'; lower(x)".format(i, big_strings[i]) for i in range(4)]) + view = table.view(expressions={"computed{}".format(i): "var x := '{}'; lower(x)".format(big_strings[i]) for i in range(4)}) result = view.to_columns() schema = view.expression_schema() @@ -219,7 +218,7 @@ def test_view_expression_string_page_stress(self): "".join(["d" for _ in range(640)]), ] - view = table.view(expressions=["//computed\nvar a := '{}'; var b := '{}'; var c := '{}'; var d := '{}'; concat(a, b, c, d)".format(*big_strings)]) + view = table.view(expressions={"computed": "var a := '{}'; var b := '{}'; var c := '{}'; var d := '{}'; concat(a, b, c, d)".format(*big_strings)}) result = view.to_columns() schema = view.expression_schema() @@ -277,17 +276,15 @@ def test_view_expression_collide_local_var(self): strings = [randstr(50) for _ in range(8)] view = table.view( - expressions=[ - "// computed \n var w := '{}'; var x := '{}'; var y := '{}'; var z := '{}'; concat(w, x, y, z)".format(*strings[:4]), - "// computed2 \n var w := '{}'; var x := '{}'; var y := '{}'; var z := '{}'; concat(w, x, y, z)".format(*strings[4:]), - ] + expressions={ + "computed": " var w := '{}'; var x := '{}'; var y := '{}'; var z := '{}'; concat(w, x, y, z)".format(*strings[:4]), + "computed2": " var w := '{}'; var x := '{}'; var y := '{}'; var z := '{}'; concat(w, x, y, z)".format(*strings[4:]), + } ) result = view.to_columns() schema = view.expression_schema() - assert schema == {"computed": str, "computed2": str} - assert result["computed"] == ["".join(strings[:4]) for _ in range(4)] assert result["computed2"] == ["".join(strings[4:]) for _ in range(4)] @@ -296,43 +293,40 @@ def make_expression(): """Create a random expression with a few local string vars that are too long to be stored in-place.""" expression_name = randstr(10) - expression = ["// {}\n".format(expression_name)] + expression = "" num_vars = randint(1, 26) output_var_name = "" output_str = "" - for i in range(num_vars): name = ascii_letters[i] string_literal = randstr(randint(15, 100)) - expression.append("var {} := '{}';\n".format(name, string_literal)) - + expression += "var {} := '{}';\n".format(name, string_literal) if i == num_vars - 1: output_var_name = name output_str = string_literal - expression.append(output_var_name) - return {"expression_name": expression_name, "expression": "".join(expression), "output": output_str} + expression += output_var_name + return {"expression_name": expression_name, "expression": expression, "output": output_str} table = Table({"a": [1, 2, 3, 4]}) for _ in range(5): exprs = [make_expression() for _ in range(5)] output_map = {expr["expression_name"]: expr["output"] for expr in exprs} - view = table.view(expressions=[expr["expression"] for expr in exprs]) + view = table.view(expressions={expr["expression_name"]: expr["expression"] for expr in exprs}) expression_schema = view.expression_schema() result = view.to_columns() - for expr in output_map.keys(): assert expression_schema[expr] == str assert result[expr] == [output_map[expr] for _ in range(4)] def test_view_expression_string_literal_compare(self): table = Table({"a": [1, 2, 3, 4], "b": [5, 6, 7, 8]}) - validated = table.validate_expressions(["// computed \n 'a' == 'a'"]) + validated = table.validate_expressions({"computed": " 'a' == 'a'"}) assert validated["expression_schema"] == {"computed": "boolean"} - view = table.view(expressions=["// computed \n 'a' == 'a'"]) + view = table.view(expressions={"computed": " 'a' == 'a'"}) assert view.to_columns() == { "a": [1, 2, 3, 4], @@ -344,11 +338,11 @@ def test_view_expression_string_literal_compare(self): def test_view_expression_string_literal_compare_null(self): table = Table({"a": [1, 2, 3, 4], "b": [5, 6, 7, 8]}) - validated = table.validate_expressions(["// computed \n 'a' == null"]) + validated = table.validate_expressions({"computed": " 'a' == null"}) assert validated["expression_schema"] == {"computed": "float"} - view = table.view(expressions=["// computed \n 'a' == null"]) + view = table.view(expressions={"computed": " 'a' == null"}) assert view.to_columns() == { "a": [1, 2, 3, 4], @@ -360,12 +354,9 @@ def test_view_expression_string_literal_compare_null(self): def test_view_expression_string_literal_compare_column(self): table = Table({"a": ["a", "a", "b", "c"]}) - validated = table.validate_expressions(["// computed \n \"a\" == 'a'"]) - + validated = table.validate_expressions({"computed": " \"a\" == 'a'"}) assert validated["expression_schema"] == {"computed": "boolean"} - - view = table.view(expressions=["// computed \n \"a\" == 'a'"]) - + view = table.view(expressions={"computed": " \"a\" == 'a'"}) assert view.to_columns() == { "a": ["a", "a", "b", "c"], "computed": [True, True, False, False], @@ -375,12 +366,9 @@ def test_view_expression_string_literal_compare_column(self): def test_view_expression_string_literal_compare_column_null(self): table = Table({"a": ["a", None, "b", "c", None]}) - validated = table.validate_expressions(["// computed \n \"a\" == 'a'"]) - + validated = table.validate_expressions({"computed": " \"a\" == 'a'"}) assert validated["expression_schema"] == {"computed": "boolean"} - - view = table.view(expressions=["// computed \n \"a\" == 'a'"]) - + view = table.view(expressions={"computed": " \"a\" == 'a'"}) assert view.to_columns() == { "a": ["a", None, "b", "c", None], "computed": [True, False, False, False, False], @@ -390,35 +378,27 @@ def test_view_expression_string_literal_compare_column_null(self): def test_view_expression_string_literal_compare_column_null_long(self): table = Table({"a": ["abcdefghijklmnopqrstuvwxyz", None, "abcdefghijklmnopqrstuvwxyz", "aabcdefghijklmnopqrstuvwxyz", None]}) - validated = table.validate_expressions(["// computed \n \"a\" == 'abcdefghijklmnopqrstuvwxyz'"]) - + validated = table.validate_expressions({"computed": " \"a\" == 'abcdefghijklmnopqrstuvwxyz'"}) assert validated["expression_schema"] == {"computed": "boolean"} - - view = table.view(expressions=["// computed \n \"a\" == 'abcdefghijklmnopqrstuvwxyz'"]) + view = table.view(expressions={"computed": "\"a\" == 'abcdefghijklmnopqrstuvwxyz'"}) result = view.to_columns() assert result["computed"] == [True, False, True, False, False] - assert view.expression_schema() == {"computed": bool} def test_view_expression_string_literal_compare_column_null_long_var(self): table = Table({"a": ["abcdefghijklmnopqrstuvwxyz", None, "abcdefghijklmnopqrstuvwxyz", "aabcdefghijklmnopqrstuvwxyz", None]}) - validated = table.validate_expressions(["// computed \n var xyz := 'abcdefghijklmnopqrstuvwxyz'; \"a\" == xyz"]) - + validated = table.validate_expressions({"computed": " var xyz := 'abcdefghijklmnopqrstuvwxyz'; \"a\" == xyz"}) assert validated["expression_schema"] == {"computed": "boolean"} - - view = table.view(expressions=["// computed \n var xyz := 'abcdefghijklmnopqrstuvwxyz'; \"a\" == xyz"]) + view = table.view(expressions={"computed": "var xyz := 'abcdefghijklmnopqrstuvwxyz'; \"a\" == xyz"}) result = view.to_columns() assert result["computed"] == [True, False, True, False, False] assert view.expression_schema() == {"computed": bool} def test_view_expression_string_literal_compare_if(self): table = Table({"a": ["a", "a", "b", "c"]}) - validated = table.validate_expressions(["// computed \n if(\"a\" == 'a', 1, 2)"]) - + validated = table.validate_expressions({"computed": " if(\"a\" == 'a', 1, 2)"}) assert validated["expression_schema"] == {"computed": "float"} - - view = table.view(expressions=["// computed \n if(\"a\" == 'a', 1, 2)"]) - + view = table.view(expressions={"computed": "if(\"a\" == 'a', 1, 2)"}) assert view.to_columns() == { "a": ["a", "a", "b", "c"], "computed": [1, 1, 2, 2], @@ -469,8 +449,7 @@ def data(): return [{"a": random()} for _ in range(50)] table = Table(data()) - view = table.view(group_by=["c0"], expressions=['//c0\n"a" * 2']) - + view = table.view(group_by=["c0"], expressions={"c0": '"a" * 2'}) for _ in range(5): table.update(data()) @@ -482,8 +461,7 @@ def data(): return [{"a": random()} for _ in range(50)] table = Table(data()) - view = table.view(group_by=["c0"], split_by=["c1"], expressions=['//c0\n"a" * 2', "//c1\n'new string'"]) - + view = table.view(group_by=["c0"], split_by=["c1"], expressions={"c0": '"a" * 2', "c1": "'new string'"}) for i in range(5): table.update(data()) @@ -498,20 +476,23 @@ def test_view_expression_create_no_alias(self): "b": [5, 6, 7, 8], '"a" + "b"': [6, 8, 10, 12], } + assert view.expression_schema() == {'"a" + "b"': float} def test_view_expression_should_not_overwrite_real(self): table = Table({"a": [1, 2, 3, 4], "b": [5, 6, 7, 8]}) with raises(PerspectiveCppError) as ex: - table.view(expressions=['// a \n upper("a")']) + table.view(expressions={"a": 'upper("a")'}) + assert str(ex.value) == "View creation failed: cannot create expression column 'a' that overwrites a column that already exists.\n" - def test_view_expression_should_resolve_to_last_alias(self): + def test_legacy_view_duplicate_expression_should_resolve_to_last_alias(self): table = Table({"a": [1, 2, 3, 4], "b": [5, 6, 7, 8]}) view = table.view( columns=["abc"], - expressions=['// abc \n "a" + "b"', '// abc \n "a" - "b"'], + expressions=['//abc\n"a" + "b"', '//abc\n"a" - "b"'], ) + assert view.to_columns() == {"abc": [-4, -4, -4, -4]} def test_view_expression_multiple_alias( @@ -519,13 +500,14 @@ def test_view_expression_multiple_alias( ): table = Table({"a": [1, 2, 3, 4], "b": [5, 6, 7, 8]}) view = table.view( - expressions=[ - '// computed \n "a" + "b"', - '// computed2 \n "a" + "b"', - '// computed3 \n "a" + "b"', - '// computed4 \n "a" + "b"', - ] + expressions={ + "computed": '"a" + "b"', + "computed2": '"a" + "b"', + "computed3": '"a" + "b"', + "computed4": '"a" + "b"', + } ) + assert view.schema() == { "a": int, "b": int, @@ -546,13 +528,9 @@ def test_view_expression_multiple_views_with_the_same_alias_should_not_overwrite self, ): table = Table({"a": [1, 2, 3, 4], "b": [5, 6, 7, 8]}) - - view = table.view(expressions=['// computed \n "a" + "b"']) - - view2 = table.view(expressions=['// computed \n "a" * "b"']) - + view = table.view(expressions={"computed": ' "a" + "b"'}) + view2 = table.view(expressions={"computed": ' "a" * "b"'}) assert view.expression_schema() == {"computed": float} - assert view2.expression_schema() == { "computed": float, } @@ -564,13 +542,9 @@ def test_view_expression_multiple_views_with_the_same_alias_pivoted( self, ): table = Table({"a": [1, 2, 3, 4], "b": [5, 6, 7, 8]}) - - view = table.view(group_by=["computed"], aggregates={"computed": ["weighted mean", "b"]}, expressions=['// computed \n "a" + "b"']) - - view2 = table.view(group_by=["computed"], aggregates={"computed": "last"}, expressions=["// computed \nconcat('abc', ' ', 'def')"]) - + view = table.view(group_by=["computed"], aggregates={"computed": ["weighted mean", "b"]}, expressions={"computed": ' "a" + "b"'}) + view2 = table.view(group_by=["computed"], aggregates={"computed": "last"}, expressions={"computed": "concat('abc', ' ', 'def')"}) assert view.expression_schema() == {"computed": float} - assert view2.expression_schema() == { "computed": str, } @@ -598,20 +572,20 @@ def test_view_expression_multiple_views_with_the_same_alias_all_types( ) view = table.view( - expressions=[ - '// computed \n "a" + "b"', - "// computed2 \n bucket(\"c\", 'M')", - "// computed3 \n concat('a', 'b', 'c')", - "// computed4 \n 'new string'", - ] + expressions={ + "computed": '"a" + "b"', + "computed2": "bucket(\"c\", 'M')", + "computed3": "concat('a', 'b', 'c')", + "computed4": "'new string'", + } ) view2 = table.view( - expressions=[ - '// computed \n upper("f")', - '// computed2 \n 20 + ("b" * "a")', - "// computed4 \n bucket(\"c\", 'm')", - ] + expressions={ + "computed": 'upper("f")', + "computed2": '20 + ("b" * "a")', + "computed4": "bucket(\"c\", 'm')", + } ) assert view.expression_schema() == { @@ -644,7 +618,7 @@ def test_view_expression_multiple_views_with_the_same_alias_all_types( def test_view_expression_create_no_columns(self): table = Table({"a": [1, 2, 3, 4], "b": [5, 6, 7, 8]}) - view = table.view(columns=[], expressions=['// computed \n "a" + "b"']) + view = table.view(columns=[], expressions={"computed": ' "a" + "b"'}) assert view.to_columns() == {} assert view.schema() == {} @@ -653,7 +627,7 @@ def test_view_expression_create_no_columns(self): def test_view_expression_create_columns(self): table = Table({"a": [1, 2, 3, 4], "b": [5, 6, 7, 8]}) - view = table.view(columns=["computed"], expressions=['// computed \n "a" + "b"']) + view = table.view(columns=["computed"], expressions={"computed": ' "a" + "b"'}) assert view.to_columns() == {"computed": [6, 8, 10, 12]} assert view.schema() == {"computed": float} # computed column should still exist @@ -661,7 +635,7 @@ def test_view_expression_create_columns(self): def test_view_expression_create_clear(self): table = Table({"a": [1, 2, 3, 4], "b": [5, 6, 7, 8]}) - view = table.view(expressions=['// computed \n "a" + "b"']) + view = table.view(expressions={"computed": ' "a" + "b"'}) assert view.to_columns() == { "a": [1, 2, 3, 4], "b": [5, 6, 7, 8], @@ -673,7 +647,7 @@ def test_view_expression_create_clear(self): def test_view_expression_create_replace(self): table = Table({"a": [1, 2, 3, 4], "b": [5, 6, 7, 8]}) - view = table.view(expressions=['// computed \n "a" + "b"']) + view = table.view(expressions={"computed": ' "a" + "b"'}) assert view.to_columns() == { "a": [1, 2, 3, 4], "b": [5, 6, 7, 8], @@ -689,7 +663,7 @@ def test_view_expression_create_replace(self): def test_view_expression_multiple_dependents_replace(self): table = Table({"a": [1, 2, 3, 4], "b": [5, 6, 7, 8]}) - view = table.view(expressions=['// computed \n "a" + "b"', '// final \n ("a" + "b") ^ 2']) + view = table.view(expressions={"computed": '"a" + "b"', "final": '("a" + "b") ^ 2'}) assert view.to_columns() == { "a": [1, 2, 3, 4], "b": [5, 6, 7, 8], @@ -712,19 +686,15 @@ def test_view_expression_multiple_dependents_replace(self): def test_view_expression_multiple_views_should_not_conflate(self): table = Table({"a": [1, 2, 3, 4], "b": [5, 6, 7, 8]}) - view = table.view( - expressions=[ - '// computed \n "a" + "b"', - ] + expressions={ + "computed": '"a" + "b"', + } ) - view2 = table.view(expressions=['// computed2 \n "a" - "b"']) - + view2 = table.view(expressions={"computed2": ' "a" - "b"'}) assert view.schema() == {"a": int, "b": int, "computed": float} - assert view2.schema() == {"a": int, "b": int, "computed2": float} - assert view.to_columns() == { "a": [1, 2, 3, 4], "b": [5, 6, 7, 8], @@ -741,17 +711,14 @@ def test_view_expression_multiple_views_should_all_clear(self): table = Table({"a": [1, 2, 3, 4], "b": [5, 6, 7, 8]}) view = table.view( - expressions=[ - '// computed \n "a" + "b"', - ] + expressions={ + "computed": '"a" + "b"', + } ) - view2 = table.view(expressions=['// computed2 \n "a" - "b"']) - + view2 = table.view(expressions={"computed2": ' "a" - "b"'}) assert view.schema() == {"a": int, "b": int, "computed": float} - assert view2.schema() == {"a": int, "b": int, "computed2": float} - assert view.to_columns() == { "a": [1, 2, 3, 4], "b": [5, 6, 7, 8], @@ -776,19 +743,15 @@ def test_view_expression_multiple_views_should_all_clear(self): def test_view_expression_multiple_views_should_all_replace(self): table = Table({"a": [1, 2, 3, 4], "b": [5, 6, 7, 8]}) - view = table.view( - expressions=[ - '// computed \n "a" + "b"', - ] + expressions={ + "computed": '"a" + "b"', + } ) - view2 = table.view(expressions=['// computed2 \n "a" - "b"']) - + view2 = table.view(expressions={"computed2": ' "a" - "b"'}) assert view.schema() == {"a": int, "b": int, "computed": float} - assert view2.schema() == {"a": int, "b": int, "computed2": float} - assert view.to_columns() == { "a": [1, 2, 3, 4], "b": [5, 6, 7, 8], @@ -802,7 +765,6 @@ def test_view_expression_multiple_views_should_all_replace(self): } table.replace({"a": [10, 20, 30, 40], "b": [50, 60, 70, 80]}) - assert view.to_columns() == { "a": [10, 20, 30, 40], "b": [50, 60, 70, 80], @@ -817,14 +779,13 @@ def test_view_expression_multiple_views_should_all_replace(self): def test_view_expression_delete_and_create(self): table = Table({"a": [1, 2, 3, 4], "b": [5, 6, 7, 8]}) - view = table.view( - expressions=[ - '// computed \n "a" + "b"', - ] + expressions={ + "computed": '"a" + "b"', + } ) - assert view.schema() == {"a": int, "b": int, "computed": float} + assert view.schema() == {"a": int, "b": int, "computed": float} assert view.to_columns() == { "a": [1, 2, 3, 4], "b": [5, 6, 7, 8], @@ -832,11 +793,8 @@ def test_view_expression_delete_and_create(self): } view.delete() - - view2 = table.view(expressions=['// computed \n "a" - "b"']) - + view2 = table.view(expressions={"computed": ' "a" - "b"'}) assert view2.schema() == {"a": int, "b": int, "computed": float} - assert view2.to_columns() == { "a": [1, 2, 3, 4], "b": [5, 6, 7, 8], @@ -845,29 +803,20 @@ def test_view_expression_delete_and_create(self): def test_view_expression_delete_and_create_with_updates(self): table = Table({"a": [1, 2, 3, 4], "b": [5, 6, 7, 8]}) - - view = table.view(expressions=['// computed \n "a" + "b"', "upper(concat('abc', 'def'))"]) - + view = table.view(expressions={"computed": ' "a" + "b"', "upper(concat('abc', 'def'))": "upper(concat('abc', 'def'))"}) assert view.schema() == {"a": int, "b": int, "computed": float, "upper(concat('abc', 'def'))": str} - table.update({"a": [5, 6], "b": [9, 10]}) - assert view.to_columns() == {"a": [1, 2, 3, 4, 5, 6], "b": [5, 6, 7, 8, 9, 10], "computed": [6, 8, 10, 12, 14, 16], "upper(concat('abc', 'def'))": ["ABCDEF" for _ in range(6)]} - view.delete() - view2 = table.view( - expressions=[ - '// computed2 \n "a" - "b"', - ] + expressions={ + "computed2": '"a" - "b"', + } ) assert view2.schema() == {"a": int, "b": int, "computed2": float} - table.update({"a": [5, 6], "b": [9, 10]}) - table.update({"a": [5, 6], "b": [9, 10]}) - assert view2.to_columns() == { "a": [1, 2, 3, 4, 5, 6, 5, 6, 5, 6], "b": [5, 6, 7, 8, 9, 10, 9, 10, 9, 10], @@ -878,9 +827,9 @@ def test_view_expression_append(self): table = Table({"a": [1, 2, 3, 4], "b": [5, 6, 7, 8]}) view = table.view( - expressions=[ - '// computed \n "a" + "b"', - ] + expressions={ + "computed": '"a" + "b"', + } ) assert view.schema() == {"a": int, "b": int, "computed": float} @@ -902,9 +851,9 @@ def test_view_expression_delta_zero(self, util): table = Table({"a": [1, 2, 3, 4], "b": [5, 6, 7, 8]}) view = table.view( - expressions=[ - '// computed \n "a" + "b"', - ] + expressions={ + "computed": '"a" + "b"', + } ) assert view.schema() == {"a": int, "b": int, "computed": float} @@ -936,9 +885,9 @@ def test_view_delete_with_scope(self): ) table.view( - expressions=[ - '// inverted \n 1 / "val"', - ], + expressions={ + "inverted": '1 / "val"', + }, columns=["inverted"], ) table.update( @@ -955,9 +904,9 @@ def test_view_expression_with_custom_columns(self): table = Table({"a": [1, 2, 3, 4], "b": [5, 6, 7, 8]}) view = table.view( columns=["computed", "b"], - expressions=[ - '// computed \n "a" + "b"', - ], + expressions={ + "computed": '"a" + "b"', + }, ) assert view.to_columns() == { "b": [5, 6, 7, 8], @@ -968,9 +917,9 @@ def test_view_expression_with_group_by(self): table = Table({"a": [1, 2, 3, 4], "b": [5, 6, 7, 8]}) view = table.view( group_by=["computed"], - expressions=[ - '// computed \n "a" + "b"', - ], + expressions={ + "computed": '"a" + "b"', + }, ) assert view.to_columns() == { "__ROW_PATH__": [[], [6], [8], [10], [12]], @@ -984,9 +933,9 @@ def test_view_expression_with_group_by_clear(self): view = table.view( group_by=["computed"], - expressions=[ - '// computed \n "a" + "b"', - ], + expressions={ + "computed": '"a" + "b"', + }, ) assert view.to_columns() == { @@ -1010,9 +959,9 @@ def test_view_expression_with_group_by_replace(self): view = table.view( group_by=["computed"], - expressions=[ - '// computed \n "a" + "b"', - ], + expressions={ + "computed": '"a" + "b"', + }, ) assert view.to_columns() == { @@ -1035,9 +984,9 @@ def test_view_expression_with_split_by(self): table = Table({"a": [1, 2, 3, 4], "b": [5, 6, 7, 8]}) view = table.view( split_by=["computed"], - expressions=[ - '// computed \n "a" + "b"', - ], + expressions={ + "computed": '"a" + "b"', + }, ) assert view.to_columns() == { "6|a": [1, None, None, None], @@ -1058,9 +1007,9 @@ def test_view_expression_with_row_split_by(self): table = Table({"a": [1, 2, 3, 4], "b": [5, 6, 7, 8]}) view = table.view( split_by=["computed"], - expressions=[ - '// computed \n "a" + "b"', - ], + expressions={ + "computed": '"a" + "b"', + }, ) assert view.to_columns() == { "6|a": [1, None, None, None], @@ -1079,8 +1028,7 @@ def test_view_expression_with_row_split_by(self): def test_view_expression_with_sort(self): table = Table({"a": ["a", "ab", "abc", "abcd"]}) - view = table.view(sort=[["computed", "desc"]], expressions=['// computed \n length("a")']) - + view = table.view(sort=[["computed", "desc"]], expressions={"computed": 'length("a")'}) assert view.to_columns() == { "a": ["abcd", "abc", "ab", "a"], "computed": [4, 3, 2, 1], @@ -1088,13 +1036,12 @@ def test_view_expression_with_sort(self): def test_view_expression_with_filter(self): table = Table({"a": ["a", "ab", "abc", "abcd"]}) - view = table.view(filter=[["computed", ">=", 3]], expressions=['// computed \n length("a")']) - + view = table.view(filter=[["computed", ">=", 3]], expressions={"computed": 'length("a")'}) assert view.to_columns() == {"a": ["abc", "abcd"], "computed": [3, 4]} def test_view_day_of_week_date(self): table = Table({"a": [date(2020, 3, i) for i in range(9, 14)]}) - view = table.view(expressions=['// bucket \n day_of_week("a")']) + view = table.view(expressions={"bucket": 'day_of_week("a")'}) assert view.schema() == {"a": date, "bucket": str} assert view.to_columns() == { "a": [datetime(2020, 3, i) for i in range(9, 14)], @@ -1109,7 +1056,7 @@ def test_view_day_of_week_date(self): def test_view_day_of_week_datetime(self): table = Table({"a": [datetime(2020, 3, i, 12, 30) for i in range(9, 14)]}) - view = table.view(expressions=['// bucket \n day_of_week("a")']) + view = table.view(expressions={"bucket": 'day_of_week("a")'}) assert view.schema() == {"a": datetime, "bucket": str} assert view.to_columns() == { "a": [datetime(2020, 3, i, 12, 30) for i in range(9, 14)], @@ -1124,7 +1071,7 @@ def test_view_day_of_week_datetime(self): def test_view_month_of_year_date(self): table = Table({"a": [date(2020, i, 15) for i in range(1, 13)]}) - view = table.view(expressions=['// bucket \n month_of_year("a")']) + view = table.view(expressions={"bucket": 'month_of_year("a")'}) assert view.schema() == {"a": date, "bucket": str} assert view.to_columns() == { "a": [datetime(2020, i, 15) for i in range(1, 13)], @@ -1150,7 +1097,7 @@ def test_view_month_of_year_datetime(self): "a": [datetime(2020, i, 15) for i in range(1, 13)], } ) - view = table.view(expressions=['// bucket \n month_of_year("a")']) + view = table.view(expressions={"bucket": 'month_of_year("a")'}) assert view.schema() == {"a": datetime, "bucket": str} assert view.to_columns() == { "a": [datetime(2020, i, 15) for i in range(1, 13)], @@ -1182,7 +1129,7 @@ def test_view_day_bucket_date(self): ], } ) - view = table.view(expressions=["// bucket \n bucket(\"a\", 'D')"]) + view = table.view(expressions={"bucket": "bucket(\"a\", 'D')"}) assert view.schema() == {"a": date, "bucket": date} assert view.to_columns() == { "a": [ @@ -1210,7 +1157,7 @@ def test_view_day_bucket_date_with_null(self): ], } ) - view = table.view(expressions=["// bucket \n bucket(\"a\", 'D')"]) + view = table.view(expressions={"bucket": "bucket(\"a\", 'D')"}) assert view.schema() == {"a": date, "bucket": date} assert view.to_columns() == { "a": [ @@ -1238,7 +1185,7 @@ def test_view_day_bucket_datetime(self): ], } ) - view = table.view(expressions=["// bucket \n bucket(\"a\", 'D')"]) + view = table.view(expressions={"bucket": "bucket(\"a\", 'D')"}) assert view.schema() == {"a": datetime, "bucket": date} assert view.to_columns() == { "a": [ @@ -1266,7 +1213,7 @@ def test_view_month_bucket_date(self): ], } ) - view = table.view(expressions=["// bucket \n bucket(\"a\", 'M')"]) + view = table.view(expressions={"bucket": "bucket(\"a\", 'M')"}) assert view.schema() == {"a": date, "bucket": date} assert view.to_columns() == { "a": [ @@ -1294,7 +1241,7 @@ def test_view_month_bucket_date_with_null(self): ], } ) - view = table.view(expressions=["// bucket \n bucket(\"a\", 'M')"]) + view = table.view(expressions={"bucket": "bucket(\"a\", 'M')"}) assert view.schema() == {"a": date, "bucket": date} assert view.to_columns() == { "a": [ @@ -1322,7 +1269,7 @@ def test_view_month_bucket_datetime(self): ], } ) - view = table.view(expressions=["// bucket \n bucket(\"a\", 'M')"]) + view = table.view(expressions={"bucket": "bucket(\"a\", 'M')"}) assert view.schema() == {"a": datetime, "bucket": date} assert view.to_columns() == { "a": [ @@ -1345,7 +1292,7 @@ def test_view_month_bucket_datetime_with_null(self): "a": [datetime(2020, 1, 1), None, None, datetime(2020, 3, 15)], } ) - view = table.view(expressions=["// bucket \n bucket(\"a\", 'M')"]) + view = table.view(expressions={"bucket": "bucket(\"a\", 'M')"}) assert view.schema() == {"a": datetime, "bucket": date} assert view.to_columns() == { "a": [datetime(2020, 1, 1), None, None, datetime(2020, 3, 15)], @@ -1356,14 +1303,14 @@ def test_view_integer_expression(self): table = Table({"x": int, "y": date, "z": float}) view = table.view( - expressions=[ - "// computed\n integer(2147483648)", - "// computed2\n integer(-2147483649)", - "// computed3 \n integer(123.456)", - '// computed4 \n integer("x")', - '// computed5 \n integer("y")', - '// computed6 \n integer("z")', - ] + expressions={ + "computed": "integer(2147483648)", + "computed2": "integer(-2147483649)", + "computed3": "integer(123.456)", + "computed4": 'integer("x")', + "computed5": 'integer("y")', + "computed6": 'integer("z")', + } ) table.update({"x": [12136582], "y": [date(2020, 6, 30)], "z": [1.23456]}) @@ -1390,15 +1337,15 @@ def test_view_float_expression(self): table = Table({"w": datetime, "x": int, "y": date, "z": float}) view = table.view( - expressions=[ - "// computed\n float(2147483648)", - "// computed2\n float(-2147483649)", - "// computed3 \n float(123.456789123)", - '// computed4 \n float("x")', - '// computed5 \n float("y")', - '// computed6 \n float("z")', - '// computed7 \n float("w")', - ] + expressions={ + "computed": "float(2147483648)", + "computed2": "float(-2147483649)", + "computed3": "float(123.456789123)", + "computed4": 'float("x")', + "computed5": 'float("y")', + "computed6": 'float("z")', + "computed7": 'float("w")', + } ) dt = datetime(2018, 8, 12, 15, 32, 55) @@ -1430,13 +1377,9 @@ def test_view_float_expression(self): def test_view_date_expression(self): table = Table({"x": [1]}) - - view = table.view(expressions=["// computed\n date(2020, 5, 30)", "// computed2\n date(1997, 8, 31)"]) - + view = table.view(expressions={"computed": " date(2020, 5, 30)", "computed2": "date(1997, 8, 31)"}) assert view.expression_schema() == {"computed": date, "computed2": date} - result = view.to_dict() - assert result["computed"] == [datetime(2020, 5, 30)] assert result["computed2"] == [datetime(1997, 8, 31)] @@ -1446,39 +1389,30 @@ def test_view_datetime_expression(self): dt = datetime(2015, 11, 29, 23, 59, 59) seconds_timestamp = mktime(dt.timetuple()) + dt.microsecond / 1000000.0 ms_timestamp = int(seconds_timestamp * 1000) - - view = table.view(expressions=["// computed\n datetime({})".format(ms_timestamp)]) - + view = table.view(expressions={"computed": "datetime({})".format(ms_timestamp)}) assert view.expression_schema() == {"computed": datetime} - result = view.to_dict() - assert result["computed"] == [datetime(2015, 11, 29, 23, 59, 59)] def test_view_datetime_expression_roundtrip(self): table = Table({"x": [datetime(2015, 11, 29, 23, 59, 59)]}) - - view = table.view(expressions=['// computed\n datetime(float("x"))']) - + view = table.view(expressions={"computed": 'datetime(float("x"))'}) assert view.expression_schema() == {"computed": datetime} - result = view.to_dict() - assert result["computed"] == [datetime(2015, 11, 29, 23, 59, 59)] def test_view_string_expression(self): table = Table({"a": date, "b": datetime, "c": int, "d": float, "e": str, "f": bool}) - view = table.view( - expressions=[ - '// computed\n string("a")', - '// computed2\n string("b")', - '// computed3\n string("c")', - '// computed4\n string("d")', - '// computed5\n string("e")', - '// computed6\n string("f")', - "// computed7\n string(1234.5678)", - ] + expressions={ + "computed": 'string("a")', + "computed2": 'string("b")', + "computed3": 'string("c")', + "computed4": 'string("d")', + "computed5": 'string("e")', + "computed6": 'string("f")', + "computed7": "string(1234.5678)", + } ) table.update( @@ -1493,9 +1427,7 @@ def test_view_string_expression(self): ) assert view.expression_schema() == {"computed": str, "computed2": str, "computed3": str, "computed4": str, "computed5": str, "computed6": str, "computed7": str} - result = view.to_dict() - assert result["computed"] == ["2020-05-30", "2021-07-13"] assert result["computed2"] == ["2015-11-29 23:59:59.000", "2016-11-29 23:59:59.000"] assert result["computed3"] == ["12345678", "1293879852"] @@ -1514,12 +1446,12 @@ def test_view_regex_email(self): endings = ["com", "net", "co.uk", "ie", "me", "io", "co"] data = ["{}@{}.{}".format(randstr(30, ascii_letters + "0123456789" + "._-"), randstr(10), choices(endings, k=1)[0]) for _ in range(100)] table = Table({"a": data}) - expressions = [ - "// address\nsearch(\"a\", '^([a-zA-Z0-9._-]+)@')", - "// domain\nsearch(\"a\", '@([a-zA-Z.]+)$')", - "//is_email?\nmatch_all(\"a\", '^([a-zA-Z0-9._-]+)@([a-zA-Z.]+)$')", - "//has_at?\nmatch(\"a\", '@')", - ] + expressions = { + "address": "search(\"a\", '^([a-zA-Z0-9._-]+)@')", + "domain": "search(\"a\", '@([a-zA-Z.]+)$')", + "is_email?": "match_all(\"a\", '^([a-zA-Z0-9._-]+)@([a-zA-Z.]+)$')", + "has_at?": "match(\"a\", '@')", + } view = table.view(expressions=expressions) schema = view.expression_schema() @@ -1548,8 +1480,8 @@ def digits(): table = Table({"a": data}) view = table.view( - expressions=[ - """// parsed\n + expressions={ + "parsed": """ var parts[4]; parts[0] := search("a", '^([0-9]{4})[ -][0-9]{4}[ -][0-9]{4}[ -][0-9]{4}'); parts[1] := search("a", '^[0-9]{4}[ -]([0-9]{4})[ -][0-9]{4}[ -][0-9]{4}'); @@ -1557,8 +1489,8 @@ def digits(): parts[3] := search("a", '^[0-9]{4}[ -][0-9]{4}[ -][0-9]{4}[ -]([0-9]{4})'); concat(parts[0], parts[1], parts[2], parts[3]) """, - "//is_number?\nmatch_all(\"a\", '^[0-9]{4}[ -][0-9]{4}[ -][0-9]{4}[ -][0-9]{4}')", - ] + "is_number?": "match_all(\"a\", '^[0-9]{4}[ -][0-9]{4}[ -][0-9]{4}[ -][0-9]{4}')", + } ) schema = view.expression_schema() assert schema == {"parsed": str, "is_number?": bool} @@ -1590,7 +1522,7 @@ def test_view_expression_newlines(self): } ) - view = table.view(expressions=["//c1\nsearch(\"a\", '(\ndef)')", "//c2\nsearch(\"b\", '(\tworld)')", "//c3\nmatch(\"a\", '\\n')", "//c4\nmatch(\"b\", '\\n')"]) + view = table.view(expressions={"c1": "search(\"a\", '(\ndef)')", "c2": "search(\"b\", '(\tworld)')", "c3": "match(\"a\", '\\n')", "c4": "match(\"b\", '\\n')"}) assert view.expression_schema() == {"c1": str, "c2": str, "c3": bool, "c4": bool} @@ -1604,15 +1536,15 @@ def test_view_regex_substring(self): data = ["abc, def", "efg", "", None, "aaaaaaaaaaaaa"] table = Table({"x": data}) view = table.view( - expressions=[ - "//a\nsubstring('abcdef', 0)", - "//abc\nsubstring('abcdef', 3)", - '//b\nsubstring("x", 0)', - '//c\nsubstring("x", 5, 1)', - '//d\nsubstring("x", 100)', - '//e\nsubstring("x", 0, 10000)', - '//f\nsubstring("x", 5, 0)', - ] + expressions={ + "a": "substring('abcdef', 0)", + "abc": "substring('abcdef', 3)", + "b": 'substring("x", 0)', + "c": 'substring("x", 5, 1)', + "d": 'substring("x", 100)', + "e": 'substring("x", 0, 10000)', + "f": 'substring("x", 5, 0)', + } ) results = view.to_columns() @@ -1630,9 +1562,9 @@ def test_view_regex_email_substr(self): endings = ["com", "net", "co.uk", "ie", "me", "io", "co"] data = ["{}@{}.{}".format(randstr(30, ascii_letters + "0123456789" + "._-"), randstr(10), choices(endings, k=1)[0]) for _ in range(100)] table = Table({"a": data}) - expressions = [ - '// address\nvar vec[2]; indexof("a", \'^([a-zA-Z0-9._-]+)@\', vec) ? substring("a", vec[0], vec[1] - vec[0] + 1) : null', - """// ending + expressions = { + "address": 'var vec[2]; indexof("a", \'^([a-zA-Z0-9._-]+)@\', vec) ? substring("a", vec[0], vec[1] - vec[0] + 1) : null', + "ending": """ var domain := search(\"a\", '@([a-zA-Z.]+)$'); var len := length(domain); if (len > 0 and is_not_null(domain)) { @@ -1640,7 +1572,7 @@ def test_view_regex_email_substr(self): } else { 'not found'; }""", - ] + } view = table.view(expressions=expressions) schema = view.expression_schema() diff --git a/python/perspective/perspective/tests/viewer/test_validate.py b/python/perspective/perspective/tests/viewer/test_validate.py index 4baa69f82a..df353ae76a 100644 --- a/python/perspective/perspective/tests/viewer/test_validate.py +++ b/python/perspective/perspective/tests/viewer/test_validate.py @@ -53,14 +53,13 @@ def test_validate_filter_is_not_null(self): assert validate.validate_filter(filters) == filters def test_validate_expressions(self): - computed = ["// expression1 \n 'Hello'"] - assert validate.validate_expressions(computed) == computed - computed = [{"name": "expression1", "expr": "'hey'"}] - assert validate.validate_expressions(computed) == computed + # with raises(PerspectiveError): + computed = {"expression1": " 'Hello'"} + validate.validate_expressions(computed) def test_validate_expressions_invalid(self): with raises(PerspectiveError): - assert validate.validate_expressions({}) + assert validate.validate_expressions("test") def test_validate_version(self): assert validate.validate_version("1.2.3") diff --git a/python/perspective/perspective/tests/viewer/test_viewer.py b/python/perspective/perspective/tests/viewer/test_viewer.py index 8ba4b6ad36..72b9ab6a31 100644 --- a/python/perspective/perspective/tests/viewer/test_viewer.py +++ b/python/perspective/perspective/tests/viewer/test_viewer.py @@ -198,7 +198,7 @@ def test_viewer_delete_without_table(self): def test_save_restore(self): table = Table({"a": [1, 2, 3]}) - viewer = PerspectiveViewer(plugin="X Bar", filter=[["a", "==", 2]], expressions=['"a" * 2']) + viewer = PerspectiveViewer(plugin="X Bar", filter=[["a", "==", 2]], expressions={'"a" * 2': '"a" * 2'}) viewer.load(table) # Save config @@ -207,19 +207,19 @@ def test_save_restore(self): assert config["filter"] == [["a", "==", 2]] assert viewer.plugin == "X Bar" assert config["plugin"] == "X Bar" - assert config["expressions"] == ['"a" * 2'] + assert config["expressions"] == {'"a" * 2': '"a" * 2'} # reset configuration viewer.reset() assert viewer.plugin == "Datagrid" assert viewer.filter == [] - assert viewer.expressions == [] + assert viewer.expressions == {} # restore configuration viewer.restore(**config) assert viewer.filter == [["a", "==", 2]] assert viewer.plugin == "X Bar" - assert viewer.expressions == ['"a" * 2'] + assert viewer.expressions == {'"a" * 2': '"a" * 2'} def test_save_restore_plugin_config(self): viewer = PerspectiveViewer(plugin="Datagrid", plugin_config={"columns": {"a": {"fixed": 4}}}) diff --git a/python/perspective/perspective/viewer/validate.py b/python/perspective/perspective/viewer/validate.py index a430127fe5..85fb296380 100644 --- a/python/perspective/perspective/viewer/validate.py +++ b/python/perspective/perspective/viewer/validate.py @@ -139,11 +139,10 @@ def validate_expressions(expressions): if expressions is None: return [] - if isinstance(expressions, str): - # wrap in a list and return - return [expressions] - if isinstance(expressions, list): + import logging + + logging.warn("Legacy `expressions` format: {}".format(expressions)) for expr in expressions: if isinstance(expr, dict): if not (expr.get("name") and expr.get("expr")): @@ -151,6 +150,11 @@ def validate_expressions(expressions): elif not isinstance(expr, str): raise PerspectiveError("Cannot parse non-string expression: {}".format(str(type(expr)))) return expressions + elif isinstance(expressions, dict): + for expr in expressions.values(): + if not isinstance(expr, str): + raise PerspectiveError("Cannot parse non-string expression: {}".format(str(type(expr)))) + return expressions else: raise PerspectiveError("Cannot parse expressions of type: {}".format(str(type(expressions)))) diff --git a/python/perspective/perspective/viewer/viewer.py b/python/perspective/perspective/viewer/viewer.py index d244414b4b..3a4390e05f 100644 --- a/python/perspective/perspective/viewer/viewer.py +++ b/python/perspective/perspective/viewer/viewer.py @@ -135,7 +135,7 @@ def __init__( self.aggregates = validate_aggregates(aggregates) or {} self.sort = validate_sort(sort) or [] self.filter = validate_filter(filter) or [] - self.expressions = validate_expressions(expressions) or [] + self.expressions = validate_expressions(expressions) or {} self.plugin_config = validate_plugin_config(plugin_config) or {} self.settings = settings self.theme = theme @@ -270,7 +270,7 @@ def reset(self): self.split_by = [] self.filter = [] self.sort = [] - self.expressions = [] + self.expressions = {} self.aggregates = {} self.columns = [] self.plugin = "Datagrid" diff --git a/python/perspective/perspective/viewer/viewer_traitlets.py b/python/perspective/perspective/viewer/viewer_traitlets.py index 6e1cd428e8..28edd85660 100644 --- a/python/perspective/perspective/viewer/viewer_traitlets.py +++ b/python/perspective/perspective/viewer/viewer_traitlets.py @@ -50,7 +50,7 @@ class PerspectiveTraitlets(HasTraits): aggregates = Dict(default_value={}).tag(sync=True) sort = List(default_value=[]).tag(sync=True) filter = List(default_value=[]).tag(sync=True) - expressions = List(default_value=[]).tag(sync=True) + expressions = Dict(default_value=[]).tag(sync=True) plugin_config = Dict(default_value={}).tag(sync=True) settings = Bool(True).tag(sync=True) theme = Unicode("Pro Light", allow_none=True).tag(sync=True) diff --git a/python/perspective/requirements/requirements-310.txt b/python/perspective/requirements/requirements-310.txt index 0fef0f72a9..b82375d08b 100644 --- a/python/perspective/requirements/requirements-310.txt +++ b/python/perspective/requirements/requirements-310.txt @@ -91,7 +91,6 @@ pandas==2.1.3 pandocfilters==1.5.0 parso==0.8.3 pathspec==0.11.2 -perspective-python==2.6.1 pexpect==4.8.0 pip==23.3.1 platformdirs==4.0.0 diff --git a/python/perspective/requirements/requirements-311.txt b/python/perspective/requirements/requirements-311.txt index 4ea5e95f18..19270847d5 100644 --- a/python/perspective/requirements/requirements-311.txt +++ b/python/perspective/requirements/requirements-311.txt @@ -90,7 +90,6 @@ pandas==2.1.3 pandocfilters==1.5.0 parso==0.8.3 pathspec==0.11.2 -perspective-python==2.6.1 pexpect==4.8.0 pip==23.3.1 platformdirs==4.0.0 diff --git a/python/perspective/requirements/requirements-38.txt b/python/perspective/requirements/requirements-38.txt index db91791041..aab2f5ce40 100644 --- a/python/perspective/requirements/requirements-38.txt +++ b/python/perspective/requirements/requirements-38.txt @@ -92,7 +92,6 @@ pandas==2.0.3 pandocfilters==1.5.0 parso==0.8.3 pathspec==0.11.2 -perspective-python==2.6.1 pexpect==4.8.0 pickleshare==0.7.5 pip==23.3.1 diff --git a/python/perspective/requirements/requirements-39.txt b/python/perspective/requirements/requirements-39.txt index 0fef0f72a9..b82375d08b 100644 --- a/python/perspective/requirements/requirements-39.txt +++ b/python/perspective/requirements/requirements-39.txt @@ -91,7 +91,6 @@ pandas==2.1.3 pandocfilters==1.5.0 parso==0.8.3 pathspec==0.11.2 -perspective-python==2.6.1 pexpect==4.8.0 pip==23.3.1 platformdirs==4.0.0 diff --git a/rust/perspective-viewer/.rustfmt.toml b/rust/perspective-viewer/.rustfmt.toml deleted file mode 100644 index e9fa326030..0000000000 --- a/rust/perspective-viewer/.rustfmt.toml +++ /dev/null @@ -1,14 +0,0 @@ -# normalize_comments = true - -group_imports = "StdExternalCrate" -imports_granularity = "Module" -newline_style = "Unix" -normalize_doc_attributes = true -overflow_delimited_expr = true -reorder_impl_items = true -use_field_init_shorthand = true -wrap_comments = true -format_code_in_doc_comments = true -format_macro_matchers = true -format_strings = true -condense_wildcard_suffixes = true \ No newline at end of file diff --git a/rust/perspective-viewer/rustfmt.toml b/rust/perspective-viewer/rustfmt.toml index 2a949c18cf..6ea11f7060 100644 --- a/rust/perspective-viewer/rustfmt.toml +++ b/rust/perspective-viewer/rustfmt.toml @@ -10,6 +10,19 @@ # ┃ of the [Apache License 2.0](https://www.apache.org/licenses/LICENSE-2.0). ┃ # ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ +# * Always insert a newline after a dedent, unless the next line is also a +# dedent. +# * Always insert a newline before a comment, unless the comment line is also +# an indent. +# * Always insert a newline before and after top-level `use` declarations, +# which should be at the top of the file after the header. +# * Prefer no newlines otherwise. +# +# [html!] +# * Always remove spacing after `<`, before `>` and `/>`, and around `=` +# literals. +# * Always insert spaces around content within `{}` blocks. + unstable_features = true wrap_comments = true use_field_init_shorthand = true @@ -17,4 +30,12 @@ format_code_in_doc_comments = true format_macro_bodies = true format_macro_matchers = true format_strings = true -overflow_delimited_expr = true \ No newline at end of file +overflow_delimited_expr = true +normalize_comments = true +normalize_doc_attributes = true +reorder_impl_items = true +group_imports = "StdExternalCrate" +imports_granularity = "Module" +newline_style = "Unix" +condense_wildcard_suffixes = true +match_block_trailing_comma = true \ No newline at end of file diff --git a/rust/perspective-viewer/src/less/column-selector.less b/rust/perspective-viewer/src/less/column-selector.less index 6887f240f0..4ba525c316 100644 --- a/rust/perspective-viewer/src/less/column-selector.less +++ b/rust/perspective-viewer/src/less/column-selector.less @@ -132,6 +132,7 @@ align-items: center; margin-right: 7px; cursor: pointer; + margin-bottom: 4px; // Button icon &:before { diff --git a/rust/perspective-viewer/src/rust/components/column_dropdown.rs b/rust/perspective-viewer/src/rust/components/column_dropdown.rs index 50f9c19edc..3c6d00636c 100644 --- a/rust/perspective-viewer/src/rust/components/column_dropdown.rs +++ b/rust/perspective-viewer/src/rust/components/column_dropdown.rs @@ -66,30 +66,30 @@ impl Component for ColumnDropDown { ColumnDropDownMsg::SetCallback(callback) => { self.on_select = Some(callback); false - } + }, ColumnDropDownMsg::SetValues(values, width) => { self.values = Some(values); self.selected = 0; self.width = width; true - } + }, ColumnDropDownMsg::ItemSelect => { if let Some(ref values) = self.values { match values.get(self.selected) { None => { console::error_1(&"Selected out-of-bounds".into()); false - } + }, Some(x) => { self.on_select.as_ref().unwrap().emit(x.clone()); false - } + }, } } else { console::error_1(&"No Values".into()); false } - } + }, ColumnDropDownMsg::ItemDown => { self.selected += 1; if let Some(ref values) = self.values { @@ -99,7 +99,7 @@ impl Component for ColumnDropDown { }; true - } + }, ColumnDropDownMsg::ItemUp => { if let Some(ref values) = self.values { if self.selected < 1 { @@ -109,7 +109,7 @@ impl Component for ColumnDropDown { self.selected -= 1; true - } + }, } } diff --git a/rust/perspective-viewer/src/rust/components/column_selector.rs b/rust/perspective-viewer/src/rust/components/column_selector.rs index 85406e1e73..7f4381c7b9 100644 --- a/rust/perspective-viewer/src/rust/components/column_selector.rs +++ b/rust/perspective-viewer/src/rust/components/column_selector.rs @@ -160,7 +160,7 @@ impl Component for ColumnSelector { self.named_row_count = named.unwrap_or_default(); true - } + }, HoverActiveIndex(Some(to_index)) => { let min_cols = ctx.props().renderer.metadata().min; let config = ctx.props().session.get_view_config(); @@ -193,11 +193,11 @@ impl Component for ColumnSelector { .dragdrop .notify_drag_enter(DragTarget::Active, to_index) } - } + }, HoverActiveIndex(_) => { ctx.props().dragdrop.notify_drag_leave(DragTarget::Active); true - } + }, Drop((column, DragTarget::Active, effect, index)) => { let update = ctx.props().session.create_drag_drop_update( column, @@ -209,7 +209,7 @@ impl Component for ColumnSelector { ApiFuture::spawn(ctx.props().update_and_render(update)); true - } + }, Drop((_, _, DragEffect::Move(DragTarget::Active), _)) => true, Drop((..)) => true, } diff --git a/rust/perspective-viewer/src/rust/components/column_selector/active_column.rs b/rust/perspective-viewer/src/rust/components/column_selector/active_column.rs index 475f96b339..191aa11995 100644 --- a/rust/perspective-viewer/src/rust/components/column_selector/active_column.rs +++ b/rust/perspective-viewer/src/rust/components/column_selector/active_column.rs @@ -105,10 +105,10 @@ impl ActiveColumnProps { columns.clear(); columns.push(Some(name)); } - } + }, ColumnSelectMode::Select => { columns.retain(|x| x.as_ref() != Some(&name)); - } + }, } self.apply_columns(columns); } @@ -179,15 +179,15 @@ impl Component for ActiveColumn { ctx.props().deactivate_column(column, shift_key); ctx.props().onselect.emit(()); false - } + }, MouseEnter(is_render) => { self.mouseover = is_render; is_render - } + }, MouseLeave(is_render) => { self.mouseover = false; is_render - } + }, New(InPlaceColumn::Column(col)) => { let mut view_config = ctx.props().session.get_view_config().clone(); if ctx.props().idx >= view_config.columns.len() { @@ -203,7 +203,7 @@ impl Component for ActiveColumn { ApiFuture::spawn(ctx.props().update_and_render(update)); true - } + }, New(InPlaceColumn::Expression(col)) => { let mut view_config = ctx.props().session.get_view_config().clone(); if ctx.props().idx >= view_config.columns.len() { @@ -221,7 +221,7 @@ impl Component for ActiveColumn { ApiFuture::spawn(ctx.props().update_and_render(update)); true - } + }, } } @@ -246,7 +246,7 @@ impl Component for ActiveColumn { label.clone(), Some(ctx.props().dragdrop.get_drag_column().unwrap()), ) - } + }, ActiveColumnState::Column(label, name) => (label.clone(), Some(name.to_owned())), ActiveColumnState::Required(label) => (label.clone(), None), }; @@ -287,7 +287,7 @@ impl Component for ActiveColumn { } - } + }, ((label, Some(name)), Some(col_type)) => { let remove_column = if self.is_required { None @@ -389,7 +389,7 @@ impl Component for ActiveColumn { } - } + }, _ => { // Expression columns are the only UI element which requires the // `View` (for its expression type), we may need to stub these @@ -402,7 +402,7 @@ impl Component for ActiveColumn {
} - } + }, } } } diff --git a/rust/perspective-viewer/src/rust/components/column_selector/aggregate_selector.rs b/rust/perspective-viewer/src/rust/components/column_selector/aggregate_selector.rs index 1d0614d442..e3c098c888 100644 --- a/rust/perspective-viewer/src/rust/components/column_selector/aggregate_selector.rs +++ b/rust/perspective-viewer/src/rust/components/column_selector/aggregate_selector.rs @@ -65,7 +65,7 @@ impl Component for AggregateSelector { AggregateSelectorMsg::SetAggregate(aggregate) => { self.set_aggregate(ctx, aggregate); false - } + }, } } diff --git a/rust/perspective-viewer/src/rust/components/column_selector/config_selector.rs b/rust/perspective-viewer/src/rust/components/column_selector/config_selector.rs index a1f1603dac..0bd1dd0279 100644 --- a/rust/perspective-viewer/src/rust/components/column_selector/config_selector.rs +++ b/rust/perspective-viewer/src/rust/components/column_selector/config_selector.rs @@ -209,11 +209,11 @@ impl Component for ConfigSelector { ctx.props().ondragenter.emit(()); } should_render - } + }, ConfigSelectorMsg::DragLeave(action) => { ctx.props().dragdrop.notify_drag_leave(action); true - } + }, ConfigSelectorMsg::Close(index, DragTarget::Sort) => { let mut sort = ctx.props().session.get_view_config().sort.clone(); sort.remove(index); @@ -226,7 +226,7 @@ impl Component for ConfigSelector { ApiFuture::spawn(ctx.props().update_and_render(config)); ctx.props().onselect.emit(()); false - } + }, ConfigSelectorMsg::Close(index, DragTarget::GroupBy) => { let mut group_by = ctx.props().session.get_view_config().group_by.clone(); group_by.remove(index); @@ -238,7 +238,7 @@ impl Component for ConfigSelector { ApiFuture::spawn(ctx.props().update_and_render(config)); ctx.props().onselect.emit(()); false - } + }, ConfigSelectorMsg::Close(index, DragTarget::SplitBy) => { let mut split_by = ctx.props().session.get_view_config().split_by.clone(); split_by.remove(index); @@ -250,7 +250,7 @@ impl Component for ConfigSelector { ApiFuture::spawn(ctx.props().update_and_render(config)); ctx.props().onselect.emit(()); false - } + }, ConfigSelectorMsg::Close(index, DragTarget::Filter) => { self.filter_dropdown.hide().unwrap(); let mut filter = ctx.props().session.get_view_config().filter.clone(); @@ -263,7 +263,7 @@ impl Component for ConfigSelector { ApiFuture::spawn(ctx.props().update_and_render(config)); ctx.props().onselect.emit(()); false - } + }, ConfigSelectorMsg::Close(..) => false, ConfigSelectorMsg::Drop(column, action, effect, index) if action != DragTarget::Active => @@ -278,12 +278,12 @@ impl Component for ConfigSelector { ApiFuture::spawn(ctx.props().update_and_render(update)); ctx.props().onselect.emit(()); false - } + }, ConfigSelectorMsg::Drop(_, _, DragEffect::Move(action), _) if action != DragTarget::Active => { true - } + }, ConfigSelectorMsg::Drop(..) => false, ConfigSelectorMsg::TransposePivots => { let mut view_config = ctx.props().session.get_view_config().clone(); @@ -298,7 +298,7 @@ impl Component for ConfigSelector { ApiFuture::spawn(ctx.props().update_and_render(update)); ctx.props().onselect.emit(()); false - } + }, ConfigSelectorMsg::SetFilterValue(index, input) => { let mut filter = ctx.props().session.get_view_config().filter.clone(); let update = if matches!(filter[index].1, FilterOp::In | FilterOp::NotIn) { @@ -329,7 +329,7 @@ impl Component for ConfigSelector { ApiFuture::spawn(ctx.props().update_and_render(update)); false - } + }, ConfigSelectorMsg::New(DragTarget::GroupBy, InPlaceColumn::Column(col)) => { let mut view_config = ctx.props().session.get_view_config().clone(); view_config.group_by.push(col); @@ -341,7 +341,7 @@ impl Component for ConfigSelector { ApiFuture::spawn(ctx.props().update_and_render(update)); ctx.props().onselect.emit(()); false - } + }, ConfigSelectorMsg::New(DragTarget::SplitBy, InPlaceColumn::Column(col)) => { let mut view_config = ctx.props().session.get_view_config().clone(); view_config.split_by.push(col); @@ -353,7 +353,7 @@ impl Component for ConfigSelector { ApiFuture::spawn(ctx.props().update_and_render(update)); ctx.props().onselect.emit(()); false - } + }, ConfigSelectorMsg::New(DragTarget::Filter, InPlaceColumn::Column(col)) => { let mut view_config = ctx.props().session.get_view_config().clone(); view_config.filter.push(Filter( @@ -370,7 +370,7 @@ impl Component for ConfigSelector { ApiFuture::spawn(ctx.props().update_and_render(update)); ctx.props().onselect.emit(()); false - } + }, ConfigSelectorMsg::New(DragTarget::Sort, InPlaceColumn::Column(col)) => { let mut view_config = ctx.props().session.get_view_config().clone(); view_config.sort.push(Sort(col, SortDir::Asc)); @@ -382,7 +382,7 @@ impl Component for ConfigSelector { ApiFuture::spawn(ctx.props().update_and_render(update)); ctx.props().onselect.emit(()); false - } + }, ConfigSelectorMsg::New(DragTarget::GroupBy, InPlaceColumn::Expression(col)) => { let mut view_config = ctx.props().session.get_view_config().clone(); view_config.group_by.push(col.name.as_ref().to_owned()); @@ -396,7 +396,7 @@ impl Component for ConfigSelector { ApiFuture::spawn(ctx.props().update_and_render(update)); ctx.props().onselect.emit(()); false - } + }, ConfigSelectorMsg::New(DragTarget::SplitBy, InPlaceColumn::Expression(col)) => { let mut view_config = ctx.props().session.get_view_config().clone(); view_config.split_by.push(col.name.as_ref().to_owned()); @@ -410,7 +410,7 @@ impl Component for ConfigSelector { ApiFuture::spawn(ctx.props().update_and_render(update)); ctx.props().onselect.emit(()); false - } + }, ConfigSelectorMsg::New(DragTarget::Filter, InPlaceColumn::Expression(col)) => { let mut view_config = ctx.props().session.get_view_config().clone(); view_config.filter.push(Filter( @@ -428,7 +428,7 @@ impl Component for ConfigSelector { ApiFuture::spawn(ctx.props().update_and_render(update)); ctx.props().onselect.emit(()); false - } + }, ConfigSelectorMsg::New(DragTarget::Sort, InPlaceColumn::Expression(col)) => { let mut view_config = ctx.props().session.get_view_config().clone(); view_config @@ -444,7 +444,7 @@ impl Component for ConfigSelector { ApiFuture::spawn(ctx.props().update_and_render(update)); ctx.props().onselect.emit(()); false - } + }, ConfigSelectorMsg::New(DragTarget::Active, _) => false, } } @@ -544,7 +544,7 @@ impl Component for ConfigSelector { allow_duplicates=true parent={ ctx.link().clone() } column_dropdown={ column_dropdown.clone() } - exclude={config.sort.iter().map(|x| x.0.clone()).collect::>() } + exclude={ config.sort.iter().map(|x| x.0.clone()).collect::>() } dragdrop={ &ctx.props().dragdrop } is_dragover={ ctx.props().dragdrop.is_dragover(DragTarget::Sort).map(|(index, name)| { (index, Sort(name, SortDir::Asc)) diff --git a/rust/perspective-viewer/src/rust/components/column_selector/empty_column.rs b/rust/perspective-viewer/src/rust/components/column_selector/empty_column.rs index 55d40f1bc2..ca69b6fdb7 100644 --- a/rust/perspective-viewer/src/rust/components/column_selector/empty_column.rs +++ b/rust/perspective-viewer/src/rust/components/column_selector/empty_column.rs @@ -99,20 +99,20 @@ impl Component for EmptyColumn { } false - } + }, KeyDown(40) => { ctx.props().column_dropdown.item_down(); false - } + }, KeyDown(38) => { ctx.props().column_dropdown.item_up(); false - } + }, KeyDown(13) => { ctx.props().column_dropdown.item_select(); ctx.props().column_dropdown.hide().unwrap(); false - } + }, KeyDown(_) => false, Input => { if let Some(elem) = self.input_ref.cast::() { @@ -124,7 +124,7 @@ impl Component for EmptyColumn { } false - } + }, } } diff --git a/rust/perspective-viewer/src/rust/components/column_selector/filter_column.rs b/rust/perspective-viewer/src/rust/components/column_selector/filter_column.rs index 1b06010870..338f127721 100644 --- a/rust/perspective-viewer/src/rust/components/column_selector/filter_column.rs +++ b/rust/perspective-viewer/src/rust/components/column_selector/filter_column.rs @@ -103,13 +103,13 @@ impl FilterColumnProps { } else { None } - } + }, (Type::Datetime, FilterTerm::Scalar(Scalar::Float(x) | Scalar::DateTime(x))) => { posix_to_utc_str(*x).ok() - } + }, (Type::Bool, FilterTerm::Scalar(Scalar::Bool(x))) => { Some((if *x { "true" } else { "false" }).to_owned()) - } + }, (Type::Bool, _) => Some("true".to_owned()), (_, x) => Some(format!("{}", x)), } @@ -135,7 +135,7 @@ impl FilterColumnProps { ], Some(Type::Bool) => { vec![FilterOp::EQ, FilterOp::IsNull, FilterOp::IsNotNull] - } + }, Some(_) => vec![ FilterOp::EQ, FilterOp::NE, @@ -189,7 +189,7 @@ impl FilterColumnProps { } else { None } - } + }, Some(Type::Float) => { if val.is_empty() { None @@ -198,7 +198,7 @@ impl FilterColumnProps { } else { None } - } + }, Some(Type::Date) => match NaiveDate::parse_from_str(&val, "%Y-%m-%d") { Ok(ref posix) => posix .and_hms_opt(0, 0, 0) @@ -283,42 +283,42 @@ impl Component for FilterColumn { ctx.props().update_filter_input(input); false - } + }, FilterColumnMsg::FilterKeyDown(40) => { if ctx.props().is_suggestable() { ctx.props().filter_dropdown.item_down(); ctx.props().filter_dropdown.item_select(); } false - } + }, FilterColumnMsg::FilterKeyDown(38) => { if ctx.props().is_suggestable() { ctx.props().filter_dropdown.item_up(); ctx.props().filter_dropdown.item_select(); } false - } + }, FilterColumnMsg::Close => { ctx.props().filter_dropdown.hide().unwrap(); false - } + }, FilterColumnMsg::FilterKeyDown(13) => { if ctx.props().is_suggestable() { ctx.props().filter_dropdown.item_select(); ctx.props().filter_dropdown.hide().unwrap(); } false - } + }, FilterColumnMsg::FilterKeyDown(_) => { if ctx.props().is_suggestable() { ctx.props().filter_dropdown.reautocomplete(); } false - } + }, FilterColumnMsg::FilterOpSelect(op) => { ctx.props().update_filter_op(op); true - } + }, } } @@ -457,10 +457,10 @@ impl Component for FilterColumn { checked={ self.input == "true" } oninput={ input }/> } - } + }, None => { html! {} - } + }, }; let filter_ops = ctx diff --git a/rust/perspective-viewer/src/rust/components/column_selector/inactive_column.rs b/rust/perspective-viewer/src/rust/components/column_selector/inactive_column.rs index 7b19adaaa1..079b1f0969 100644 --- a/rust/perspective-viewer/src/rust/components/column_selector/inactive_column.rs +++ b/rust/perspective-viewer/src/rust/components/column_selector/inactive_column.rs @@ -127,15 +127,15 @@ impl Component for InactiveColumn { .activate_column(ctx.props().name.to_owned(), shift_key); ctx.props().onselect.emit(()); false - } + }, MouseEnter(is_render) => { self.mouseover = is_render; is_render - } + }, MouseLeave(is_render) => { self.mouseover = false; is_render - } + }, } } diff --git a/rust/perspective-viewer/src/rust/components/column_selector/sort_column.rs b/rust/perspective-viewer/src/rust/components/column_selector/sort_column.rs index c9f8b7e2e9..3e89fd037f 100644 --- a/rust/perspective-viewer/src/rust/components/column_selector/sort_column.rs +++ b/rust/perspective-viewer/src/rust/components/column_selector/sort_column.rs @@ -76,7 +76,7 @@ impl Component for SortColumn { }; ApiFuture::spawn(ctx.props().update_and_render(update)); false - } + }, } } diff --git a/rust/perspective-viewer/src/rust/components/column_settings_sidebar/attributes_tab.rs b/rust/perspective-viewer/src/rust/components/column_settings_sidebar/attributes_tab.rs index bfe2e15323..72cc8bb6ce 100644 --- a/rust/perspective-viewer/src/rust/components/column_settings_sidebar/attributes_tab.rs +++ b/rust/perspective-viewer/src/rust/components/column_settings_sidebar/attributes_tab.rs @@ -40,8 +40,7 @@ pub fn AttributesTab(p: &AttributesTabProps) -> Html { {on_close} {selected_column} {session} - {renderer} - /> + {renderer}/> } diff --git a/rust/perspective-viewer/src/rust/components/column_settings_sidebar/attributes_tab/expression_editor.rs b/rust/perspective-viewer/src/rust/components/column_settings_sidebar/attributes_tab/expression_editor.rs index 993a888e7f..49908fcb87 100644 --- a/rust/perspective-viewer/src/rust/components/column_settings_sidebar/attributes_tab/expression_editor.rs +++ b/rust/perspective-viewer/src/rust/components/column_settings_sidebar/attributes_tab/expression_editor.rs @@ -54,6 +54,7 @@ pub fn expression_editor_attr(p: &ExprEditorAttrProps) -> Html { ColumnLocator::Expr(Some(ref s)) => delete_expr(s, p), _ => panic!("Tried to delete an invalid column!"), } + p.on_close.emit(()); }); @@ -63,10 +64,9 @@ pub fn expression_editor_attr(p: &ExprEditorAttrProps) -> Html { { on_save } { on_validate } { on_delete } - session = { &p.session } - old_alias = { p.selected_column.name().cloned() } - disabled = {!matches!(p.selected_column, ColumnLocator::Expr(_))} - /> + session={ &p.session } + old_alias={ p.selected_column.name().cloned() } + disabled={ !matches!(p.selected_column, ColumnLocator::Expr(_)) }/> } } diff --git a/rust/perspective-viewer/src/rust/components/column_settings_sidebar/sidebar.rs b/rust/perspective-viewer/src/rust/components/column_settings_sidebar/sidebar.rs index 4c11047f89..e97e40d2f3 100644 --- a/rust/perspective-viewer/src/rust/components/column_settings_sidebar/sidebar.rs +++ b/rust/perspective-viewer/src/rust/components/column_settings_sidebar/sidebar.rs @@ -123,6 +123,7 @@ pub fn ColumnSettingsSidebar(p: &ColumnSettingsProps) -> Html { custom_events, column_name ); + match tab { ColumnSettingsTab::Attributes => { html! { @@ -130,28 +131,23 @@ pub fn ColumnSettingsSidebar(p: &ColumnSettingsProps) -> Html { { session } { renderer } { custom_events } - { selected_column } - { on_close } - /> + { on_close }/> } - } + }, ColumnSettingsTab::Style => html! { + ty={ maybe_ty.unwrap() }/> }, } }) }; let selected_tab = use_state(|| None); - let on_tab_change = { clone!(selected_tab); use_callback((), move |idx, _| { @@ -160,21 +156,19 @@ pub fn ColumnSettingsSidebar(p: &ColumnSettingsProps) -> Html { }; html_template! { - + + icon={ "column_settings_icon" } + width_override={ p.width_override } + selected_tab={ *selected_tab }> - {tabs} - {match_fn} - {on_tab_change} - selected_tab={*selected_tab} - /> + { tabs } + { match_fn } + { on_tab_change } + selected_tab={ *selected_tab }/> } diff --git a/rust/perspective-viewer/src/rust/components/column_settings_sidebar/style_tab.rs b/rust/perspective-viewer/src/rust/components/column_settings_sidebar/style_tab.rs index ec688ea136..fb7962147b 100644 --- a/rust/perspective-viewer/src/rust/components/column_settings_sidebar/style_tab.rs +++ b/rust/perspective-viewer/src/rust/components/column_settings_sidebar/style_tab.rs @@ -41,8 +41,7 @@ pub fn StyleTab(p: &StyleTabProps) -> Html { session={ p.session.clone() } renderer={ p.renderer.clone() } ty={ p.ty } - column_name={ p.column_name.clone() } - /> + column_name={ p.column_name.clone() }/> } diff --git a/rust/perspective-viewer/src/rust/components/column_settings_sidebar/style_tab/column_style.rs b/rust/perspective-viewer/src/rust/components/column_settings_sidebar/style_tab/column_style.rs index d9b8a2a0d8..f0763c2bb3 100644 --- a/rust/perspective-viewer/src/rust/components/column_settings_sidebar/style_tab/column_style.rs +++ b/rust/perspective-viewer/src/rust/components/column_settings_sidebar/style_tab/column_style.rs @@ -126,8 +126,7 @@ pub fn ColumnStyle(p: &ColumnStyleProps) -> Html { { enable_time_config } { config } { default_config } - { on_change } - /> + { on_change }/> } }), @@ -200,10 +199,9 @@ pub fn ColumnStyle(p: &ColumnStyleProps) -> Html { column_name={ props.column_name.clone() } session={ &props.session } renderer={ &props.renderer } - custom_events={ &props.custom_events } - /> + custom_events={ &props.custom_events }/> }) - } + }, _ => None, } }) diff --git a/rust/perspective-viewer/src/rust/components/column_settings_sidebar/style_tab/symbol.rs b/rust/perspective-viewer/src/rust/components/column_settings_sidebar/style_tab/symbol.rs index 7a0a285577..a8ad89b085 100644 --- a/rust/perspective-viewer/src/rust/components/column_settings_sidebar/style_tab/symbol.rs +++ b/rust/perspective-viewer/src/rust/components/column_settings_sidebar/style_tab/symbol.rs @@ -131,7 +131,7 @@ impl yew::Component for SymbolAttr { self.pairs = new_pairs; true - } + }, } } diff --git a/rust/perspective-viewer/src/rust/components/column_settings_sidebar/style_tab/symbol/row_selector.rs b/rust/perspective-viewer/src/rust/components/column_settings_sidebar/style_tab/symbol/row_selector.rs index 58f9fadc30..8574fdddf2 100644 --- a/rust/perspective-viewer/src/rust/components/column_settings_sidebar/style_tab/symbol/row_selector.rs +++ b/rust/perspective-viewer/src/rust/components/column_settings_sidebar/style_tab/symbol/row_selector.rs @@ -65,22 +65,23 @@ pub fn row_selector(p: &RowSelectorProps) -> Html { pairs.remove(i); } } + let exclude: HashSet<_> = pairs .into_iter() .filter_map(|SymbolKVPair { key, .. }| key) .collect(); + html! { -
+
+ { on_select } + focused={ p.focused } + index={ p.index } + set_focused_index={ p.set_focused_index.clone() } + value={ p.selected_row.clone().unwrap_or_default() } + column_name={ p.column_name.clone() }/>
} } else { diff --git a/rust/perspective-viewer/src/rust/components/column_settings_sidebar/style_tab/symbol/symbol_pairs.rs b/rust/perspective-viewer/src/rust/components/column_settings_sidebar/style_tab/symbol/symbol_pairs.rs index 69d5cbc475..fe592f2bca 100644 --- a/rust/perspective-viewer/src/rust/components/column_settings_sidebar/style_tab/symbol/symbol_pairs.rs +++ b/rust/perspective-viewer/src/rust/components/column_settings_sidebar/style_tab/symbol/symbol_pairs.rs @@ -131,21 +131,21 @@ impl yew::Component for PairsListItem { new_pairs.remove(p.index); p.update_pairs.emit(new_pairs); true - } + }, PairListItemMsg::UpdateKey(key) => { let next = p.pair.update_key(key); let mut new_pairs = p.pairs.clone(); new_pairs[p.index] = next; p.update_pairs.emit(new_pairs); true - } + }, PairListItemMsg::UpdateValue(val) => { let next = p.pair.update_value(val); let mut new_pairs = p.pairs.clone(); new_pairs[p.index] = next; p.update_pairs.emit(new_pairs); true - } + }, } } diff --git a/rust/perspective-viewer/src/rust/components/containers/dragdrop_list.rs b/rust/perspective-viewer/src/rust/components/containers/dragdrop_list.rs index 922529993d..c690846213 100644 --- a/rust/perspective-viewer/src/rust/components/containers/dragdrop_list.rs +++ b/rust/perspective-viewer/src/rust/components/containers/dragdrop_list.rs @@ -160,7 +160,7 @@ where } else { false } - } + }, } } diff --git a/rust/perspective-viewer/src/rust/components/containers/dropdown_menu.rs b/rust/perspective-viewer/src/rust/components/containers/dropdown_menu.rs index d3fd456816..21de54db82 100644 --- a/rust/perspective-viewer/src/rust/components/containers/dropdown_menu.rs +++ b/rust/perspective-viewer/src/rust/components/containers/dropdown_menu.rs @@ -74,7 +74,7 @@ where { x.clone().into() } } - } + }, DropDownMenuItem::OptGroup(name, xs) => { html_template! { { name } @@ -94,7 +94,7 @@ where }
} - } + }, }) .collect::() } else { diff --git a/rust/perspective-viewer/src/rust/components/containers/radio_list.rs b/rust/perspective-viewer/src/rust/components/containers/radio_list.rs index 518a9116b0..d74073064c 100644 --- a/rust/perspective-viewer/src/rust/components/containers/radio_list.rs +++ b/rust/perspective-viewer/src/rust/components/containers/radio_list.rs @@ -133,7 +133,7 @@ where self.selected = x.clone(); ctx.props().on_change.emit(x); } - } + }, }; false } diff --git a/rust/perspective-viewer/src/rust/components/containers/scroll_panel.rs b/rust/perspective-viewer/src/rust/components/containers/scroll_panel.rs index ef60e07cd2..1e29ccae9f 100644 --- a/rust/perspective-viewer/src/rust/components/containers/scroll_panel.rs +++ b/rust/perspective-viewer/src/rust/components/containers/scroll_panel.rs @@ -206,7 +206,7 @@ impl Component for ScrollPanel { ScrollPanelMsg::ResetAutoWidth => { self.viewport_width = 0.0; self.calculate_window_content(ctx) - } + }, ScrollPanelMsg::UpdateViewportDimensions => { let viewport = self.viewport_elem(ctx); let rect = viewport.get_bounding_client_rect(); @@ -218,7 +218,7 @@ impl Component for ScrollPanel { self.viewport_height = rect.height() - 8.0; self.viewport_width = max!(self.viewport_width, rect.width() - 6.0); re_render - } + }, ScrollPanelMsg::CalculateWindowContent => self.calculate_window_content(ctx), ScrollPanelMsg::ChildrenChanged => true, } diff --git a/rust/perspective-viewer/src/rust/components/containers/select.rs b/rust/perspective-viewer/src/rust/components/containers/select.rs index 67758b3816..4a43386441 100644 --- a/rust/perspective-viewer/src/rust/components/containers/select.rs +++ b/rust/perspective-viewer/src/rust/components/containers/select.rs @@ -81,10 +81,10 @@ fn find_nth(mut count: i32, items: &[SelectItem]) -> Option<&T> { match item { SelectItem::Option(_) if count > 0 => { count -= 1; - } + }, SelectItem::OptGroup(_, items) if count >= items.len() as i32 => { count -= items.len() as i32; - } + }, SelectItem::OptGroup(_, items) => return items.get(count as usize), SelectItem::Option(x) => return Some(x), } @@ -154,7 +154,7 @@ where id={ ctx.props().id } class={ class } ref={ &self.select_ref } - onchange={callback}> + onchange={ callback }> { for ctx.props().values.iter().map(|value| match value { SelectItem::Option(value) => { diff --git a/rust/perspective-viewer/src/rust/components/containers/sidebar.rs b/rust/perspective-viewer/src/rust/components/containers/sidebar.rs index 88127a820c..db20e2ed20 100644 --- a/rust/perspective-viewer/src/rust/components/containers/sidebar.rs +++ b/rust/perspective-viewer/src/rust/components/containers/sidebar.rs @@ -22,6 +22,7 @@ use crate::clone; pub struct SidebarProps { /// The component's children. pub children: Children, + /// When this callback is called, the sidebar will close pub on_close: Callback<()>, pub title: String, @@ -55,27 +56,27 @@ pub fn Sidebar(p: &SidebarProps) -> Html { } } }); - let width_style = format!("min-width: 200px; width: {}px", *auto_width); + let width_style = format!("min-width: 200px; width: {}px", *auto_width); html! { -