@perspective-dev/client 4.2.0 → 4.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cdn/perspective.js +2 -2
- package/dist/cdn/perspective.js.map +3 -3
- package/dist/esm/perspective.inline.js +2 -2
- package/dist/esm/perspective.inline.js.map +3 -3
- package/dist/esm/perspective.js +2 -2
- package/dist/esm/perspective.js.map +3 -3
- package/dist/esm/perspective.node.d.ts +10 -0
- package/dist/esm/perspective.node.js +95 -9
- package/dist/esm/perspective.node.js.map +2 -2
- package/dist/esm/ts-rs/GroupRollupMode.d.ts +1 -0
- package/dist/esm/ts-rs/JoinOptions.d.ts +9 -0
- package/dist/esm/ts-rs/JoinType.d.ts +1 -0
- package/dist/esm/ts-rs/ViewConfig.d.ts +2 -0
- package/dist/esm/ts-rs/ViewConfigUpdate.d.ts +2 -0
- package/dist/esm/ts-rs/ViewWindow.d.ts +0 -1
- package/dist/esm/virtual_server.d.ts +7 -0
- package/dist/esm/virtual_servers/clickhouse.js +1 -1
- package/dist/esm/virtual_servers/clickhouse.js.map +3 -3
- package/dist/esm/virtual_servers/duckdb.d.ts +5 -0
- package/dist/esm/virtual_servers/duckdb.js +1 -1
- package/dist/esm/virtual_servers/duckdb.js.map +3 -3
- package/dist/wasm/perspective-js.d.ts +41 -5
- package/dist/wasm/perspective-js.js +90 -11
- package/dist/wasm/perspective-js.wasm +0 -0
- package/dist/wasm/perspective-js.wasm.d.ts +8 -5
- package/package.json +5 -5
- package/src/rust/client.rs +70 -1
- package/src/rust/generic_sql_model.rs +16 -0
- package/src/rust/lib.rs +4 -0
- package/src/rust/utils/errors.rs +4 -0
- package/src/rust/utils/futures.rs +5 -6
- package/src/rust/view.rs +13 -4
- package/src/rust/virtual_server.rs +68 -1
- package/src/ts/perspective.node.ts +18 -0
- package/src/ts/ts-rs/GroupRollupMode.ts +3 -0
- package/src/ts/ts-rs/JoinOptions.ts +7 -0
- package/src/ts/ts-rs/JoinType.ts +3 -0
- package/src/ts/ts-rs/ViewConfig.ts +2 -1
- package/src/ts/ts-rs/ViewConfigUpdate.ts +2 -1
- package/src/ts/ts-rs/ViewWindow.ts +1 -1
- package/src/ts/virtual_server.ts +5 -0
- package/src/ts/virtual_servers/clickhouse.ts +3 -13
- package/src/ts/virtual_servers/duckdb.ts +39 -69
- package/tsconfig.json +1 -0
|
@@ -12,6 +12,7 @@ export const client___getClassname: (a: number, b: number) => void;
|
|
|
12
12
|
export const client_get_hosted_table_names: (a: number) => number;
|
|
13
13
|
export const client_handle_error: (a: number, b: number, c: number, d: number) => number;
|
|
14
14
|
export const client_handle_response: (a: number, b: number) => number;
|
|
15
|
+
export const client_join: (a: number, b: number, c: number, d: number, e: number, f: number) => number;
|
|
15
16
|
export const client_new: (a: number, b: number, c: number) => void;
|
|
16
17
|
export const client_new_proxy_session: (a: number, b: number) => number;
|
|
17
18
|
export const client_on_error: (a: number, b: number) => number;
|
|
@@ -30,6 +31,7 @@ export const genericsqlvirtualservermodel_tableValidateExpression: (a: number, b
|
|
|
30
31
|
export const genericsqlvirtualservermodel_viewColumnSize: (a: number, b: number, c: number, d: number) => void;
|
|
31
32
|
export const genericsqlvirtualservermodel_viewDelete: (a: number, b: number, c: number, d: number) => void;
|
|
32
33
|
export const genericsqlvirtualservermodel_viewGetData: (a: number, b: number, c: number, d: number, e: number, f: number, g: number) => void;
|
|
34
|
+
export const genericsqlvirtualservermodel_viewGetMinMax: (a: number, b: number, c: number, d: number, e: number, f: number, g: number) => void;
|
|
33
35
|
export const genericsqlvirtualservermodel_viewSchema: (a: number, b: number, c: number, d: number) => void;
|
|
34
36
|
export const genericsqlvirtualservermodel_viewSize: (a: number, b: number, c: number, d: number) => void;
|
|
35
37
|
export const init: () => void;
|
|
@@ -78,6 +80,7 @@ export const view_to_csv: (a: number, b: number) => number;
|
|
|
78
80
|
export const view_to_json: (a: number, b: number) => number;
|
|
79
81
|
export const view_to_json_string: (a: number, b: number) => number;
|
|
80
82
|
export const view_to_ndjson: (a: number, b: number) => number;
|
|
83
|
+
export const virtualdataslice_fromArrowIpc: (a: number, b: number, c: number) => void;
|
|
81
84
|
export const virtualdataslice_new: (a: number) => number;
|
|
82
85
|
export const virtualdataslice_setBooleanCol: (a: number, b: number, c: number, d: number, e: number, f: number, g: number) => void;
|
|
83
86
|
export const virtualdataslice_setCol: (a: number, b: number, c: number, d: number, e: number, f: number, g: number, h: number, i: number) => void;
|
|
@@ -87,11 +90,11 @@ export const virtualdataslice_setIntegerCol: (a: number, b: number, c: number, d
|
|
|
87
90
|
export const virtualdataslice_setStringCol: (a: number, b: number, c: number, d: number, e: number, f: number, g: number) => void;
|
|
88
91
|
export const virtualserver_handleRequest: (a: number, b: number, c: number) => number;
|
|
89
92
|
export const virtualserver_new: (a: number, b: number) => void;
|
|
90
|
-
export const
|
|
91
|
-
export const
|
|
92
|
-
export const
|
|
93
|
-
export const
|
|
94
|
-
export const
|
|
93
|
+
export const __wasm_bindgen_func_elem_838: (a: number, b: number) => void;
|
|
94
|
+
export const __wasm_bindgen_func_elem_3910: (a: number, b: number) => void;
|
|
95
|
+
export const __wasm_bindgen_func_elem_10782: (a: number, b: number, c: number, d: number) => void;
|
|
96
|
+
export const __wasm_bindgen_func_elem_3927: (a: number, b: number, c: number) => void;
|
|
97
|
+
export const __wasm_bindgen_func_elem_1727: (a: number, b: number) => number;
|
|
95
98
|
export const __wbindgen_export: (a: number, b: number) => number;
|
|
96
99
|
export const __wbindgen_export2: (a: number, b: number, c: number, d: number) => number;
|
|
97
100
|
export const __wbindgen_export3: (a: number) => void;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@perspective-dev/client",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.4.0",
|
|
4
4
|
"description": "",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -49,13 +49,13 @@
|
|
|
49
49
|
"@perspective-dev/metadata": "",
|
|
50
50
|
"@perspective-dev/test": "",
|
|
51
51
|
"@clickhouse/client-web": "^1.12.0",
|
|
52
|
-
"@duckdb/duckdb-wasm": "
|
|
53
|
-
"@playwright/experimental-ct-react": "=1.
|
|
54
|
-
"@playwright/test": "=1.
|
|
52
|
+
"@duckdb/duckdb-wasm": "1.33.1-dev18.0",
|
|
53
|
+
"@playwright/experimental-ct-react": "=1.58.0",
|
|
54
|
+
"@playwright/test": "=1.58.0",
|
|
55
55
|
"@types/node": ">=22",
|
|
56
56
|
"@types/ws": ">=8",
|
|
57
57
|
"@types/stoppable": ">=1",
|
|
58
|
-
"apache-arrow": "
|
|
58
|
+
"apache-arrow": "17.0.0",
|
|
59
59
|
"superstore-arrow": "3.2.0",
|
|
60
60
|
"lodash": "^4.17.20",
|
|
61
61
|
"moment": "^2.30.1",
|
package/src/rust/client.rs
CHANGED
|
@@ -20,7 +20,7 @@ use js_sys::{Function, Uint8Array};
|
|
|
20
20
|
#[cfg(doc)]
|
|
21
21
|
use perspective_client::SystemInfo;
|
|
22
22
|
use perspective_client::{
|
|
23
|
-
ClientError, ReconnectCallback, Session, TableData, TableInitOptions, asyncfn,
|
|
23
|
+
ClientError, ReconnectCallback, Session, TableData, TableInitOptions, TableRef, asyncfn,
|
|
24
24
|
};
|
|
25
25
|
use wasm_bindgen::prelude::*;
|
|
26
26
|
use wasm_bindgen_derive::TryFromJsValue;
|
|
@@ -35,6 +35,35 @@ extern "C" {
|
|
|
35
35
|
#[derive(Clone)]
|
|
36
36
|
#[wasm_bindgen(typescript_type = "TableInitOptions")]
|
|
37
37
|
pub type JsTableInitOptions;
|
|
38
|
+
|
|
39
|
+
#[derive(Clone)]
|
|
40
|
+
#[wasm_bindgen(typescript_type = "JoinOptions")]
|
|
41
|
+
pub type JsJoinOptions;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
async fn js_to_table_ref(val: &JsValue) -> ApiResult<TableRef> {
|
|
45
|
+
if let Some(name) = val.as_string() {
|
|
46
|
+
Ok(TableRef::from(name))
|
|
47
|
+
} else {
|
|
48
|
+
let get_name = js_sys::Reflect::get(val, &wasm_bindgen::intern("get_name").into())
|
|
49
|
+
.map_err(|_| apierror!(TableRefError))?
|
|
50
|
+
.dyn_into::<js_sys::Function>()
|
|
51
|
+
.map_err(|_| apierror!(TableRefError))?;
|
|
52
|
+
|
|
53
|
+
let promise = get_name
|
|
54
|
+
.call0(val)
|
|
55
|
+
.map_err(|_| apierror!(TableRefError))?
|
|
56
|
+
.dyn_into::<js_sys::Promise>()
|
|
57
|
+
.map_err(|_| apierror!(TableRefError))?;
|
|
58
|
+
|
|
59
|
+
let name = wasm_bindgen_futures::JsFuture::from(promise)
|
|
60
|
+
.await
|
|
61
|
+
.map_err(|_| apierror!(TableRefError))?
|
|
62
|
+
.as_string()
|
|
63
|
+
.ok_or_else(|| apierror!(TableRefError))?;
|
|
64
|
+
|
|
65
|
+
Ok(TableRef::from(name))
|
|
66
|
+
}
|
|
38
67
|
}
|
|
39
68
|
|
|
40
69
|
#[wasm_bindgen]
|
|
@@ -382,6 +411,46 @@ impl Client {
|
|
|
382
411
|
Ok(Table(self.client.table(args, options).await?))
|
|
383
412
|
}
|
|
384
413
|
|
|
414
|
+
/// Creates a new read-only [`Table`] by performing an INNER JOIN on two
|
|
415
|
+
/// source tables. The resulting table is reactive: when either source
|
|
416
|
+
/// table is updated, the join is automatically recomputed.
|
|
417
|
+
///
|
|
418
|
+
/// # Arguments
|
|
419
|
+
///
|
|
420
|
+
/// - `left` - The left source table (a [`Table`] instance or a table name
|
|
421
|
+
/// string).
|
|
422
|
+
/// - `right` - The right source table (a [`Table`] instance or a table name
|
|
423
|
+
/// string).
|
|
424
|
+
/// - `on` - The column name to join on. Must exist in both tables with the
|
|
425
|
+
/// same type.
|
|
426
|
+
/// - `options` - Optional join configuration: `{ join_type?: "inner" |
|
|
427
|
+
/// "left" | "outer", name?: string }`.
|
|
428
|
+
///
|
|
429
|
+
/// # JavaScript Examples
|
|
430
|
+
///
|
|
431
|
+
/// ```javascript
|
|
432
|
+
/// const joined = await client.join(orders_table, products_table, "Product ID", { join_type: "left" });
|
|
433
|
+
/// const joined = await client.join("orders", "products", "Product ID", { join_type: "left" });
|
|
434
|
+
/// ```
|
|
435
|
+
#[wasm_bindgen]
|
|
436
|
+
pub async fn join(
|
|
437
|
+
&self,
|
|
438
|
+
left: JsValue,
|
|
439
|
+
right: JsValue,
|
|
440
|
+
on: &str,
|
|
441
|
+
options: Option<JsJoinOptions>,
|
|
442
|
+
) -> ApiResult<Table> {
|
|
443
|
+
let options = options
|
|
444
|
+
.into_serde_ext::<Option<perspective_client::JoinOptions>>()?
|
|
445
|
+
.unwrap_or_default();
|
|
446
|
+
|
|
447
|
+
let left_ref = js_to_table_ref(&left).await?;
|
|
448
|
+
let right_ref = js_to_table_ref(&right).await?;
|
|
449
|
+
Ok(Table(
|
|
450
|
+
self.client.join(left_ref, right_ref, on, options).await?,
|
|
451
|
+
))
|
|
452
|
+
}
|
|
453
|
+
|
|
385
454
|
/// Terminates this [`Client`], cleaning up any [`crate::View`] handles the
|
|
386
455
|
/// [`Client`] has open as well as its callbacks.
|
|
387
456
|
#[wasm_bindgen]
|
|
@@ -157,6 +157,22 @@ impl GenericSQLVirtualServerModel {
|
|
|
157
157
|
.view_size(view_id)
|
|
158
158
|
.map_err(|e| JsValue::from_str(&e.to_string()))
|
|
159
159
|
}
|
|
160
|
+
|
|
161
|
+
/// Returns the SQL query to get the min and max values of a column.
|
|
162
|
+
#[wasm_bindgen(js_name = "viewGetMinMax")]
|
|
163
|
+
pub fn view_get_min_max(
|
|
164
|
+
&self,
|
|
165
|
+
view_id: &str,
|
|
166
|
+
column_name: &str,
|
|
167
|
+
config: JsValue,
|
|
168
|
+
) -> Result<String, JsValue> {
|
|
169
|
+
let config: ViewConfig = serde_wasm_bindgen::from_value(config)
|
|
170
|
+
.map_err(|e| JsValue::from_str(&e.to_string()))?;
|
|
171
|
+
|
|
172
|
+
self.inner
|
|
173
|
+
.view_get_min_max(view_id, column_name, &config)
|
|
174
|
+
.map_err(|e| JsValue::from_str(&e.to_string()))
|
|
175
|
+
}
|
|
160
176
|
}
|
|
161
177
|
|
|
162
178
|
impl GenericSQLVirtualServerModel {
|
package/src/rust/lib.rs
CHANGED
|
@@ -59,11 +59,15 @@ export type * from "../../src/ts/ts-rs/SystemInfo.d.ts";
|
|
|
59
59
|
export type * from "../../src/ts/ts-rs/SortDir.d.ts";
|
|
60
60
|
export type * from "../../src/ts/ts-rs/Filter.d.ts";
|
|
61
61
|
export type * from "../../src/ts/ts-rs/ViewConfig.d.ts";
|
|
62
|
+
export type * from "../../src/ts/ts-rs/JoinOptions.ts";
|
|
63
|
+
export type * from "../../src/ts/ts-rs/JoinType.ts";
|
|
62
64
|
|
|
63
65
|
import type {ColumnWindow} from "../../src/ts/ts-rs/ColumnWindow.d.ts";
|
|
64
66
|
import type {ColumnType} from "../../src/ts/ts-rs/ColumnType.d.ts";
|
|
65
67
|
import type {ViewWindow} from "../../src/ts/ts-rs/ViewWindow.d.ts";
|
|
66
68
|
import type {TableInitOptions} from "../../src/ts/ts-rs/TableInitOptions.d.ts";
|
|
69
|
+
import type {JoinOptions} from "../../src/ts/ts-rs/JoinOptions.ts";
|
|
70
|
+
import type {JoinType} from "../../src/ts/ts-rs/JoinType.ts";
|
|
67
71
|
import type {ViewConfigUpdate} from "../../src/ts/ts-rs/ViewConfigUpdate.d.ts";
|
|
68
72
|
import type * as on_update_args from "../../src/ts/ts-rs/ViewOnUpdateResp.d.ts";
|
|
69
73
|
import type {OnUpdateOptions} from "../../src/ts/ts-rs/OnUpdateOptions.d.ts";
|
package/src/rust/utils/errors.rs
CHANGED
|
@@ -97,6 +97,9 @@ pub enum ApiErrorType {
|
|
|
97
97
|
#[error("Invalid `expressions` {}", format_valid_exprs(.0))]
|
|
98
98
|
InvalidViewerConfigExpressionsError(Rc<ExprValidationResult>),
|
|
99
99
|
|
|
100
|
+
#[error("Expected a Table or string table name")]
|
|
101
|
+
TableRefError,
|
|
102
|
+
|
|
100
103
|
#[error("No `Table` attached")]
|
|
101
104
|
NoTableError,
|
|
102
105
|
|
|
@@ -134,6 +137,7 @@ impl ApiError {
|
|
|
134
137
|
ApiErrorType::ProstError(_) => "[ProstError]",
|
|
135
138
|
ApiErrorType::InvalidViewerConfigError(..) => "[InvalidViewerConfigError]",
|
|
136
139
|
ApiErrorType::InvalidViewerConfigExpressionsError(_) => "[InvalidViewerConfigError]",
|
|
140
|
+
ApiErrorType::TableRefError => "[TableRefError]",
|
|
137
141
|
ApiErrorType::NoTableError => "[NoTableError]",
|
|
138
142
|
ApiErrorType::SerdeWasmBindgenError(_) => "[SerdeWasmBindgenError]",
|
|
139
143
|
ApiErrorType::Utf8Error(_) => "[FromUtf8Error]",
|
|
@@ -64,6 +64,10 @@ where
|
|
|
64
64
|
pub fn spawn<U: Future<Output = ApiResult<T>> + 'static>(x: U) {
|
|
65
65
|
drop(js_sys::Promise::from(Self::new(x)))
|
|
66
66
|
}
|
|
67
|
+
|
|
68
|
+
pub fn spawn_throttled<U: Future<Output = ApiResult<T>> + 'static>(x: U) {
|
|
69
|
+
drop(js_sys::Promise::from(Self::new_throttled(x)))
|
|
70
|
+
}
|
|
67
71
|
}
|
|
68
72
|
|
|
69
73
|
impl<T> Default for ApiFuture<T>
|
|
@@ -90,12 +94,7 @@ where
|
|
|
90
94
|
Result<T, JsValue>: IntoJsResult + 'static,
|
|
91
95
|
{
|
|
92
96
|
fn from(fut: ApiFuture<T>) -> Self {
|
|
93
|
-
future_to_promise(async move {
|
|
94
|
-
match fut.0.await.ignore_view_delete()? {
|
|
95
|
-
Some(x) => Ok(x).into_js_result(),
|
|
96
|
-
None => Err("View not found".into()).into_js_result(),
|
|
97
|
-
}
|
|
98
|
-
})
|
|
97
|
+
future_to_promise(async move { Ok(fut.0.await?).into_js_result() })
|
|
99
98
|
}
|
|
100
99
|
}
|
|
101
100
|
|
package/src/rust/view.rs
CHANGED
|
@@ -47,6 +47,15 @@ impl From<ViewWindow> for JsViewWindow {
|
|
|
47
47
|
}
|
|
48
48
|
}
|
|
49
49
|
|
|
50
|
+
fn scalar_to_jsvalue(scalar: &perspective_client::config::Scalar) -> JsValue {
|
|
51
|
+
match scalar {
|
|
52
|
+
perspective_client::config::Scalar::Float(x) => JsValue::from_f64(*x),
|
|
53
|
+
perspective_client::config::Scalar::String(x) => JsValue::from_str(x),
|
|
54
|
+
perspective_client::config::Scalar::Bool(x) => JsValue::from_bool(*x),
|
|
55
|
+
perspective_client::config::Scalar::Null => JsValue::NULL,
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
50
59
|
/// The [`View`] struct is Perspective's query and serialization interface. It
|
|
51
60
|
/// represents a query on the `Table`'s dataset and is always created from an
|
|
52
61
|
/// existing `Table` instance via the [`Table::view`] method.
|
|
@@ -147,10 +156,10 @@ impl View {
|
|
|
147
156
|
#[wasm_bindgen]
|
|
148
157
|
pub async fn get_min_max(&self, name: String) -> ApiResult<Array> {
|
|
149
158
|
let result = self.0.get_min_max(name).await?;
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
159
|
+
let arr = Array::new();
|
|
160
|
+
arr.push(&scalar_to_jsvalue(&result.0));
|
|
161
|
+
arr.push(&scalar_to_jsvalue(&result.1));
|
|
162
|
+
Ok(arr)
|
|
154
163
|
}
|
|
155
164
|
|
|
156
165
|
/// The number of aggregated rows in this [`View`]. This is affected by the
|
|
@@ -18,7 +18,7 @@ use std::str::FromStr;
|
|
|
18
18
|
use std::sync::{Arc, Mutex};
|
|
19
19
|
|
|
20
20
|
use indexmap::IndexMap;
|
|
21
|
-
use js_sys::{Array, Date, Object, Reflect};
|
|
21
|
+
use js_sys::{Array, Date, Object, Reflect, Uint8Array};
|
|
22
22
|
use perspective_client::proto::{ColumnType, HostedTable};
|
|
23
23
|
use perspective_client::virtual_server;
|
|
24
24
|
use perspective_client::virtual_server::{Features, ResultExt, VirtualServerHandler};
|
|
@@ -60,6 +60,20 @@ impl From<serde_wasm_bindgen::Error> for JsError {
|
|
|
60
60
|
}
|
|
61
61
|
}
|
|
62
62
|
|
|
63
|
+
fn jsvalue_to_scalar(val: &JsValue) -> perspective_client::config::Scalar {
|
|
64
|
+
if val.is_null() || val.is_undefined() {
|
|
65
|
+
perspective_client::config::Scalar::Null
|
|
66
|
+
} else if let Some(b) = val.as_bool() {
|
|
67
|
+
perspective_client::config::Scalar::Bool(b)
|
|
68
|
+
} else if let Some(n) = val.as_f64() {
|
|
69
|
+
perspective_client::config::Scalar::Float(n)
|
|
70
|
+
} else if let Some(s) = val.as_string() {
|
|
71
|
+
perspective_client::config::Scalar::String(s)
|
|
72
|
+
} else {
|
|
73
|
+
perspective_client::config::Scalar::Null
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
63
77
|
pub struct JsServerHandler(Object);
|
|
64
78
|
|
|
65
79
|
impl JsServerHandler {
|
|
@@ -452,6 +466,48 @@ impl VirtualServerHandler for JsServerHandler {
|
|
|
452
466
|
})
|
|
453
467
|
}
|
|
454
468
|
|
|
469
|
+
fn view_get_min_max(
|
|
470
|
+
&self,
|
|
471
|
+
view_id: &str,
|
|
472
|
+
column_name: &str,
|
|
473
|
+
config: &perspective_client::config::ViewConfig,
|
|
474
|
+
) -> HandlerFuture<
|
|
475
|
+
Result<
|
|
476
|
+
(
|
|
477
|
+
perspective_client::config::Scalar,
|
|
478
|
+
perspective_client::config::Scalar,
|
|
479
|
+
),
|
|
480
|
+
Self::Error,
|
|
481
|
+
>,
|
|
482
|
+
> {
|
|
483
|
+
let has_method = Reflect::get(&self.0, &JsValue::from_str("viewGetMinMax"))
|
|
484
|
+
.map(|val| !val.is_undefined())
|
|
485
|
+
.unwrap_or(false);
|
|
486
|
+
|
|
487
|
+
if !has_method {
|
|
488
|
+
return Box::pin(async {
|
|
489
|
+
Err(JsError(JsValue::from_str("viewGetMinMax not implemented")))
|
|
490
|
+
});
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
let handler = self.0.clone();
|
|
494
|
+
let view_id = view_id.to_string();
|
|
495
|
+
let column_name = column_name.to_string();
|
|
496
|
+
let config_js = serde_wasm_bindgen::to_value(config).unwrap();
|
|
497
|
+
Box::pin(async move {
|
|
498
|
+
let this = JsServerHandler(handler);
|
|
499
|
+
let args = Array::new();
|
|
500
|
+
args.push(&JsValue::from_str(&view_id));
|
|
501
|
+
args.push(&JsValue::from_str(&column_name));
|
|
502
|
+
args.push(&config_js);
|
|
503
|
+
let result = this.call_method_js_async("viewGetMinMax", &args).await?;
|
|
504
|
+
let obj = result.dyn_ref::<Object>().unwrap();
|
|
505
|
+
let min_val = Reflect::get(obj, &JsValue::from_str(wasm_bindgen::intern("min")))?;
|
|
506
|
+
let max_val = Reflect::get(obj, &JsValue::from_str(wasm_bindgen::intern("max")))?;
|
|
507
|
+
Ok((jsvalue_to_scalar(&min_val), jsvalue_to_scalar(&max_val)))
|
|
508
|
+
})
|
|
509
|
+
}
|
|
510
|
+
|
|
455
511
|
fn view_get_data(
|
|
456
512
|
&self,
|
|
457
513
|
view_id: &str,
|
|
@@ -531,6 +587,17 @@ impl VirtualDataSlice {
|
|
|
531
587
|
)
|
|
532
588
|
}
|
|
533
589
|
|
|
590
|
+
#[wasm_bindgen(js_name = "fromArrowIpc")]
|
|
591
|
+
pub fn from_arrow_ipc(&self, ipc: Uint8Array) -> Result<(), JsValue> {
|
|
592
|
+
self.1
|
|
593
|
+
.lock()
|
|
594
|
+
.unwrap()
|
|
595
|
+
.as_mut()
|
|
596
|
+
.unwrap()
|
|
597
|
+
.from_arrow_ipc(&ipc.to_vec())
|
|
598
|
+
.map_err(|e| JsValue::from_str(&e.to_string()))
|
|
599
|
+
}
|
|
600
|
+
|
|
534
601
|
#[wasm_bindgen(js_name = "setCol")]
|
|
535
602
|
pub fn set_col(
|
|
536
603
|
&self,
|
|
@@ -273,6 +273,23 @@ export function on_error(callback: Function) {
|
|
|
273
273
|
return SYNC_CLIENT.on_error(callback);
|
|
274
274
|
}
|
|
275
275
|
|
|
276
|
+
/**
|
|
277
|
+
* Create a read-only table from a JOIN of two source tables.
|
|
278
|
+
* @param left - The left source table (a Table instance or a table name string).
|
|
279
|
+
* @param right - The right source table (a Table instance or a table name string).
|
|
280
|
+
* @param on
|
|
281
|
+
* @param options - Optional join configuration: { join_type?: "inner"|"left"|"outer", name?: string }
|
|
282
|
+
* @returns
|
|
283
|
+
*/
|
|
284
|
+
export function join(
|
|
285
|
+
left: perspective_client.Table | string,
|
|
286
|
+
right: perspective_client.Table | string,
|
|
287
|
+
on: string,
|
|
288
|
+
options?: perspective_client.JoinOptions,
|
|
289
|
+
) {
|
|
290
|
+
return SYNC_CLIENT.join(left as any, right as any, on, options);
|
|
291
|
+
}
|
|
292
|
+
|
|
276
293
|
/**
|
|
277
294
|
* Create a table from the global Perspective instance.
|
|
278
295
|
* @param init_data
|
|
@@ -356,6 +373,7 @@ export { perspective_client as wasmModule };
|
|
|
356
373
|
|
|
357
374
|
export default {
|
|
358
375
|
table,
|
|
376
|
+
join,
|
|
359
377
|
websocket,
|
|
360
378
|
worker,
|
|
361
379
|
get_hosted_table_names,
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
|
|
2
|
+
import type { JoinType } from "./JoinType.js";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Options for [`Client::join`].
|
|
6
|
+
*/
|
|
7
|
+
export type JoinOptions = { join_type?: JoinType, name?: string, right_on?: string, };
|
|
@@ -3,6 +3,7 @@ import type { Aggregate } from "./Aggregate.js";
|
|
|
3
3
|
import type { Expressions } from "./Expressions.js";
|
|
4
4
|
import type { Filter } from "./Filter.js";
|
|
5
5
|
import type { FilterReducer } from "./FilterReducer.js";
|
|
6
|
+
import type { GroupRollupMode } from "./GroupRollupMode.js";
|
|
6
7
|
import type { Sort } from "./Sort.js";
|
|
7
8
|
|
|
8
|
-
export type ViewConfig = { group_by: Array<string>, split_by: Array<string>, sort: Array<Sort>, filter: Array<Filter>, filter_op?: FilterReducer, expressions: Expressions, columns: Array<string | null>, aggregates: { [key in string]?: Aggregate }, group_by_depth?: number | null, };
|
|
9
|
+
export type ViewConfig = { group_by: Array<string>, split_by: Array<string>, sort: Array<Sort>, filter: Array<Filter>, group_rollup_mode: GroupRollupMode, filter_op?: FilterReducer, expressions: Expressions, columns: Array<string | null>, aggregates: { [key in string]?: Aggregate }, group_by_depth?: number | null, };
|
|
@@ -3,6 +3,7 @@ import type { Aggregate } from "./Aggregate.js";
|
|
|
3
3
|
import type { Expressions } from "./Expressions.js";
|
|
4
4
|
import type { Filter } from "./Filter.js";
|
|
5
5
|
import type { FilterReducer } from "./FilterReducer.js";
|
|
6
|
+
import type { GroupRollupMode } from "./GroupRollupMode.js";
|
|
6
7
|
import type { Sort } from "./Sort.js";
|
|
7
8
|
|
|
8
9
|
export type ViewConfigUpdate = {
|
|
@@ -84,4 +85,4 @@ expressions?: Expressions,
|
|
|
84
85
|
* applied to columns in the `View` constructor using a dictionary of
|
|
85
86
|
* column name to aggregate function name.
|
|
86
87
|
*/
|
|
87
|
-
aggregates?: { [key in string]?: Aggregate }, group_by_depth?: number, filter_op?: FilterReducer, };
|
|
88
|
+
aggregates?: { [key in string]?: Aggregate }, group_by_depth?: number, filter_op?: FilterReducer, group_rollup_mode?: GroupRollupMode, };
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
* Some fields of [`ViewWindow`] are only applicable to specific methods of
|
|
7
7
|
* [`View`].
|
|
8
8
|
*/
|
|
9
|
-
export type ViewWindow = { start_row?: number, start_col?: number, end_row?: number, end_col?: number, id?: boolean, index?: boolean,
|
|
9
|
+
export type ViewWindow = { start_row?: number, start_col?: number, end_row?: number, end_col?: number, id?: boolean, index?: boolean,
|
|
10
10
|
/**
|
|
11
11
|
* Only impacts [`View::to_csv`]
|
|
12
12
|
*/
|
package/src/ts/virtual_server.ts
CHANGED
|
@@ -68,6 +68,11 @@ export interface VirtualServerHandler {
|
|
|
68
68
|
tableId: string,
|
|
69
69
|
expression: string,
|
|
70
70
|
): ColumnType | Promise<ColumnType>;
|
|
71
|
+
viewGetMinMax?(
|
|
72
|
+
viewId: string,
|
|
73
|
+
columnName: string,
|
|
74
|
+
config: ViewConfig,
|
|
75
|
+
): { min: any; max: any } | Promise<{ min: any; max: any }>;
|
|
71
76
|
getFeatures?(): ServerFeatures | Promise<ServerFeatures>;
|
|
72
77
|
makeTable?(
|
|
73
78
|
tableId: string,
|
|
@@ -223,6 +223,7 @@ export class ClickhouseHandler implements perspective.VirtualServerHandler {
|
|
|
223
223
|
split_by: false,
|
|
224
224
|
sort: true,
|
|
225
225
|
expressions: true,
|
|
226
|
+
group_rollup_mode: ["rollup", "flat", "total"],
|
|
226
227
|
filter_ops: {
|
|
227
228
|
integer: FILTER_OPS,
|
|
228
229
|
float: FILTER_OPS,
|
|
@@ -308,8 +309,6 @@ export class ClickhouseHandler implements perspective.VirtualServerHandler {
|
|
|
308
309
|
viewport: ViewWindow,
|
|
309
310
|
dataSlice: perspective.VirtualDataSlice,
|
|
310
311
|
) {
|
|
311
|
-
const is_group_by = config.group_by?.length > 0;
|
|
312
|
-
const is_split_by = config.split_by?.length > 0;
|
|
313
312
|
const query = this.sqlBuilder.viewGetData(
|
|
314
313
|
viewId,
|
|
315
314
|
config,
|
|
@@ -322,23 +321,14 @@ export class ClickhouseHandler implements perspective.VirtualServerHandler {
|
|
|
322
321
|
});
|
|
323
322
|
|
|
324
323
|
for (let cidx = 0; cidx < columns.length; cidx++) {
|
|
325
|
-
|
|
326
|
-
// This is the grouping_id column, skip it
|
|
327
|
-
continue;
|
|
328
|
-
}
|
|
329
|
-
|
|
330
|
-
let col = columns[cidx];
|
|
331
|
-
if (is_split_by && !col.startsWith("__ROW_PATH_")) {
|
|
332
|
-
col = col.replaceAll("_", "|");
|
|
333
|
-
}
|
|
334
|
-
|
|
324
|
+
const col = columns[cidx];
|
|
335
325
|
const dtype = duckdbTypeToPsp(dtypes[cidx]) as ColumnType;
|
|
336
326
|
|
|
337
327
|
const isDecimal = dtypes[cidx].startsWith("Decimal");
|
|
338
328
|
for (let ridx = 0; ridx < rows.length; ridx++) {
|
|
339
329
|
const row = rows[ridx];
|
|
340
330
|
const grouping_id = row["__GROUPING_ID__"];
|
|
341
|
-
let value = row[
|
|
331
|
+
let value = row[col];
|
|
342
332
|
if (isDecimal) {
|
|
343
333
|
value = convertDecimalToNumber(value, dtypes[cidx]);
|
|
344
334
|
}
|