Search Operators

The search operators consume a Collection of Events and select a singular Event if possible, otherwise returning null.

If a non-collection queryable is passed as the input, the operator implicitly wraps it with a window operator, selecting the authoritative domain's window length of data.

Unary search operators

The unary search operators select an Event from a Collection based on static parameters.

first

The first operator collects events and maintains the top-1 item as defined by an arbitrary order. By default the ordering is by time.

The example below finds the earliest event in the bounding box defined by the mouse.

import { useDataTransformer } from '@electricui/timeseries-react'
import { useMouseSignal, MouseData } from '@electricui/components-desktop-charts'
import { first, collectBoundingBox } from '@electricui/dataflow'
 
const [mouseSignal, captureRef] = useMouseSignal()
 
const dataTransformer = useDataTransformer(() => {
const selected = collectBoundingBox(dataSource, mouseSignal, {
positionAccessor: (data, time, tags) => ({ x: data.x, y: data.y }),
searchBoundingBoxAccessor: (data, time, tags) => {
if (data.hasDragged) {
return ({ xMin: data.dragXMin, xMax: data.dragXMax, yMin: data.dragYMin, yMax: data.dragYMax })
}
return null
},
})
 
const firstEvent = first(selected)
 
return firstEvent
})

Alternatively, a top-N Collection can be maintained with the head operator.

last

The last operator collects events and maintains the bottom-1 item as defined by an arbitrary order. By default the ordering is by time.

It's usage is identical to the first operator above.

Alternatively, a bottom-N Collection can be maintained with the tail operator.

firstAndLast

The firstAndLast operator collects events and maintains both the top-1 and bottom-1 items as defined by an arbitrary order. By default the ordering is by time. If either event cannot be found, the entire result is null. If there is only one event, they will be presented as both first and last.

It's usage is identical to the first and last operators above, except the returned keyed by first or last.

The example below finds the first and events in the bounding box defined by the mouse.

import { useDataTransformer } from '@electricui/timeseries-react'
import { useMouseSignal, MouseData } from '@electricui/components-desktop-charts'
import { firstAndLast, collectBoundingBox } from '@electricui/dataflow'
 
const [mouseSignal, captureRef] = useMouseSignal()
 
const dataTransformer = useDataTransformer(() => {
const selected = collectBoundingBox(dataSource, mouseSignal, {
positionAccessor: (data, time, tags) => ({ x: data.x, y: data.y }),
searchBoundingBoxAccessor: (data, time, tags) => {
if (data.hasDragged) {
return ({ xMin: data.dragXMin, xMax: data.dragXMax, yMin: data.dragYMin, yMax: data.dragYMax })
}
return null
},
})
 
const firstEvent = firstAndLast(selected)
 
return firstEvent
})

Alternatively, a bottom-N Collection can be maintained with the tail operator.

Binary search operators

The binary search operators utilise an external source of data to drive the 'selection' of events.

closestTemporally

Screenshot of component operatorsSearch closestTemporally

The closestTemporally operator finds the closest Event to a target time.

The timeSourceAccessor is used to define the search time.

The method can be set to closest (the default), before or after. The search is exclusive of the search time if set to before or after.

The resulting event can be remapped with the mapResult and tagResult methods.

The example below finds the closest event before the mouse x coordinate.

import { useDataTransformer } from '@electricui/timeseries-react'
import { useMouseSignal, MouseData } from '@electricui/components-desktop-charts'
import { closestTemporally } from '@electricui/dataflow'
 
const [mouseSignal, captureRef] = useMouseSignal()
 
const dataTransformer = useDataTransformer(() => {
const closest = closestTemporally(dataSource, mouseSignal, {
// The mouse x position is the time to search for
timeSourceAccessor: (data, time, tags) => data.x,
// only search for Events 'before' the mouse position
method: 'before',
// map the result directly to the data, the default case
mapResult: (data, time, tags, searchData, searchTags, searchTime) => data
})
 
return closest
})

closestSpatially

