TinyBase logoTinyBase

Having

The Having type describes a function that lets you specify conditions to filter results, based on the grouped Cells resulting from a Group clause.

Calling this function with two parameters is used to include only those Rows for which a specified Cell in the query's root Table has a specified value.

(
  selectedOrGroupedCellId: Id,
  equals: Cell,
): void
TypeDescription
selectedOrGroupedCellIdId

The Id of the Cell in the query to test.

equalsCell

The value that the Cell has to have for the Row to be included in the result.

returnsvoid

This has no return value.


Calling this function with one callback parameter is used to include only those Rows which meet a calculated boolean condition.

(condition: (getSelectedOrGroupedCell: GetCell) => boolean): void
TypeDescription
condition(getSelectedOrGroupedCell: GetCell) => boolean

A callback that takes a GetCell function and that should return true for the Row to be included in the result.

returnsvoid

This has no return value.

The Having function is provided to the third query parameter of the setQueryDefinition method.

A Having condition has to be true for a Row to be included in the results. Each Having class is additive, as though combined with a logical 'and'. If you wish to create an 'or' expression, use the single parameter version of the type that allows arbitrary programmatic conditions.

The Where keyword differs from the Having keyword in that the former describes conditions that should be met by underlying Cell values (whether selected or not), and the latter describes conditions based on calculated and aggregated values - after Group clauses have been applied.

Whilst it is technically possible to use a Having clause even if the results have not been grouped with a Group clause, you should expect it to be less performant than using a Where clause, due to that being applied earlier in the query process.

Examples

This example shows a query that filters the results from a grouped Table by comparing a Cell from it with a value.

const store = createStore().setTable('pets', {
  fido: {species: 'dog', price: 5},
  felix: {species: 'cat', price: 4},
  cujo: {species: 'dog', price: 4},
  tom: {species: 'cat', price: 3},
  carnaby: {species: 'parrot', price: 3},
  polly: {species: 'parrot', price: 3},
});

const queries = createQueries(store);
queries.setQueryDefinition('query', 'pets', ({select, group, having}) => {
  select('pets', 'species');
  select('pets', 'price');
  group('price', 'min').as('minPrice');
  group('price', 'max').as('maxPrice');
  having('minPrice', 3);
});

queries.forEachResultRow('query', (rowId) => {
  console.log({[rowId]: queries.getResultRow('query', rowId)});
});
// -> {0: {species: 'cat', minPrice: 3, maxPrice: 4}}
// -> {1: {species: 'parrot', minPrice: 3, maxPrice: 3}}

This example shows a query that filters the results from a grouped Table with a condition that is calculated from Cell values.

const store = createStore().setTable('pets', {
  fido: {species: 'dog', price: 5},
  felix: {species: 'cat', price: 4},
  cujo: {species: 'dog', price: 4},
  tom: {species: 'cat', price: 3},
  carnaby: {species: 'parrot', price: 3},
  polly: {species: 'parrot', price: 3},
});

const queries = createQueries(store);
queries.setQueryDefinition('query', 'pets', ({select, group, having}) => {
  select('pets', 'species');
  select('pets', 'price');
  group('price', 'min').as('minPrice');
  group('price', 'max').as('maxPrice');
  having(
    (getSelectedOrGroupedCell) =>
      getSelectedOrGroupedCell('minPrice') !=
      getSelectedOrGroupedCell('maxPrice'),
  );
});

queries.forEachResultRow('query', (rowId) => {
  console.log({[rowId]: queries.getResultRow('query', rowId)});
});
// -> {0: {species: 'dog', minPrice: 4, maxPrice: 5}}
// -> {1: {species: 'cat', minPrice: 3, maxPrice: 4}}
// Parrots are filtered out because they have zero range in price.

Since

v2.0.0