fix: backend ORDER BY CASE for series sort, default value_asc

This commit is contained in:
Sebastian Seedorf
2026-06-03 15:08:25 +02:00
parent 25db053a7b
commit d6c2bb0b6a
3 changed files with 11 additions and 6 deletions

View File

@@ -16,7 +16,7 @@ export const POST = withAuth(async (req: NextRequest) => {
filter_combinators = null, filter_items = null, filter_combinators = null, filter_items = null,
filter_items_exclude = null, filter_items_regex = false, filter_items_exclude = null, filter_items_regex = false,
y_min = null, y_max = null, y_scale = 'linear', y_min = null, y_max = null, y_scale = 'linear',
series_limit = 20, order_by = 'time', series_limit = 20, order_by = 'value_asc',
} = body; } = body;
if (!title) return NextResponse.json({ error: 'title required' }, { status: 400 }); if (!title) return NextResponse.json({ error: 'title required' }, { status: 400 });

View File

@@ -13,7 +13,7 @@ export const GET = withAuth(async (req: NextRequest) => {
const from = p.get('from'); const from = p.get('from');
const to = p.get('to'); const to = p.get('to');
const useRegex = p.get('regex') === 'true'; const useRegex = p.get('regex') === 'true';
const orderBy = p.get('order_by') ?? 'time'; const orderBy = p.get('order_by') ?? 'value_asc';
const limit = p.get('limit') ? parseInt(p.get('limit')!, 10) : null; const limit = p.get('limit') ? parseInt(p.get('limit')!, 10) : null;
const conditions: string[] = []; const conditions: string[] = [];
@@ -147,8 +147,11 @@ export const GET = withAuth(async (req: NextRequest) => {
`(combinator = $${i + idx * 2} AND item_key = $${i + idx * 2 + 1})`, `(combinator = $${i + idx * 2} AND item_key = $${i + idx * 2 + 1})`,
); );
const fullWhere = `${where ? where + ' AND' : 'WHERE'} (${seriesConditions.join(' OR ')})`; const fullWhere = `${where ? where + ' AND' : 'WHERE'} (${seriesConditions.join(' OR ')})`;
const orderCase = top.map((_, idx) =>
`WHEN combinator = $${i + idx * 2} AND item_key = $${i + idx * 2 + 1} THEN ${idx}`,
).join(' ');
const result = await pool.query( const result = await pool.query(
`SELECT ${selectCols} FROM signals ${fullWhere} ORDER BY real_time ASC`, `SELECT ${selectCols} FROM signals ${fullWhere} ORDER BY CASE ${orderCase} ELSE ${top.length} END, real_time ASC`,
[...values, ...top.flatMap(r => [r.combinator, r.item_key])], [...values, ...top.flatMap(r => [r.combinator, r.item_key])],
); );
return NextResponse.json(result.rows); return NextResponse.json(result.rows);
@@ -175,8 +178,11 @@ export const GET = withAuth(async (req: NextRequest) => {
`(combinator = $${i + idx * 2} AND item_key = $${i + idx * 2 + 1})`, `(combinator = $${i + idx * 2} AND item_key = $${i + idx * 2 + 1})`,
); );
const fullWhere = `${where ? where + ' AND' : 'WHERE'} (${seriesConditions.join(' OR ')})`; const fullWhere = `${where ? where + ' AND' : 'WHERE'} (${seriesConditions.join(' OR ')})`;
const orderCase = top.map((_, idx) =>
`WHEN combinator = $${i + idx * 2} AND item_key = $${i + idx * 2 + 1} THEN ${idx}`,
).join(' ');
const result = await pool.query( const result = await pool.query(
`SELECT ${selectCols} FROM signals ${fullWhere} ORDER BY real_time ASC`, `SELECT ${selectCols} FROM signals ${fullWhere} ORDER BY CASE ${orderCase} ELSE ${top.length} END, real_time ASC`,
[...values, ...top.flatMap(r => [r.combinator, r.item_key])], [...values, ...top.flatMap(r => [r.combinator, r.item_key])],
); );
return NextResponse.json(result.rows); return NextResponse.json(result.rows);

View File

@@ -37,7 +37,7 @@ export default function ChartEditor({ initial, onSave, onClose }: Props) {
const [whitelist, setWhitelist] = useState((initial?.filter_items ?? []).join(', ')); const [whitelist, setWhitelist] = useState((initial?.filter_items ?? []).join(', '));
const [blacklist, setBlacklist] = useState((initial?.filter_items_exclude ?? []).join(', ')); const [blacklist, setBlacklist] = useState((initial?.filter_items_exclude ?? []).join(', '));
const [useRegex, setUseRegex] = useState(initial?.filter_items_regex ?? false); const [useRegex, setUseRegex] = useState(initial?.filter_items_regex ?? false);
const [orderBy, setOrderBy] = useState<ChartConfig['order_by']>(initial?.order_by ?? 'time'); const [orderBy, setOrderBy] = useState<ChartConfig['order_by']>(initial?.order_by ?? 'value_asc');
const [seriesLimit, setSeriesLimit] = useState(initial?.series_limit ?? 20); const [seriesLimit, setSeriesLimit] = useState(initial?.series_limit ?? 20);
const [yMin, setYMin] = useState(initial?.y_min?.toString() ?? ''); const [yMin, setYMin] = useState(initial?.y_min?.toString() ?? '');
const [yMax, setYMax] = useState(initial?.y_max?.toString() ?? ''); const [yMax, setYMax] = useState(initial?.y_max?.toString() ?? '');
@@ -123,7 +123,6 @@ export default function ChartEditor({ initial, onSave, onClose }: Props) {
<label className="block text-sm text-gray-400 mb-1">Sort series by</label> <label className="block text-sm text-gray-400 mb-1">Sort series by</label>
<select value={orderBy} onChange={e => setOrderBy(e.target.value as ChartConfig['order_by'])} className={`${inputCls} mb-3`}> <select value={orderBy} onChange={e => setOrderBy(e.target.value as ChartConfig['order_by'])} className={`${inputCls} mb-3`}>
<option value="time">Time (no sort)</option>
<option value="value_asc">Latest lowest values</option> <option value="value_asc">Latest lowest values</option>
<option value="value_desc">Latest highest values</option> <option value="value_desc">Latest highest values</option>
<option value="delta_asc">Biggest decrease (last 10 min)</option> <option value="delta_asc">Biggest decrease (last 10 min)</option>