Screenshot of component operatorsSearch closestSpatially

The closestSpatially operator finds the closest Event to a target time.

The positionAccessor defines the position of the Queryable's Events. Returning a null skips ingesting this Event.

The searchPositionAccessor defines the search point. Pass chartAspectRatio to modify the aspect ratio for perceptual distance adjustment. Mouse data sources contain this information so that the distance is in the pixel domain, rather than the data domain.

The predicate can be used to exclude candidates from the search result.

The maxDistance parameter can be used to limit the distance to search for.

The resulting event can be remapped with the mapResult and tagResult methods.

The example below finds the closest event to the mouse.

import { useDataTransformer } from '@electricui/timeseries-react'
import { useMouseSignal, MouseData } from '@electricui/components-desktop-charts'
import { closestSpatially } from '@electricui/dataflow'
 
const [mouseSignal, captureRef] = useMouseSignal()
 
const dataTransformer = useDataTransformer(() => {
const closest = closestSpatially(dataSource, mouseSignal, {
// Use the Event coordinates as the position
positionAccessor: (data, time, tags) => ({x: data.x, y: data.y}),
// The mouse x position is the time to search for
searchPositionAccessor: (data, time, tags) => ({
x: data.x,
y: data.y,
chartAspectRatio: data.chartAspectRatio
}),
// only search within 250 data units of the mouse
maxDistance: 250,
// Throw away odd numbered voltages
predicate: (
data,
time,
tags,
position,
searchData,
searchTags,
searchPoint,
distance,
) => data.voltage % 2 === 0,
// map the result directly to the data, the default case
mapResult: (data, time, tags, searchData, searchTags, searchTime) => data,
})
 
return closest
})

closestByValue

The closestByValue operator finds the closest Event ordered by an arbitrary value to a search value.

The valueAccessor defines the position of the Queryable's Events. Returning a null skips ingesting this Event.

The searchValueAccessor defines the search point. Returning a null resets the search.

The order method is used to totally order the Value. The distance method is used to calculate the distance between a pair of values.

The method can be set to closest (the default), below or above. The search is exclusive of the search value if set to below or above.

The predicate can be used to exclude candidates from the search result.

The resulting event can be remapped with the mapResult and tagResult methods.

The example below finds the event with the closest voltage to the mouse y coordinate.

import { useDataTransformer } from '@electricui/timeseries-react'
import { useMouseSignal, MouseData } from '@electricui/components-desktop-charts'
import { closestByValue } from '@electricui/dataflow'
 
const [mouseSignal, captureRef] = useMouseSignal()
 
const dataTransformer = useDataTransformer(() => {
const closest = closestByValue(dataSource, mouseSignal, {
// Index the events by their voltage
valueAccessor: (data, time, tags) => data.voltage,
// The mouse y position is the value to search for
searchValueAccessor: (data, time, tags) => data.y,
// map the result directly to the data, the default case
mapResult: (data, time, tags, value, searchData, searchTags, searchValue, distance) => data,
})
 
return closest
})

closestPointOnLineSegment

Screenshot of component operatorsSearch closestPointOnLineSegment

The closestPointOnLineSegment operator finds the closest point on an LineSegment accessed from the indexed Queryable, to a point defined by the search queryable.

The lineSegmentAccessor defines the LineSegment from the Queryable's Events. Returning a null skips ingesting this LineSegment.

The searchPositionAccessor defines the search point. Returning a null resets the search.

The predicate can be used to exclude candidates from the search result.

The maxDistance parameter can be used to limit the distance to search for.

The resulting event can be remapped with the mapResult and tagResult methods.

The example below finds the closest interpolated point on the line segments generated by a number over time signal.

import { useDataTransformer } from '@electricui/timeseries-react'
import { useMouseSignal, MouseData } from '@electricui/components-desktop-charts'
import { closestPointOnLineSegment, lineSegments } from '@electricui/dataflow'
 
const dataSource = useMessageDataSource<number>('signal')
 
