Join
The Join type describes a function that lets you specify a Cell or calculated value to join the main query Table to other Tables, by their Row Id.
Calling this function with two Id parameters will indicate that the join to a Row in an adjacent Table is made by finding its Id in a Cell of the query's root Table.
(
joinedTableId: string,
on: string,
): JoinedAs| Type | Description | |
|---|---|---|
joinedTableId | string | |
on | string | The |
| returns | JoinedAs | A |
Since
v2.0.0
Calling this function with two parameters (where the second is a function) will indicate that the join to a Row in an adjacent Table is made by calculating its Id from the Cells and the Row Id of the query's root Table.
(
joinedTableId: string,
on: (getCell: GetCell, rowId: string) => undefined | string,
): JoinedAs| Type | Description | |
|---|---|---|
joinedTableId | string | |
on | (getCell: GetCell, rowId: string) => undefined | string | A callback that takes a |
| returns | JoinedAs | A |
Since
v2.0.0
Calling this function with three Id parameters will indicate that the join to a Row in distant Table is made by finding its Id in a Cell of an intermediately joined Table.
(
joinedTableId: string,
fromIntermediateJoinedTableId: string,
on: string,
): JoinedAs| Type | Description | |
|---|---|---|
joinedTableId | string | |
fromIntermediateJoinedTableId | string | The |
on | string | The |
| returns | JoinedAs | A |
Since
v2.0.0
Calling this function with three parameters (where the third is a function) will indicate that the join to a Row in distant Table is made by calculating its Id from the Cells and the Row Id of an intermediately joined Table.
(
joinedTableId: string,
fromIntermediateJoinedTableId: string,
on: (getIntermediateJoinedCell: GetCell, intermediateJoinedRowId: string) => undefined | string,
): JoinedAs| Type | Description | |
|---|---|---|
joinedTableId | string | |
fromIntermediateJoinedTableId | string | The |
on | (getIntermediateJoinedCell: GetCell, intermediateJoinedRowId: string) => undefined | string | A callback that takes a |
| returns | JoinedAs | A |
Since
v2.0.0
The Join function is provided to the third query parameter of the setQueryDefinition method.
You can join zero, one, or many Tables. You can join the same underlying Table multiple times, but in that case you will need to use the 'as' function to distinguish them from each other.
By default, each join is made from the main query Table to the joined table, but it is also possible to connect via an intermediate join Table to a more distant join Table.
Because a Join clause is used to identify which unique Row Id of the joined Table will be joined to each Row of the root Table, queries follow the 'left join' semantics you may be familiar with from SQL. This means that an unfiltered query will only ever return the same number of Rows as the main Table being queried, and indeed the resulting table (assuming it has not been aggregated) will even preserve the root Table's original Row Ids.
Examples
This example shows a query that joins a single Table by using an Id present in the main query Table.
import {createQueries, createStore} from 'tinybase';
const store = createStore()
.setTable('pets', {
fido: {species: 'dog', ownerId: '1'},
felix: {species: 'cat', ownerId: '2'},
cujo: {species: 'dog', ownerId: '3'},
})
.setTable('owners', {
'1': {name: 'Alice'},
'2': {name: 'Bob'},
'3': {name: 'Carol'},
});
const queries = createQueries(store);
queries.setQueryDefinition('query', 'pets', ({select, join}) => {
select('species');
select('owners', 'name');
// from pets
join('owners', 'ownerId');
});
queries.forEachResultRow('query', (rowId) => {
console.log({[rowId]: queries.getResultRow('query', rowId)});
});
// -> {fido: {species: 'dog', name: 'Alice'}}
// -> {felix: {species: 'cat', name: 'Bob'}}
// -> {cujo: {species: 'dog', name: 'Carol'}}
This example shows a query that joins the same underlying Table twice, and aliases them (and the selected Cell Ids). Note the left-join semantics: Felix the cat was bought, but the seller was unknown. The record still exists in the ResultTable.
import {createQueries, createStore} from 'tinybase';
const store = createStore()
.setTable('pets', {
fido: {species: 'dog', buyerId: '1', sellerId: '2'},
felix: {species: 'cat', buyerId: '2'},
cujo: {species: 'dog', buyerId: '3', sellerId: '1'},
})
.setTable('humans', {
'1': {name: 'Alice'},
'2': {name: 'Bob'},
'3': {name: 'Carol'},
});
const queries = createQueries(store);
queries.setQueryDefinition('query', 'pets', ({select, join}) => {
select('buyers', 'name').as('buyer');
select('sellers', 'name').as('seller');
// from pets
join('humans', 'buyerId').as('buyers');
join('humans', 'sellerId').as('sellers');
});
queries.forEachResultRow('query', (rowId) => {
console.log({[rowId]: queries.getResultRow('query', rowId)});
});
// -> {fido: {buyer: 'Alice', seller: 'Bob'}}
// -> {felix: {buyer: 'Bob'}}
// -> {cujo: {buyer: 'Carol', seller: 'Alice'}}
This example shows a query that calculates the Id of the joined Table based from multiple values in the root Table rather than a single Cell.
import {createQueries, createStore} from 'tinybase';
const store = createStore()
.setTable('pets', {
fido: {species: 'dog', color: 'brown'},
felix: {species: 'cat', color: 'black'},
cujo: {species: 'dog', color: 'black'},
})
.setTable('colorSpecies', {
'brown-dog': {price: 6},
'black-dog': {price: 5},
'brown-cat': {price: 4},
'black-cat': {price: 3},
});
const queries = createQueries(store);
queries.setQueryDefinition('query', 'pets', ({select, join}) => {
select('colorSpecies', 'price');
// from pets
join(
'colorSpecies',
(getCell) => `${getCell('color')}-${getCell('species')}`,
);
});
queries.forEachResultRow('query', (rowId) => {
console.log({[rowId]: queries.getResultRow('query', rowId)});
});
// -> {fido: {price: 6}}
// -> {felix: {price: 3}}
// -> {cujo: {price: 5}}
This example shows a query that joins two Tables, one through the intermediate other.
import {createQueries, createStore} from 'tinybase';
const store = createStore()
.setTable('pets', {
fido: {species: 'dog', ownerId: '1'},
felix: {species: 'cat', ownerId: '2'},
cujo: {species: 'dog', ownerId: '3'},
})
.setTable('owners', {
'1': {name: 'Alice', state: 'CA'},
'2': {name: 'Bob', state: 'CA'},
'3': {name: 'Carol', state: 'WA'},
})
.setTable('states', {
CA: {name: 'California'},
WA: {name: 'Washington'},
});
const queries = createQueries(store);
queries.setQueryDefinition('query', 'pets', ({select, join}) => {
select(
(getTableCell) =>
`${getTableCell('species')} in ${getTableCell('states', 'name')}`,
).as('description');
// from pets
join('owners', 'ownerId');
join('states', 'owners', 'state');
});
queries.forEachResultRow('query', (rowId) => {
console.log({[rowId]: queries.getResultRow('query', rowId)});
});
// -> {fido: {description: 'dog in California'}}
// -> {felix: {description: 'cat in California'}}
// -> {cujo: {description: 'dog in Washington'}}
Since
v2.0.0