# A chart over every row

> osy lint's ui-chart-over-unbounded-rows (SHOULD) fires when a chart or a map is drawn from a page query that reads every row of an entity with no Take. Past one answer (about 2 MB of rows) the chart silently plots the first answer's rows, and long before that it ships every row to draw a handful of marks. Aggregate in the query — one row per mark — or narrow it to the range the chart shows.

<!-- id: ui-chart-aggregates · area: ui · stability: stable · html: https://osysharp.com/reference/ui/chart-aggregates/ -->

## Summary        {#summary}

`ui-chart-over-unbounded-rows` is a **SHOULD** finding: a `Chart` (or a map) is drawn from a read of every row of an
entity. A page query is sent about 2 MB of rows an answer ([How many rows a page is sent](https://osysharp.com/reference/ui/read-limits/)), so the chart of a growing table
quietly leaves rows out — and before then it ships every row to draw a few bars.

Let the database do the summing, so the answer is one row per mark:

```osy title="one row per day, summed in the query" test app=ui-chart-aggregates
entity Sale {
  [Required, MaxLength(10)] string Day;
  decimal Amount;
  security { allow read, create when IsAnonymous || IsAuthenticated; }
}

class DayTotal { public string Day; public decimal Total; }

[Route("/sales")] [Render(CSR)] [AllowAnonymous]
component SalesPage() {
  live var perDay = Sale.GroupBy(s => s.Day)
                        .Select(g => new DayTotal { Day = g.Key, Total = g.Sum(s => s.Amount) }).ToList();

  render {
    Stack(gap: 2, p: 6) {
      foreach (var d in perDay) { Text($"{d.Day}: {d.Total}"); }
    }
  }
}
```

## Signature      {#signature}

```osy syntax
Entity.GroupBy(x => key).Select(g => new Row { Key = g.Key, Value = g.Sum(x => …) }).ToList()   // one row per mark
Entity.Where(x => x.At >= from).OrderBy(x => x.At).Take(n).ToList()                              // or a narrowed range
```

## Description    {#description}

### When does it fire?   {#fires}

On a component field read from an entity with no `Take` that a chart or map is drawn from — a `Chart`, a `Pie`, a
`Sparkline`, or any control whose name holds `Chart` or `Map` — through its arguments or anything written inside it.
It takes precedence over the list and dropdown rules for the same read.

### When is it silent?   {#silent}

When the read is an aggregate — a `GroupBy` (one row per key), a `Count`, a `Sum` — or bounded by a `Take`, a one-row
read or a child collection pinned to one parent.

### What can it not tell?   {#limits}

How many distinct keys a `GroupBy` has. A group per customer can be large; a group per day of a month is not. A
grouped read is trusted as bounded.

## See also        {#see-also}

- [How many rows a page is sent](https://osysharp.com/reference/ui/read-limits/) — what a page is sent, and why an aggregate is never windowed
- [The chart kit — line, column, bar, scatter and candle, with no JavaScript](https://osysharp.com/reference/ui/chart-kit/) — the chart kit
