Sorting and filtering with Client Side Refiner

Client Side Refiner is used to lighten the load on Transformer Refiner by sorting and filtering subsets of data on the client side.

Available from: StreamLinkTS 7.1.16+

Should I use Transformer Refiner or Client Side Refiner?

Client Side Refiner best complements Transformer Refiner when using it to sort and filter small containers with few subjects, particularly when the data is rapidly changing. This means fewer requests to the server, and quicker sort and filter operations.

Client Side Refiner is easier to customise quickly if you value being able to write custom sort and filter functions in TypeScript in the client.

Enabling Client Side Refiner

Client Side Refiner is disabled by default. To enable Client Side Refiner, pass the following arguments to StreamLinkFactory.create when creating a StreamLink instance.

var sl = StreamLink.StreamLinkFactory.create({
    // This URL is an example – use the Liberator URL according to your setup.
    liberator_urls: "ws://localhost:18080",
    clientside_refiner_enable: true
})

Configuring Client Side Refiner

Configure Client Side Refiner by setting the following keys in the configuration object you pass to StreamLinkFactory.create. A full list of configuration-object keys can be found in the documentation for StreamLinkFactory.

clientside_refiner_enable

Default: false | Type: boolean

When set to true, enables the Client Side Refiner and allows other arguments to take effect.

clientside_refiner_fn_use_for

Default: undefined | Type: function

If clientside_refiner_enable is true, this function determines if individual container subscriptions should be refined on the client or server.

Returning true from this function will enable Client Side Refiner for the container subject matching the subject parameter.

Example:

Using Client Side Refiner for container subscriptions under EXAMPLES/PRICING
StreamLinkFactory.create({
    // ...
    clientside_refiner_fn_use_for: (subject: String, parameters: SubscriptionParameters): boolean => {
        return subject.includes("EXAMPLES/PRICING");
    }
})
clientside_refiner_custom_sorts

Default: undefined | Type: object

clientside_refiner_custom_sorts takes an object consisting of key-value pairs mapping custom sort names to their sort functions.

See Refiner subject syntax for how to construct subject sort parameters.

Example:

Sort items containing the text "Inc."
const daysOfWeek = ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday'];

function sortByDay(a: any, b: any): number  {
    const dayA = daysOfWeek.indexOf(a);
    const dayB = daysOfWeek.indexOf(b);

    if (dayA < dayB) {
        return -1;
    } else if (dayA > dayB) {
        return 1;
    } else {
        return 0;
    }
}

var streamlink = StreamLinkFactory.create({
    // ...
    clientside_refiner_custom_sorts: {
        sort_by_day: sortByDay
    }
});
clientside_refiner_custom_filters

Default: undefined | Type: object

clientside_refiner_custom_filters takes an object consisting of key-value pairs mapping custom filter names to their filter functions.

The filter functions can be one of two types:

  1. A comparator function with the signature (fieldValue: string, filterCriterion: string): number, similar to the functions provided to clientside_refiner_custom_sorts.

    This type should be used when the filter expression is of the form where:customFilterName:<fieldname>:<operator>:<value>.

  2. A function with the signature (fieldProvider: IFieldProvider): boolean, where fieldProvider contains the method getField(fieldName: string): any, which is used to get a field from the record being filtered.

    This type should be used when the filter expression is of the form where:x.

See Refiner subject syntax for how to construct subject filter parameters.

Examples:

Custom filter by grade (type A)
const grades = ['A', 'A+', 'AA-', 'AA', 'AA+', 'AAA'];

function gradeFilter(fieldValue: string, filterCriterion: string): number {

  const fieldValueRank = grades.indexOf(fieldValue);
  const filterCriterionRank = grades.indexOf(filterCriterion);

  if (fieldValueRank === filterCriterionRank) {
    return 0;
  } else if (fieldValueRank < filterCriterionRank) {
    return -1;
  } else {
    return 1;
  }
}

var streamLink = StreamLinkFactory.create({
    // ...
    clientside_refiner_custom_filters: {
        grade_filter: gradeFilter
    }
});
Custom filter by a combination of fields (type B)
function spreadTargetFilter(fieldProvider: { getField: (name: string) => any }): boolean {
    const bestAsk = Number(fieldProvider.getField("BestAsk"));
    const bestBid = Number(fieldProvider.getField("BestBid"));
    const spreadTarget = Number(fieldProvider.getField("SpreadTarget"));

    return bestAsk - bestBid >= spreadTarget;
}

var streamLink = StreamLinkFactory.create({
    // ...
    clientside_refiner_custom_filters: {
        spread_target_filter: spreadTargetFilter
    }
});