Macaw.Data.Window (macaw_data v0.6.0)

Copy Markdown View Source

Windows, boxes and regions over a source's rows (spec/01 §2.3, §2.4).

  • A window is nil (whatever a source holds) or [from, to], either bound nil for open, over the window field: numbers for a :number field, instants (DateTime, NaiveDateTime or Date) for a :time one (applies?/2).
  • A box is %{west, south, east, north} in degrees over a :point window field (Macaw.Data.Source.Request.box?/1); west east of east crosses the antimeridian.
  • A region is ranges over named :time or :number fields, %{field => [from, to]}.

These are the comparisons, the trims and the numeric position that finishing (Macaw.Data.Source.Finish) and push share. Fields are read by either spelling of their name (Macaw.Data.Table.get/2). Ported from visualize's Visualize.Feed.Window, its box rewritten for a geographic box and its ranges kept as the region.

Examples

iex> Macaw.Data.Window.within?(5, [0, 10])
true
iex> Macaw.Data.Window.trim([%{t: 1}, %{t: 5}, %{t: 9}], :t, [2, nil])
[%{t: 5}, %{t: 9}]
iex> Macaw.Data.Window.in_box?({179.5, 0}, %{west: 170, south: -10, east: -170, north: 10})
true

Summary

Functions

Whether a window applies to a window field of the type: its bounds instants for :time, numbers for :number; a nil window, and a window with no bound, to either.

The rows whose field lies in [from, upper): from inclusive and open when nil, upper exclusive and open when nil; a row with no time lies in none. The seam of a combination, where one source's rows stop and the next's begin (macaw#185, ported from visualize's Visualize.Feed.Window.before/4).

Whether a schema can answer a request's window, box, region and latest: :ok, or {:error, faults}, every fault {path, reason} by path. A window must apply to a :time or :number window field; a box needs a :point window field; each region field is described, :time or :number, its range two ends of its type, not inverted; latest needs a :time or :number window field and a declared identity. A request asking none of them is :ok.

:lt, :eq or :gt between two times of one kind.

The earlier of two times, a nil ignored.

Whether a position {longitude, latitude} lies in a box, bounds inclusive; a box whose west is east of its east crosses the antimeridian. Anything but a position lies in none.

Whether a value is an instant: a DateTime, a NaiveDateTime or a Date.

The intersection of two windows, either nil for everything.

The later of two times, a nil ignored.

Each identity's last row: of the rows, for each identity identify gives, the row whose time (a number) is greatest, the later in order on a tie, kept in their order. A row whose identity or time is nil names nothing and is dropped.

The rows whose field lies strictly after time; every row for nil. A push's seam.

A time as a number, for a reduction's x: a number itself, a DateTime its Unix microseconds, a NaiveDateTime its Gregorian seconds, a Date its Gregorian days; anything else nil, a gap.

A field's range of a window or a region (macaw#185, visualize's range/2): a region's entry for the field, by either spelling of its name; a range [from, to] itself, which is the window field's; nil when there is none. Anything else is returned as it is, so a source that reads its window field's range this way still refuses what it can't.