const [mouseSignal, captureRef] = useMouseSignal()
 
const dataTransformer = useDataTransformer(() => {
const segments = lineSegments(dataSource, (data, time, tags) => ({x: time, y: data}))
 
const closest = closestPointOnLineSegment(segments, mouseSignal, {
// Index the lineSegments directly
lineSegmentAccessor: (data, time, tags) => data,
// The mouse position can be used directly as it contains x, y, chartAspectRatio keys
searchPositionAccessor: (data, time, tags) => data,
// search up to 250 units away
maxDistance: 250,
// Return only the closest point
mapResult: (
data,
time,
tags,
lineSegment,
searchData,
searchTags,
searchPoint,
closestPoint,
distance
) => closestPoint,
})
 
return closest
})

If the data is made of 2D points, lineSegments can also be used.

import { useDataTransformer } from '@electricui/timeseries-react'
import { useMouseSignal, MouseData } from '@electricui/components-desktop-charts'
import { closestPointOnLineSegment, mapWithPrevious, lineSegments } from '@electricui/dataflow'
 
const dataSource = useMessageDataSource<{x: number, y: number, voltage: number}>('samples')
const [mouseSignal, captureRef] = useMouseSignal()
 
const dataTransformer = useDataTransformer(() => {
const segments = lineSegments(dataSource, (data, time, tags) => ({
x: data.x,
y: data.y
}))
 
const closest = closestPointOnLineSegment(segments, mouseSignal, {
// Index the lineSegments directly
lineSegmentAccessor: (data, time, tags) => data,
// The mouse position can be used directly as it contains x, y, chartAspectRatio keys
searchPositionAccessor: (data, time, tags) => data,
// Return only the closest point
mapResult: (
data,
time,
tags,
lineSegment,
searchData,
searchTags,
searchPoint,
closestPoint,
distance
) => closestPoint,
})
 
return closest
})

closestLineSegmentIntersection

Screenshot of component operatorsSearch closestLineSegmentIntersection

The closestLineSegmentIntersection operator finds the intersection of a LineSegment accessed from the indexed Queryable, to a LineSegment provided by the search queryable, closest to a point provided by the search queryable.

The lineSegmentAccessor defines the LineSegment from the Queryable's Events. Returning a null skips ingesting this LineSegment.

The searchLineSegmentAccessor defines the search LineSegment. Returning a null resets the search.

The searchPositionAccessor defines the search point. Returning a null resets the search.

The predicate can be used to exclude candidates from the search result.

The maxDistance parameter can be used to limit the distance to search for.

The resulting event can be remapped with the mapResult and tagResult methods.

The example below finds the closest interpolated point on the line segments generated by a number over time signal, searching horizontally left from the mouse position.

import { useDataTransformer } from '@electricui/timeseries-react'
import { useMouseSignal, MouseData } from '@electricui/components-desktop-charts'
import { closestLineSegmentIntersection, lineSegments } from '@electricui/dataflow'
 
const dataSource = useMessageDataSource<number>('signal')
 
const [mouseSignal, captureRef] = useMouseSignal()
 
const dataTransformer = useDataTransformer(() => {
const segments = lineSegments(dataSource, (data, time, tags) => ({x: time, y: data}))
 
const closest = closestLineSegmentIntersection(segments, mouseSignal, {
// Index the lineSegments directly
lineSegmentAccessor: (data, time, tags) => data,
// Search from the chart xMin to the x value of the mouse, at the y value of the mouse
searchLineSegmentAccessor: (data, time, tags) => ({
x1: data.chartXMin,
x2: data.x,
y1: data.y,
y2: data.y,
}),
// Find the closest intersection to the mouse position, which contains x, y, chartAspectRatio keys
searchPositionAccessor: (data, time, tags) => data,
// Return only the intersection point
mapResult: (
data,
time,
tags,
lineSegment,
searchData,
searchTags,
searchLineSegment,
searchPoint,
intersectionPoint,
distance,
) => intersectionPoint,
})
 
return closest
})