Result views
A result view adds a tab next to Table and JSON in the Query Editor. Use it for a chart, a pivot or a summary. There are two kinds:
- A component, declared with
"view": true. Flare mounts it with the results and you draw them. - A function, declared without
view. It returns new rows, and Flare shows them in its own table, where they sort, copy and export like any other results.
Neither kind changes the original results.
Declare them
{
"permissions": { "data": "read" },
"contributes": {
"transforms": [
{ "id": "chart", "title": "Chart", "icon": "chart-column", "view": true },
{ "id": "summary", "title": "Summary", "icon": "sigma" }
]
}
}The manifest and definePlugin call result views transforms. They need permissions.data set to read or write.
Reshape rows with a function
The function gets the results and returns new ones in the same shape. Results have columns, rows as arrays of values, truncated (whether Flare hit its row limit) and the query's language:
import { definePlugin, type ResultTransform } from '@flare/plugin';
/** Count, sum and average of every column whose values are all numbers. */
const summary: ResultTransform = (result) => {
const numeric = result.columns
.map((name, index) => ({ name, index }))
.filter(({ index }) => result.rows.every((row) => typeof row[index] === 'number'));
return {
columns: ['column', 'count', 'sum', 'average'],
rows: numeric.map(({ name, index }) => {
const values = result.rows.map((row) => row[index] as number);
const sum = values.reduce((total, value) => total + value, 0);
return [name, values.length, sum, values.length ? sum / values.length : null];
}),
truncated: result.truncated,
};
};
export default definePlugin({
transforms: { summary },
});You can also write the function inline in transforms. It's typed as a ResultTransform either way.
Draw them with a component
The component gets the results as TransformProps, through useViewProps() or defineProps. They update each time the query runs:
<script lang="ts" setup>
import {
BarChart,
EmptyState,
useViewProps,
type TransformProps,
} from '@flare/plugin/vue';
import { computed } from 'vue';
const props = useViewProps<TransformProps>();
/** The first column whose values are all numbers, or -1. */
const column = computed(() =>
props.result.columns.findIndex((_, index) =>
props.result.rows.every(
(row) => row[index] === null || typeof row[index] === 'number'
)
)
);
const points = computed(() =>
props.result.rows.slice(0, 60).map((row, index) => ({
label: String(row.find((value) => typeof value === 'string') ?? index + 1),
value: typeof row[column.value] === 'number' ? (row[column.value] as number) : null,
}))
);
</script>
<template>
<div class="p-4">
<EmptyState
v-if="column < 0"
title="No numbers to chart"
description="These results have no column of numbers."
/>
<div v-else class="h-64">
<BarChart :data="points" :title="props.result.columns[column]" />
</div>
</div>
</template>import { definePlugin } from '@flare/plugin';
import Chart from './views/Chart.vue';
export default definePlugin({
transforms: { chart: Chart },
});The tab is as tall as your component, up to a limit. Charts fill their container, so give it a height such as h-64.
Any query language
Results have the same shape on every database, whether the query was SQL, CQL, MongoDB shell, Redis commands or a Firestore chain. result.language tells you which. If your view only makes sense for one, set engines in the manifest.