The region a window asks of a schema (macaw#185): a range [from, to] is the window field's region, %{field => [from, to]}, where it applies to the window field's type, and nil (the source's own span) where it does not or the schema has no :time or :number window field; a region passes as it is; nil is nil.

Whether a value is one a window can be over: a number or an instant.

The rows whose field lies within the window, in their order.

The rows whose field lies in the box, in their order; every row for nil.

The rows whose every ranged field lies in its range; every row for nil.

Whether a time lies within a window, both bounds inclusive; nil lies in none but nil.

Types

t()

@type t() :: [time() | nil] | nil

time()

@type time() :: number() | DateTime.t() | NaiveDateTime.t() | Date.t()

Functions

applies?(arg1, type)

@spec applies?(t(), :time | :number) :: boolean()

Whether a window applies to a window field of the type: its bounds instants for :time, numbers for :number; a nil window, and a window with no bound, to either.

iex> Macaw.Data.Window.applies?([~U[2026-10-12 00:00:00Z], nil], :number)
false

before(rows, field, from, upper)

@spec before([map()], Macaw.Data.Table.name(), time() | nil, time() | nil) :: [map()]

The rows whose field lies in [from, upper): from inclusive and open when nil, upper exclusive and open when nil; a row with no time lies in none. The seam of a combination, where one source's rows stop and the next's begin (macaw#185, ported from visualize's Visualize.Feed.Window.before/4).

iex> Macaw.Data.Window.before([%{t: 1}, %{t: 5}, %{t: 9}], :t, nil, 5)
[%{t: 1}]
iex> Macaw.Data.Window.before([%{t: 1}, %{t: 5}, %{t: 9}], :t, 5, nil)
[%{t: 5}, %{t: 9}]

check(request_or_axes, schema)

@spec check(Macaw.Data.Source.Request.t() | map(), Macaw.Data.Schema.t()) ::
  :ok | {:error, [{list(), term()}, ...]}

Whether a schema can answer a request's window, box, region and latest: :ok, or {:error, faults}, every fault {path, reason} by path. A window must apply to a :time or :number window field; a box needs a :point window field; each region field is described, :time or :number, its range two ends of its type, not inverted; latest needs a :time or :number window field and a declared identity. A request asking none of them is :ok.

It takes a %Request{}, or the axes alone as a map of its fields (window, box, region, latest, each defaulting as a request's does), so a source stepping its window checks one without a whole request (macaw#185); any other key is {[key], :unknown_key}.

iex> alias Macaw.Data.Schema
iex> schema = Schema.new!(%{t: %{type: :number, window: true}, v: %{type: :number}})
iex> Macaw.Data.Window.check(%{region: %{t: [0, 5]}}, schema)
:ok
iex> Macaw.Data.Window.check(%{window: [9, 1], at: 3}, schema)
{:error, [{[:at], :unknown_key}]}

iex> alias Macaw.Data.{Schema, Source.Request}
iex> schema = Schema.new!(%{t: %{type: :number, window: true}, s: %{type: :category}})
iex> Macaw.Data.Window.check(Request.new!(now: ~U[2026-10-27 00:00:00Z], region: %{t: [9, 1], s: ["a", "b"]}, latest: true), schema)
{:error,
 [
   {[:latest], {:window, :no_identity}},
   {[:region, :s], {:invalid_for, :category}},
   {[:region, :t], {:window, :inverted}}
 ]}

compare(a, b)

@spec compare(time(), time()) :: :lt | :eq | :gt

:lt, :eq or :gt between two times of one kind.

earliest(a, b)

@spec earliest(time() | nil, time() | nil) :: time() | nil

The earlier of two times, a nil ignored.

in_box?(arg1, arg2)

@spec in_box?(term(), map()) :: boolean()

Whether a position {longitude, latitude} lies in a box, bounds inclusive; a box whose west is east of its east crosses the antimeridian. Anything but a position lies in none.

instant?(arg1)

@spec instant?(term()) :: boolean()

Whether a value is an instant: a DateTime, a NaiveDateTime or a Date.

intersect(window, window)

@spec intersect(t(), t()) :: t()

The intersection of two windows, either nil for everything.

latest(a, b)

@spec latest(time() | nil, time() | nil) :: time() | nil

The later of two times, a nil ignored.

latest_rows(rows, time, identify)

@spec latest_rows([map()], (map() -> number() | nil), (map() -> term())) :: [map()]

Each identity's last row: of the rows, for each identity identify gives, the row whose time (a number) is greatest, the later in order on a tie, kept in their order. A row whose identity or time is nil names nothing and is dropped.

iex> rows = [%{id: "a", t: 1}, %{id: "b", t: 2}, %{id: "a", t: 3}, %{id: nil, t: 4}]
iex> Macaw.Data.Window.latest_rows(rows, & &1.t, & &1.id)
[%{id: "b", t: 2}, %{id: "a", t: 3}]

newer(rows, field, time)

@spec newer([map()], Macaw.Data.Table.name(), time() | nil) :: [map()]

The rows whose field lies strictly after time; every row for nil. A push's seam.

position(value)

@spec position(term()) :: number() | nil

A time as a number, for a reduction's x: a number itself, a DateTime its Unix microseconds, a NaiveDateTime its Gregorian seconds, a Date its Gregorian days; anything else nil, a gap.

range(region, field)

@spec range(term(), Macaw.Data.Table.name()) :: term()

A field's range of a window or a region (macaw#185, visualize's range/2): a region's entry for the field, by either spelling of its name; a range [from, to] itself, which is the window field's; nil when there is none. Anything else is returned as it is, so a source that reads its window field's range this way still refuses what it can't.

iex> Macaw.Data.Window.range(%{"t" => [1, 5], x: [0, 9]}, :t)
[1, 5]
iex> Macaw.Data.Window.range([1, 5], :t)
[1, 5]
iex> Macaw.Data.Window.range(%{x: [0, 9]}, :t)
nil

region(window, schema)

@spec region(t() | %{required(Macaw.Data.Table.name()) => t()}, Macaw.Data.Schema.t()) ::
  %{required(Macaw.Data.Table.name()) => t()} | nil

The region a window asks of a schema (macaw#185): a range [from, to] is the window field's region, %{field => [from, to]}, where it applies to the window field's type, and nil (the source's own span) where it does not or the schema has no :time or :number window field; a region passes as it is; nil is nil.

visualize's box (D-168), %{ranges: ranges, latest: latest}, is a request's region and latest here: region(ranges, schema) and latest: beside it.

iex> alias Macaw.Data.Schema
iex> schema = Schema.new!(%{t: %{type: :number, window: true}, v: %{type: :number}})
iex> Macaw.Data.Window.region([0, 10], schema)
%{t: [0, 10]}
iex> Macaw.Data.Window.region([~U[2026-10-14 00:00:00Z], nil], schema)
nil
iex> Macaw.Data.Window.region(%{v: [0, 1]}, schema)
%{v: [0, 1]}

time?(value)

@spec time?(term()) :: boolean()

Whether a value is one a window can be over: a number or an instant.

trim(rows, field, window)

@spec trim([map()], Macaw.Data.Table.name(), t()) :: [map()]

The rows whose field lies within the window, in their order.

trim_box(rows, field, box)

@spec trim_box([map()], Macaw.Data.Table.name(), map() | nil) :: [map()]

The rows whose field lies in the box, in their order; every row for nil.

trim_region(rows, region)

@spec trim_region([map()], %{required(Macaw.Data.Table.name()) => t()} | nil) :: [
  map()
]

The rows whose every ranged field lies in its range; every row for nil.

within?(time, arg2)

@spec within?(time() | nil, t()) :: boolean()

Whether a time lies within a window, both bounds inclusive; nil lies in none but nil.