Skip to content

Commit 3b622da

Browse files
committed
Client::join API
Signed-off-by: Andrew Stein <steinlink@gmail.com>
1 parent b0ee116 commit 3b622da

13 files changed

Lines changed: 822 additions & 2 deletions

File tree

rust/perspective-client/perspective.proto

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,7 @@ message Request {
157157
TableUpdateReq table_update_req = 33;
158158
ViewOnDeleteReq view_on_delete_req = 34;
159159
ViewRemoveDeleteReq view_remove_delete_req = 35;
160+
MakeJoinTableReq make_join_table_req = 38;
160161
}
161162
}
162163

@@ -199,6 +200,7 @@ message Response {
199200
TableUpdateResp table_update_resp = 33;
200201
ViewOnDeleteResp view_on_delete_resp = 34;
201202
ViewRemoveDeleteResp view_remove_delete_resp = 35;
203+
MakeJoinTableResp make_join_table_resp = 38;
202204
ServerError server_error = 50;
203205
}
204206
}
@@ -335,6 +337,14 @@ message MakeTableReq {
335337
}
336338
message MakeTableResp {}
337339

340+
// `Client::join` — create a read-only table from an INNER JOIN of two tables.
341+
message MakeJoinTableReq {
342+
string left_table_id = 1;
343+
string right_table_id = 2;
344+
string on_column = 3;
345+
}
346+
message MakeJoinTableResp {}
347+
338348
// `Table::delete`
339349
message TableDeleteReq {
340350
bool is_immediate = 1;

rust/perspective-client/src/rust/client.rs

Lines changed: 41 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,8 @@ use crate::proto::request::ClientReq;
2626
use crate::proto::response::ClientResp;
2727
use crate::proto::{
2828
ColumnType, GetFeaturesReq, GetFeaturesResp, GetHostedTablesReq, GetHostedTablesResp,
29-
HostedTable, MakeTableReq, RemoveHostedTablesUpdateReq, Request, Response, ServerError,
30-
ServerSystemInfoReq,
29+
HostedTable, MakeJoinTableReq, MakeTableReq, RemoveHostedTablesUpdateReq, Request, Response,
30+
ServerError, ServerSystemInfoReq,
3131
};
3232
use crate::table::{Table, TableInitOptions, TableOptions};
3333
use crate::table_data::{TableData, UpdateData};
@@ -589,6 +589,45 @@ impl Client {
589589
}
590590
}
591591

592+
/// Create a new read-only [`Table`] by performing an INNER JOIN on two
593+
/// source tables. The resulting table is reactive: when either source
594+
/// table is updated, the join is automatically recomputed.
595+
///
596+
/// # Arguments
597+
///
598+
/// * `left` - The left source table.
599+
/// * `right` - The right source table.
600+
/// * `on` - The column name to join on. Must exist in both tables with the
601+
/// same type.
602+
/// * `name` - Optional name for the resulting table.
603+
pub async fn join(
604+
&self,
605+
left: &Table,
606+
right: &Table,
607+
on: &str,
608+
name: Option<String>,
609+
) -> ClientResult<Table> {
610+
let entity_id = name.unwrap_or_else(randid);
611+
let msg = Request {
612+
msg_id: self.gen_id(),
613+
entity_id: entity_id.clone(),
614+
client_req: Some(ClientReq::MakeJoinTableReq(MakeJoinTableReq {
615+
left_table_id: left.get_name().to_owned(),
616+
right_table_id: right.get_name().to_owned(),
617+
on_column: on.to_owned(),
618+
})),
619+
};
620+
621+
let client = self.clone();
622+
match self.oneshot(&msg).await? {
623+
ClientResp::MakeJoinTableResp(_) => Ok(Table::new(entity_id, client, TableOptions {
624+
index: Some(on.to_owned()),
625+
limit: None,
626+
})),
627+
resp => Err(resp.into()),
628+
}
629+
}
630+
592631
async fn get_table_infos(&self) -> ClientResult<Vec<HostedTable>> {
593632
let msg = Request {
594633
msg_id: self.gen_id(),

rust/perspective-js/src/rust/client.rs

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -382,6 +382,34 @@ impl Client {
382382
Ok(Table(self.client.table(args, options).await?))
383383
}
384384

385+
/// Creates a new read-only [`Table`] by performing an INNER JOIN on two
386+
/// source tables. The resulting table is reactive: when either source
387+
/// table is updated, the join is automatically recomputed.
388+
///
389+
/// # Arguments
390+
///
391+
/// - `left` - The left source table.
392+
/// - `right` - The right source table.
393+
/// - `on` - The column name to join on. Must exist in both tables with the
394+
/// same type.
395+
/// - `name` - Optional name for the resulting table.
396+
///
397+
/// # JavaScript Examples
398+
///
399+
/// ```javascript
400+
/// const joined = await client.join(orders_table, products_table, "Product ID");
401+
/// ```
402+
#[wasm_bindgen]
403+
pub async fn join(
404+
&self,
405+
left: &Table,
406+
right: &Table,
407+
on: &str,
408+
name: Option<String>,
409+
) -> ApiResult<Table> {
410+
Ok(Table(self.client.join(&left.0, &right.0, on, name).await?))
411+
}
412+
385413
/// Terminates this [`Client`], cleaning up any [`crate::View`] handles the
386414
/// [`Client`] has open as well as its callbacks.
387415
#[wasm_bindgen]

rust/perspective-js/src/ts/perspective.node.ts

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -273,6 +273,23 @@ export function on_error(callback: Function) {
273273
return SYNC_CLIENT.on_error(callback);
274274
}
275275

276+
/**
277+
* Create a read-only table from an INNER JOIN of two source tables.
278+
* @param left
279+
* @param right
280+
* @param on
281+
* @param name
282+
* @returns
283+
*/
284+
export function join(
285+
left: perspective_client.Table,
286+
right: perspective_client.Table,
287+
on: string,
288+
name?: string,
289+
) {
290+
return SYNC_CLIENT.join(left, right, on, name);
291+
}
292+
276293
/**
277294
* Create a table from the global Perspective instance.
278295
* @param init_data
@@ -356,6 +373,7 @@ export { perspective_client as wasmModule };
356373

357374
export default {
358375
table,
376+
join,
359377
websocket,
360378
worker,
361379
get_hosted_table_names,

0 commit comments

Comments
 (0)