mirror of
https://github.com/prometheus/prometheus.git
synced 2024-11-12 16:44:05 -08:00
Merge pull request #14913 from prometheus/explain-view-set-ops
Some checks are pending
buf.build / lint and publish (push) Waiting to run
CI / Go tests (push) Waiting to run
CI / More Go tests (push) Waiting to run
CI / Go tests with previous Go version (push) Waiting to run
CI / UI tests (push) Waiting to run
CI / Go tests on Windows (push) Waiting to run
CI / Mixins tests (push) Waiting to run
CI / Build Prometheus for common architectures (0) (push) Waiting to run
CI / Build Prometheus for common architectures (1) (push) Waiting to run
CI / Build Prometheus for common architectures (2) (push) Waiting to run
CI / Build Prometheus for all architectures (0) (push) Waiting to run
CI / Build Prometheus for all architectures (1) (push) Waiting to run
CI / Build Prometheus for all architectures (10) (push) Waiting to run
CI / Build Prometheus for all architectures (11) (push) Waiting to run
CI / Build Prometheus for all architectures (2) (push) Waiting to run
CI / Build Prometheus for all architectures (3) (push) Waiting to run
CI / Build Prometheus for all architectures (4) (push) Waiting to run
CI / Build Prometheus for all architectures (5) (push) Waiting to run
CI / Build Prometheus for all architectures (6) (push) Waiting to run
CI / Build Prometheus for all architectures (7) (push) Waiting to run
CI / Build Prometheus for all architectures (8) (push) Waiting to run
CI / Build Prometheus for all architectures (9) (push) Waiting to run
CI / Report status of build Prometheus for all architectures (push) Blocked by required conditions
CI / Check generated parser (push) Waiting to run
CI / golangci-lint (push) Waiting to run
CI / fuzzing (push) Waiting to run
CI / codeql (push) Waiting to run
CI / Publish main branch artifacts (push) Blocked by required conditions
CI / Publish release artefacts (push) Blocked by required conditions
CI / Publish UI on npm Registry (push) Blocked by required conditions
Scorecards supply-chain security / Scorecards analysis (push) Waiting to run
Some checks are pending
buf.build / lint and publish (push) Waiting to run
CI / Go tests (push) Waiting to run
CI / More Go tests (push) Waiting to run
CI / Go tests with previous Go version (push) Waiting to run
CI / UI tests (push) Waiting to run
CI / Go tests on Windows (push) Waiting to run
CI / Mixins tests (push) Waiting to run
CI / Build Prometheus for common architectures (0) (push) Waiting to run
CI / Build Prometheus for common architectures (1) (push) Waiting to run
CI / Build Prometheus for common architectures (2) (push) Waiting to run
CI / Build Prometheus for all architectures (0) (push) Waiting to run
CI / Build Prometheus for all architectures (1) (push) Waiting to run
CI / Build Prometheus for all architectures (10) (push) Waiting to run
CI / Build Prometheus for all architectures (11) (push) Waiting to run
CI / Build Prometheus for all architectures (2) (push) Waiting to run
CI / Build Prometheus for all architectures (3) (push) Waiting to run
CI / Build Prometheus for all architectures (4) (push) Waiting to run
CI / Build Prometheus for all architectures (5) (push) Waiting to run
CI / Build Prometheus for all architectures (6) (push) Waiting to run
CI / Build Prometheus for all architectures (7) (push) Waiting to run
CI / Build Prometheus for all architectures (8) (push) Waiting to run
CI / Build Prometheus for all architectures (9) (push) Waiting to run
CI / Report status of build Prometheus for all architectures (push) Blocked by required conditions
CI / Check generated parser (push) Waiting to run
CI / golangci-lint (push) Waiting to run
CI / fuzzing (push) Waiting to run
CI / codeql (push) Waiting to run
CI / Publish main branch artifacts (push) Blocked by required conditions
CI / Publish release artefacts (push) Blocked by required conditions
CI / Publish UI on npm Registry (push) Blocked by required conditions
Scorecards supply-chain security / Scorecards analysis (push) Waiting to run
PromQL explain view: Support set operators
This commit is contained in:
commit
6acd3dee53
|
@ -361,309 +361,296 @@ const VectorVectorBinaryExprExplainView: FC<
|
||||||
<>
|
<>
|
||||||
<Text size="sm">{explanationText(node)}</Text>
|
<Text size="sm">{explanationText(node)}</Text>
|
||||||
|
|
||||||
{!isSetOperator(node.op) && (
|
<Group my="lg" justify="flex-end" gap="xl">
|
||||||
<>
|
<Switch
|
||||||
<Group my="lg" justify="flex-end" gap="xl">
|
label="Show sample values"
|
||||||
{/* <Switch
|
checked={showSampleValues}
|
||||||
label="Break long lines"
|
onChange={(event) => setShowSampleValues(event.currentTarget.checked)}
|
||||||
checked={allowLineBreaks}
|
/>
|
||||||
onChange={(event) =>
|
</Group>
|
||||||
setAllowLineBreaks(event.currentTarget.checked)
|
|
||||||
}
|
|
||||||
/> */}
|
|
||||||
<Switch
|
|
||||||
label="Show sample values"
|
|
||||||
checked={showSampleValues}
|
|
||||||
onChange={(event) =>
|
|
||||||
setShowSampleValues(event.currentTarget.checked)
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
</Group>
|
|
||||||
|
|
||||||
{numGroups > Object.keys(matchGroups).length && (
|
{numGroups > Object.keys(matchGroups).length && (
|
||||||
<Alert color="yellow" mb="md" icon={<IconAlertTriangle />}>
|
<Alert color="yellow" mb="md" icon={<IconAlertTriangle />}>
|
||||||
Too many match groups to display, only showing{" "}
|
Too many match groups to display, only showing{" "}
|
||||||
{Object.keys(matchGroups).length} out of {numGroups} groups.
|
{Object.keys(matchGroups).length} out of {numGroups} groups.
|
||||||
<br />
|
<br />
|
||||||
<br />
|
<br />
|
||||||
<Anchor fz="sm" onClick={() => setMaxGroups(undefined)}>
|
<Anchor fz="sm" onClick={() => setMaxGroups(undefined)}>
|
||||||
Show all groups
|
Show all groups
|
||||||
</Anchor>
|
</Anchor>
|
||||||
</Alert>
|
</Alert>
|
||||||
)}
|
|
||||||
|
|
||||||
{errCount > 0 && (
|
|
||||||
<Alert color="yellow" mb="md" icon={<IconAlertTriangle />}>
|
|
||||||
Found matching issues in {errCount} match group
|
|
||||||
{errCount > 1 ? "s" : ""}. See below for per-group error details.
|
|
||||||
</Alert>
|
|
||||||
)}
|
|
||||||
|
|
||||||
<Table fz="xs" withRowBorders={false}>
|
|
||||||
<Table.Tbody>
|
|
||||||
{Object.values(matchGroups).map((mg, mgIdx) => {
|
|
||||||
const {
|
|
||||||
groupLabels,
|
|
||||||
lhs,
|
|
||||||
lhsCount,
|
|
||||||
rhs,
|
|
||||||
rhsCount,
|
|
||||||
result,
|
|
||||||
error,
|
|
||||||
} = mg;
|
|
||||||
|
|
||||||
const matchGroupTitleRow = (color: string) => (
|
|
||||||
<Table.Tr ta="center">
|
|
||||||
<Table.Td
|
|
||||||
colSpan={2}
|
|
||||||
style={{ backgroundColor: `${color}25` }}
|
|
||||||
>
|
|
||||||
<SeriesName labels={groupLabels} format={true} />
|
|
||||||
</Table.Td>
|
|
||||||
</Table.Tr>
|
|
||||||
);
|
|
||||||
|
|
||||||
const matchGroupTable = (
|
|
||||||
series: InstantSample[],
|
|
||||||
seriesCount: number,
|
|
||||||
color: string,
|
|
||||||
colorOffset?: number
|
|
||||||
) => (
|
|
||||||
<Box
|
|
||||||
style={{
|
|
||||||
borderRadius: 3,
|
|
||||||
border: "2px solid",
|
|
||||||
borderColor:
|
|
||||||
series.length === 0
|
|
||||||
? "light-dark(var(--mantine-color-gray-4), var(--mantine-color-gray-7))"
|
|
||||||
: color,
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Table fz="xs" withRowBorders={false} verticalSpacing={5}>
|
|
||||||
<Table.Tbody>
|
|
||||||
{series.length === 0 ? (
|
|
||||||
<Table.Tr>
|
|
||||||
<Table.Td
|
|
||||||
ta="center"
|
|
||||||
c="light-dark(var(--mantine-color-gray-7), var(--mantine-color-gray-5))"
|
|
||||||
py="md"
|
|
||||||
fw="bold"
|
|
||||||
>
|
|
||||||
no matching series
|
|
||||||
</Table.Td>
|
|
||||||
</Table.Tr>
|
|
||||||
) : (
|
|
||||||
<>
|
|
||||||
{matchGroupTitleRow(color)}
|
|
||||||
{series.map((s, sIdx) => {
|
|
||||||
if (s.value === undefined) {
|
|
||||||
// TODO: Figure out how to handle native histograms.
|
|
||||||
throw new Error(
|
|
||||||
"Native histograms are not supported yet"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Table.Tr key={sIdx}>
|
|
||||||
<Table.Td>
|
|
||||||
<Group wrap="nowrap" gap={7} align="center">
|
|
||||||
{seriesSwatch(
|
|
||||||
colorForIndex(sIdx, colorOffset)
|
|
||||||
)}
|
|
||||||
|
|
||||||
<SeriesName
|
|
||||||
labels={noMatchLabels(
|
|
||||||
s.metric,
|
|
||||||
matching.on,
|
|
||||||
matching.labels
|
|
||||||
)}
|
|
||||||
format={true}
|
|
||||||
/>
|
|
||||||
</Group>
|
|
||||||
</Table.Td>
|
|
||||||
{showSampleValues && (
|
|
||||||
<Table.Td ta="right">{s.value[1]}</Table.Td>
|
|
||||||
)}
|
|
||||||
</Table.Tr>
|
|
||||||
);
|
|
||||||
})}
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
{seriesCount > series.length && (
|
|
||||||
<Table.Tr>
|
|
||||||
<Table.Td ta="center" py="md" fw="bold" c="gray.6">
|
|
||||||
{seriesCount - series.length} more series omitted
|
|
||||||
–
|
|
||||||
<Anchor
|
|
||||||
size="xs"
|
|
||||||
onClick={() => setMaxSeriesPerGroup(undefined)}
|
|
||||||
>
|
|
||||||
Show all series
|
|
||||||
</Anchor>
|
|
||||||
</Table.Td>
|
|
||||||
</Table.Tr>
|
|
||||||
)}
|
|
||||||
</Table.Tbody>
|
|
||||||
</Table>
|
|
||||||
</Box>
|
|
||||||
);
|
|
||||||
|
|
||||||
const noLHSMatches = lhs.length === 0;
|
|
||||||
const noRHSMatches = rhs.length === 0;
|
|
||||||
|
|
||||||
const groupColor = colorPool[mgIdx % colorPool.length];
|
|
||||||
|
|
||||||
const lhsTable = matchGroupTable(lhs, lhsCount, groupColor);
|
|
||||||
const rhsTable = matchGroupTable(
|
|
||||||
rhs,
|
|
||||||
rhsCount,
|
|
||||||
groupColor,
|
|
||||||
rhsColorOffset
|
|
||||||
);
|
|
||||||
|
|
||||||
const resultTable = (
|
|
||||||
<Box
|
|
||||||
style={{
|
|
||||||
borderRadius: 3,
|
|
||||||
border: `2px solid`,
|
|
||||||
borderColor:
|
|
||||||
noLHSMatches || noRHSMatches || error !== null
|
|
||||||
? "light-dark(var(--mantine-color-gray-4), var(--mantine-color-gray-7))"
|
|
||||||
: groupColor,
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Table fz="xs" withRowBorders={false} verticalSpacing={5}>
|
|
||||||
<Table.Tbody>
|
|
||||||
{noLHSMatches || noRHSMatches ? (
|
|
||||||
<Table.Tr>
|
|
||||||
<Table.Td
|
|
||||||
ta="center"
|
|
||||||
c="light-dark(var(--mantine-color-gray-7), var(--mantine-color-gray-5))"
|
|
||||||
py="md"
|
|
||||||
fw="bold"
|
|
||||||
>
|
|
||||||
dropped
|
|
||||||
</Table.Td>
|
|
||||||
</Table.Tr>
|
|
||||||
) : error !== null ? (
|
|
||||||
<Table.Tr>
|
|
||||||
<Table.Td
|
|
||||||
ta="center"
|
|
||||||
c="light-dark(var(--mantine-color-gray-7), var(--mantine-color-gray-5))"
|
|
||||||
py="md"
|
|
||||||
fw="bold"
|
|
||||||
>
|
|
||||||
error, result omitted
|
|
||||||
</Table.Td>
|
|
||||||
</Table.Tr>
|
|
||||||
) : (
|
|
||||||
<>
|
|
||||||
{result.map(({ sample, manySideIdx }, resIdx) => {
|
|
||||||
if (sample.value === undefined) {
|
|
||||||
// TODO: Figure out how to handle native histograms.
|
|
||||||
throw new Error(
|
|
||||||
"Native histograms are not supported yet"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
const filtered =
|
|
||||||
sample.value[1] === filteredSampleValue;
|
|
||||||
const [lIdx, rIdx] =
|
|
||||||
matching.card ===
|
|
||||||
vectorMatchCardinality.oneToMany
|
|
||||||
? [0, manySideIdx]
|
|
||||||
: [manySideIdx, 0];
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Table.Tr key={resIdx}>
|
|
||||||
<Table.Td
|
|
||||||
style={{ opacity: filtered ? 0.5 : 1 }}
|
|
||||||
title={
|
|
||||||
filtered
|
|
||||||
? "Series has been filtered by comparison operator"
|
|
||||||
: undefined
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<Group
|
|
||||||
wrap="nowrap"
|
|
||||||
gap="xs"
|
|
||||||
align="flex-start"
|
|
||||||
>
|
|
||||||
<Group wrap="nowrap" gap={0}>
|
|
||||||
{seriesSwatch(colorForIndex(lIdx))}
|
|
||||||
<span style={{ color: "#aaa" }}>–</span>
|
|
||||||
{seriesSwatch(
|
|
||||||
colorForIndex(rIdx, rhsColorOffset)
|
|
||||||
)}
|
|
||||||
</Group>
|
|
||||||
|
|
||||||
<SeriesName
|
|
||||||
labels={sample.metric}
|
|
||||||
format={true}
|
|
||||||
/>
|
|
||||||
</Group>
|
|
||||||
</Table.Td>
|
|
||||||
{showSampleValues && (
|
|
||||||
<Table.Td ta="right">
|
|
||||||
{filtered ? (
|
|
||||||
<span style={{ color: "grey" }}>
|
|
||||||
filtered
|
|
||||||
</span>
|
|
||||||
) : (
|
|
||||||
<span>{sample.value[1]}</span>
|
|
||||||
)}
|
|
||||||
</Table.Td>
|
|
||||||
)}
|
|
||||||
</Table.Tr>
|
|
||||||
);
|
|
||||||
})}
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
</Table.Tbody>
|
|
||||||
</Table>
|
|
||||||
</Box>
|
|
||||||
);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<React.Fragment key={mgIdx}>
|
|
||||||
{mgIdx !== 0 && <tr style={{ height: 30 }}></tr>}
|
|
||||||
<Table.Tr>
|
|
||||||
<Table.Td colSpan={5}>
|
|
||||||
{error && (
|
|
||||||
<Alert
|
|
||||||
color="red"
|
|
||||||
mb="md"
|
|
||||||
title="Error in match group below"
|
|
||||||
icon={<IconAlertTriangle />}
|
|
||||||
>
|
|
||||||
{explainError(node, mg, error)}
|
|
||||||
</Alert>
|
|
||||||
)}
|
|
||||||
</Table.Td>
|
|
||||||
</Table.Tr>
|
|
||||||
<Table.Tr>
|
|
||||||
<Table.Td valign="middle" p={0}>
|
|
||||||
{lhsTable}
|
|
||||||
</Table.Td>
|
|
||||||
<Table.Td ta="center">
|
|
||||||
{node.op}
|
|
||||||
{node.bool && " bool"}
|
|
||||||
</Table.Td>
|
|
||||||
<Table.Td valign="middle" p={0}>
|
|
||||||
{rhsTable}
|
|
||||||
</Table.Td>
|
|
||||||
<Table.Td ta="center">=</Table.Td>
|
|
||||||
<Table.Td valign="middle" p={0}>
|
|
||||||
{resultTable}
|
|
||||||
</Table.Td>
|
|
||||||
</Table.Tr>
|
|
||||||
</React.Fragment>
|
|
||||||
);
|
|
||||||
})}
|
|
||||||
</Table.Tbody>
|
|
||||||
</Table>
|
|
||||||
</>
|
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
{errCount > 0 && (
|
||||||
|
<Alert color="yellow" mb="md" icon={<IconAlertTriangle />}>
|
||||||
|
Found matching issues in {errCount} match group
|
||||||
|
{errCount > 1 ? "s" : ""}. See below for per-group error details.
|
||||||
|
</Alert>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<Table fz="xs" withRowBorders={false}>
|
||||||
|
<Table.Tbody>
|
||||||
|
{Object.values(matchGroups).map((mg, mgIdx) => {
|
||||||
|
const { groupLabels, lhs, lhsCount, rhs, rhsCount, result, error } =
|
||||||
|
mg;
|
||||||
|
|
||||||
|
const matchGroupTitleRow = (color: string) => (
|
||||||
|
<Table.Tr ta="center">
|
||||||
|
<Table.Td colSpan={2} style={{ backgroundColor: `${color}25` }}>
|
||||||
|
<SeriesName labels={groupLabels} format={true} />
|
||||||
|
</Table.Td>
|
||||||
|
</Table.Tr>
|
||||||
|
);
|
||||||
|
|
||||||
|
const matchGroupTable = (
|
||||||
|
series: InstantSample[],
|
||||||
|
seriesCount: number,
|
||||||
|
color: string,
|
||||||
|
colorOffset?: number
|
||||||
|
) => (
|
||||||
|
<Box
|
||||||
|
style={{
|
||||||
|
borderRadius: 3,
|
||||||
|
border: "2px solid",
|
||||||
|
borderColor:
|
||||||
|
seriesCount === 0
|
||||||
|
? "light-dark(var(--mantine-color-gray-4), var(--mantine-color-gray-7))"
|
||||||
|
: color,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Table fz="xs" withRowBorders={false} verticalSpacing={5}>
|
||||||
|
<Table.Tbody>
|
||||||
|
{seriesCount === 0 ? (
|
||||||
|
<Table.Tr>
|
||||||
|
<Table.Td
|
||||||
|
ta="center"
|
||||||
|
c="light-dark(var(--mantine-color-gray-7), var(--mantine-color-gray-5))"
|
||||||
|
py="md"
|
||||||
|
fw="bold"
|
||||||
|
>
|
||||||
|
no matching series
|
||||||
|
</Table.Td>
|
||||||
|
</Table.Tr>
|
||||||
|
) : (
|
||||||
|
<>
|
||||||
|
{matchGroupTitleRow(color)}
|
||||||
|
{series.map((s, sIdx) => {
|
||||||
|
if (s.value === undefined) {
|
||||||
|
// TODO: Figure out how to handle native histograms.
|
||||||
|
throw new Error(
|
||||||
|
"Native histograms are not supported yet"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Table.Tr key={sIdx}>
|
||||||
|
<Table.Td>
|
||||||
|
<Group wrap="nowrap" gap={7} align="center">
|
||||||
|
{seriesSwatch(
|
||||||
|
colorForIndex(sIdx, colorOffset)
|
||||||
|
)}
|
||||||
|
|
||||||
|
<SeriesName
|
||||||
|
labels={noMatchLabels(
|
||||||
|
s.metric,
|
||||||
|
matching.on,
|
||||||
|
matching.labels
|
||||||
|
)}
|
||||||
|
format={true}
|
||||||
|
/>
|
||||||
|
</Group>
|
||||||
|
</Table.Td>
|
||||||
|
{showSampleValues && (
|
||||||
|
<Table.Td ta="right">{s.value[1]}</Table.Td>
|
||||||
|
)}
|
||||||
|
</Table.Tr>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
{seriesCount > series.length && (
|
||||||
|
<Table.Tr>
|
||||||
|
<Table.Td ta="center" py="md" fw="bold" c="gray.6">
|
||||||
|
{seriesCount - series.length} more series omitted
|
||||||
|
–
|
||||||
|
<Anchor
|
||||||
|
size="xs"
|
||||||
|
onClick={() => setMaxSeriesPerGroup(undefined)}
|
||||||
|
>
|
||||||
|
Show all series
|
||||||
|
</Anchor>
|
||||||
|
</Table.Td>
|
||||||
|
</Table.Tr>
|
||||||
|
)}
|
||||||
|
</Table.Tbody>
|
||||||
|
</Table>
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
|
||||||
|
const groupColor = colorPool[mgIdx % colorPool.length];
|
||||||
|
|
||||||
|
const lhsTable = matchGroupTable(lhs, lhsCount, groupColor);
|
||||||
|
const rhsTable = matchGroupTable(
|
||||||
|
rhs,
|
||||||
|
rhsCount,
|
||||||
|
groupColor,
|
||||||
|
rhsColorOffset
|
||||||
|
);
|
||||||
|
|
||||||
|
const resultTable = (
|
||||||
|
<Box
|
||||||
|
style={{
|
||||||
|
borderRadius: 3,
|
||||||
|
border: `2px solid`,
|
||||||
|
borderColor:
|
||||||
|
result.length === 0 || error !== null
|
||||||
|
? "light-dark(var(--mantine-color-gray-4), var(--mantine-color-gray-7))"
|
||||||
|
: groupColor,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Table fz="xs" withRowBorders={false} verticalSpacing={5}>
|
||||||
|
<Table.Tbody>
|
||||||
|
{error !== null ? (
|
||||||
|
<Table.Tr>
|
||||||
|
<Table.Td
|
||||||
|
ta="center"
|
||||||
|
c="light-dark(var(--mantine-color-gray-7), var(--mantine-color-gray-5))"
|
||||||
|
py="md"
|
||||||
|
fw="bold"
|
||||||
|
>
|
||||||
|
error, result omitted
|
||||||
|
</Table.Td>
|
||||||
|
</Table.Tr>
|
||||||
|
) : result.length === 0 ? (
|
||||||
|
<Table.Tr>
|
||||||
|
<Table.Td
|
||||||
|
ta="center"
|
||||||
|
c="light-dark(var(--mantine-color-gray-7), var(--mantine-color-gray-5))"
|
||||||
|
py="md"
|
||||||
|
fw="bold"
|
||||||
|
>
|
||||||
|
dropped
|
||||||
|
</Table.Td>
|
||||||
|
</Table.Tr>
|
||||||
|
) : error !== null ? (
|
||||||
|
<Table.Tr>
|
||||||
|
<Table.Td
|
||||||
|
ta="center"
|
||||||
|
c="light-dark(var(--mantine-color-gray-7), var(--mantine-color-gray-5))"
|
||||||
|
py="md"
|
||||||
|
fw="bold"
|
||||||
|
>
|
||||||
|
error, result omitted
|
||||||
|
</Table.Td>
|
||||||
|
</Table.Tr>
|
||||||
|
) : (
|
||||||
|
<>
|
||||||
|
{result.map(({ sample, manySideIdx }, resIdx) => {
|
||||||
|
if (sample.value === undefined) {
|
||||||
|
// TODO: Figure out how to handle native histograms.
|
||||||
|
throw new Error(
|
||||||
|
"Native histograms are not supported yet"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const filtered =
|
||||||
|
sample.value[1] === filteredSampleValue;
|
||||||
|
const [lIdx, rIdx] =
|
||||||
|
matching.card === vectorMatchCardinality.oneToMany
|
||||||
|
? [0, manySideIdx]
|
||||||
|
: [manySideIdx, 0];
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Table.Tr key={resIdx}>
|
||||||
|
<Table.Td
|
||||||
|
style={{ opacity: filtered ? 0.5 : 1 }}
|
||||||
|
title={
|
||||||
|
filtered
|
||||||
|
? "Series has been filtered by comparison operator"
|
||||||
|
: undefined
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<Group
|
||||||
|
wrap="nowrap"
|
||||||
|
gap="xs"
|
||||||
|
align="flex-start"
|
||||||
|
>
|
||||||
|
<Group wrap="nowrap" gap={0}>
|
||||||
|
{seriesSwatch(colorForIndex(lIdx))}
|
||||||
|
<span style={{ color: "#aaa" }}>–</span>
|
||||||
|
{seriesSwatch(
|
||||||
|
colorForIndex(rIdx, rhsColorOffset)
|
||||||
|
)}
|
||||||
|
</Group>
|
||||||
|
|
||||||
|
<SeriesName
|
||||||
|
labels={sample.metric}
|
||||||
|
format={true}
|
||||||
|
/>
|
||||||
|
</Group>
|
||||||
|
</Table.Td>
|
||||||
|
{showSampleValues && (
|
||||||
|
<Table.Td ta="right">
|
||||||
|
{filtered ? (
|
||||||
|
<span style={{ color: "grey" }}>
|
||||||
|
filtered
|
||||||
|
</span>
|
||||||
|
) : (
|
||||||
|
<span>{sample.value[1]}</span>
|
||||||
|
)}
|
||||||
|
</Table.Td>
|
||||||
|
)}
|
||||||
|
</Table.Tr>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</Table.Tbody>
|
||||||
|
</Table>
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<React.Fragment key={mgIdx}>
|
||||||
|
{mgIdx !== 0 && <tr style={{ height: 30 }}></tr>}
|
||||||
|
<Table.Tr>
|
||||||
|
<Table.Td colSpan={5}>
|
||||||
|
{error && (
|
||||||
|
<Alert
|
||||||
|
color="red"
|
||||||
|
mb="md"
|
||||||
|
title="Error in match group below"
|
||||||
|
icon={<IconAlertTriangle />}
|
||||||
|
>
|
||||||
|
{explainError(node, mg, error)}
|
||||||
|
</Alert>
|
||||||
|
)}
|
||||||
|
</Table.Td>
|
||||||
|
</Table.Tr>
|
||||||
|
<Table.Tr>
|
||||||
|
<Table.Td valign="middle" p={0}>
|
||||||
|
{lhsTable}
|
||||||
|
</Table.Td>
|
||||||
|
<Table.Td
|
||||||
|
ta="center"
|
||||||
|
fw={isSetOperator(node.op) ? "bold" : undefined}
|
||||||
|
>
|
||||||
|
{node.op}
|
||||||
|
{node.bool && " bool"}
|
||||||
|
</Table.Td>
|
||||||
|
<Table.Td valign="middle" p={0}>
|
||||||
|
{rhsTable}
|
||||||
|
</Table.Td>
|
||||||
|
<Table.Td ta="center">=</Table.Td>
|
||||||
|
<Table.Td valign="middle" p={0}>
|
||||||
|
{resultTable}
|
||||||
|
</Table.Td>
|
||||||
|
</Table.Tr>
|
||||||
|
</React.Fragment>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</Table.Tbody>
|
||||||
|
</Table>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
@ -73,6 +73,7 @@ const testMetricC: InstantSample[] = [
|
||||||
|
|
||||||
const testCases: TestCase[] = [
|
const testCases: TestCase[] = [
|
||||||
{
|
{
|
||||||
|
// metric_a - metric_b
|
||||||
desc: "one-to-one matching on all labels",
|
desc: "one-to-one matching on all labels",
|
||||||
op: binaryOperatorType.sub,
|
op: binaryOperatorType.sub,
|
||||||
matching: {
|
matching: {
|
||||||
|
@ -238,6 +239,7 @@ const testCases: TestCase[] = [
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
// metric_a - on(label1, label2) metric_b
|
||||||
desc: "one-to-one matching on explicit labels",
|
desc: "one-to-one matching on explicit labels",
|
||||||
op: binaryOperatorType.sub,
|
op: binaryOperatorType.sub,
|
||||||
matching: {
|
matching: {
|
||||||
|
@ -403,6 +405,7 @@ const testCases: TestCase[] = [
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
// metric_a - ignoring(same) metric_b
|
||||||
desc: "one-to-one matching ignoring explicit labels",
|
desc: "one-to-one matching ignoring explicit labels",
|
||||||
op: binaryOperatorType.sub,
|
op: binaryOperatorType.sub,
|
||||||
matching: {
|
matching: {
|
||||||
|
@ -568,6 +571,7 @@ const testCases: TestCase[] = [
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
// metric_b - metric_c
|
||||||
desc: "many-to-one matching with no matching labels specified (empty output)",
|
desc: "many-to-one matching with no matching labels specified (empty output)",
|
||||||
op: binaryOperatorType.sub,
|
op: binaryOperatorType.sub,
|
||||||
matching: {
|
matching: {
|
||||||
|
@ -689,6 +693,7 @@ const testCases: TestCase[] = [
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
// metric_b - on(label1) metric_c
|
||||||
desc: "many-to-one matching with matching labels specified, but no group_left (error)",
|
desc: "many-to-one matching with matching labels specified, but no group_left (error)",
|
||||||
op: binaryOperatorType.sub,
|
op: binaryOperatorType.sub,
|
||||||
matching: {
|
matching: {
|
||||||
|
@ -778,6 +783,7 @@ const testCases: TestCase[] = [
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
// metric_b - on(label1) group_left metric_c
|
||||||
desc: "many-to-one matching with matching labels specified and group_left",
|
desc: "many-to-one matching with matching labels specified and group_left",
|
||||||
op: binaryOperatorType.sub,
|
op: binaryOperatorType.sub,
|
||||||
matching: {
|
matching: {
|
||||||
|
@ -891,6 +897,7 @@ const testCases: TestCase[] = [
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
// metric_c - on(label1) group_right metric_b
|
||||||
desc: "one-to-many matching with matching labels specified and group_right",
|
desc: "one-to-many matching with matching labels specified and group_right",
|
||||||
op: binaryOperatorType.sub,
|
op: binaryOperatorType.sub,
|
||||||
matching: {
|
matching: {
|
||||||
|
@ -1004,6 +1011,7 @@ const testCases: TestCase[] = [
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
// metric_c - on(label1) group_left metric_b
|
||||||
desc: "one-to-many matching with matching labels specified but incorrect group_left (error)",
|
desc: "one-to-many matching with matching labels specified but incorrect group_left (error)",
|
||||||
op: binaryOperatorType.sub,
|
op: binaryOperatorType.sub,
|
||||||
matching: {
|
matching: {
|
||||||
|
@ -1091,6 +1099,7 @@ const testCases: TestCase[] = [
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
// metric_a - on(label1) metric_b
|
||||||
desc: "insufficient matching labels leading to many-to-many matching for intended one-to-one match (error)",
|
desc: "insufficient matching labels leading to many-to-many matching for intended one-to-one match (error)",
|
||||||
op: binaryOperatorType.sub,
|
op: binaryOperatorType.sub,
|
||||||
matching: {
|
matching: {
|
||||||
|
@ -1206,6 +1215,7 @@ const testCases: TestCase[] = [
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
// metric_a < metric_b
|
||||||
desc: "filter op keeping all series",
|
desc: "filter op keeping all series",
|
||||||
op: binaryOperatorType.lss,
|
op: binaryOperatorType.lss,
|
||||||
matching: {
|
matching: {
|
||||||
|
@ -1391,6 +1401,7 @@ const testCases: TestCase[] = [
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
// metric_a >= metric_b
|
||||||
desc: "filter op dropping all series",
|
desc: "filter op dropping all series",
|
||||||
op: binaryOperatorType.gte,
|
op: binaryOperatorType.gte,
|
||||||
matching: {
|
matching: {
|
||||||
|
@ -1576,6 +1587,7 @@ const testCases: TestCase[] = [
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
// metric_a >= bool metric_b
|
||||||
desc: "filter op dropping all series, but with bool",
|
desc: "filter op dropping all series, but with bool",
|
||||||
op: binaryOperatorType.gte,
|
op: binaryOperatorType.gte,
|
||||||
bool: true,
|
bool: true,
|
||||||
|
@ -1742,6 +1754,7 @@ const testCases: TestCase[] = [
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
// metric_a < bool metric_b
|
||||||
desc: "filter op keeping all series, but with bool",
|
desc: "filter op keeping all series, but with bool",
|
||||||
op: binaryOperatorType.lss,
|
op: binaryOperatorType.lss,
|
||||||
bool: true,
|
bool: true,
|
||||||
|
@ -1908,6 +1921,7 @@ const testCases: TestCase[] = [
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
// metric_a - metric_b
|
||||||
desc: "exceeding the match group limit",
|
desc: "exceeding the match group limit",
|
||||||
op: binaryOperatorType.sub,
|
op: binaryOperatorType.sub,
|
||||||
matching: {
|
matching: {
|
||||||
|
@ -2000,6 +2014,7 @@ const testCases: TestCase[] = [
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
// metric_c - on(label1) group_left metric_b
|
||||||
desc: "exceeding the per-group series limit",
|
desc: "exceeding the per-group series limit",
|
||||||
op: binaryOperatorType.sub,
|
op: binaryOperatorType.sub,
|
||||||
matching: {
|
matching: {
|
||||||
|
@ -2082,6 +2097,7 @@ const testCases: TestCase[] = [
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
// metric_c - on(label1) group_left metric_b
|
||||||
desc: "exceeding both group limit and per-group series limit",
|
desc: "exceeding both group limit and per-group series limit",
|
||||||
op: binaryOperatorType.sub,
|
op: binaryOperatorType.sub,
|
||||||
matching: {
|
matching: {
|
||||||
|
@ -2131,6 +2147,732 @@ const testCases: TestCase[] = [
|
||||||
numGroups: 2,
|
numGroups: 2,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
// metric_a and metric b
|
||||||
|
desc: "and operator with no matching labels and matching groups",
|
||||||
|
op: binaryOperatorType.and,
|
||||||
|
matching: {
|
||||||
|
card: vectorMatchCardinality.manyToMany,
|
||||||
|
on: false,
|
||||||
|
include: [],
|
||||||
|
labels: [],
|
||||||
|
},
|
||||||
|
lhs: testMetricA,
|
||||||
|
rhs: testMetricB,
|
||||||
|
result: {
|
||||||
|
groups: {
|
||||||
|
[fnv1a(["a", "x", "same"])]: {
|
||||||
|
groupLabels: { label1: "a", label2: "x", same: "same" },
|
||||||
|
lhs: [
|
||||||
|
{
|
||||||
|
metric: {
|
||||||
|
__name__: "metric_a",
|
||||||
|
label1: "a",
|
||||||
|
label2: "x",
|
||||||
|
same: "same",
|
||||||
|
},
|
||||||
|
value: [0, "1"],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
lhsCount: 1,
|
||||||
|
rhs: [
|
||||||
|
{
|
||||||
|
metric: {
|
||||||
|
__name__: "metric_b",
|
||||||
|
label1: "a",
|
||||||
|
label2: "x",
|
||||||
|
same: "same",
|
||||||
|
},
|
||||||
|
value: [0, "10"],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
rhsCount: 1,
|
||||||
|
result: [
|
||||||
|
{
|
||||||
|
sample: {
|
||||||
|
metric: {
|
||||||
|
__name__: "metric_a",
|
||||||
|
label1: "a",
|
||||||
|
label2: "x",
|
||||||
|
same: "same",
|
||||||
|
},
|
||||||
|
value: [0, "1"],
|
||||||
|
},
|
||||||
|
manySideIdx: 0,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
error: null,
|
||||||
|
},
|
||||||
|
[fnv1a(["a", "y", "same"])]: {
|
||||||
|
groupLabels: { label1: "a", label2: "y", same: "same" },
|
||||||
|
lhs: [
|
||||||
|
{
|
||||||
|
metric: {
|
||||||
|
__name__: "metric_a",
|
||||||
|
label1: "a",
|
||||||
|
label2: "y",
|
||||||
|
same: "same",
|
||||||
|
},
|
||||||
|
value: [0, "2"],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
lhsCount: 1,
|
||||||
|
rhs: [
|
||||||
|
{
|
||||||
|
metric: {
|
||||||
|
__name__: "metric_b",
|
||||||
|
label1: "a",
|
||||||
|
label2: "y",
|
||||||
|
same: "same",
|
||||||
|
},
|
||||||
|
value: [0, "20"],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
rhsCount: 1,
|
||||||
|
result: [
|
||||||
|
{
|
||||||
|
sample: {
|
||||||
|
metric: {
|
||||||
|
__name__: "metric_a",
|
||||||
|
label1: "a",
|
||||||
|
label2: "y",
|
||||||
|
same: "same",
|
||||||
|
},
|
||||||
|
value: [0, "2"],
|
||||||
|
},
|
||||||
|
manySideIdx: 0,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
error: null,
|
||||||
|
},
|
||||||
|
[fnv1a(["b", "x", "same"])]: {
|
||||||
|
groupLabels: { label1: "b", label2: "x", same: "same" },
|
||||||
|
lhs: [
|
||||||
|
{
|
||||||
|
metric: {
|
||||||
|
__name__: "metric_a",
|
||||||
|
label1: "b",
|
||||||
|
label2: "x",
|
||||||
|
same: "same",
|
||||||
|
},
|
||||||
|
value: [0, "3"],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
lhsCount: 1,
|
||||||
|
rhs: [
|
||||||
|
{
|
||||||
|
metric: {
|
||||||
|
__name__: "metric_b",
|
||||||
|
label1: "b",
|
||||||
|
label2: "x",
|
||||||
|
same: "same",
|
||||||
|
},
|
||||||
|
value: [0, "30"],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
rhsCount: 1,
|
||||||
|
result: [
|
||||||
|
{
|
||||||
|
sample: {
|
||||||
|
metric: {
|
||||||
|
__name__: "metric_a",
|
||||||
|
label1: "b",
|
||||||
|
label2: "x",
|
||||||
|
same: "same",
|
||||||
|
},
|
||||||
|
value: [0, "3"],
|
||||||
|
},
|
||||||
|
manySideIdx: 0,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
error: null,
|
||||||
|
},
|
||||||
|
[fnv1a(["b", "y", "same"])]: {
|
||||||
|
groupLabels: { label1: "b", label2: "y", same: "same" },
|
||||||
|
lhs: [
|
||||||
|
{
|
||||||
|
metric: {
|
||||||
|
__name__: "metric_a",
|
||||||
|
label1: "b",
|
||||||
|
label2: "y",
|
||||||
|
same: "same",
|
||||||
|
},
|
||||||
|
value: [0, "4"],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
lhsCount: 1,
|
||||||
|
rhs: [
|
||||||
|
{
|
||||||
|
metric: {
|
||||||
|
__name__: "metric_b",
|
||||||
|
label1: "b",
|
||||||
|
label2: "y",
|
||||||
|
same: "same",
|
||||||
|
},
|
||||||
|
value: [0, "40"],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
rhsCount: 1,
|
||||||
|
result: [
|
||||||
|
{
|
||||||
|
sample: {
|
||||||
|
metric: {
|
||||||
|
__name__: "metric_a",
|
||||||
|
label1: "b",
|
||||||
|
label2: "y",
|
||||||
|
same: "same",
|
||||||
|
},
|
||||||
|
value: [0, "4"],
|
||||||
|
},
|
||||||
|
manySideIdx: 0,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
error: null,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
numGroups: 4,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
// metric_a[0...2] and on(label1) metric_b[1...3]
|
||||||
|
desc: "and operator with matching label and series on each side",
|
||||||
|
op: binaryOperatorType.and,
|
||||||
|
matching: {
|
||||||
|
card: vectorMatchCardinality.manyToMany,
|
||||||
|
on: true,
|
||||||
|
include: [],
|
||||||
|
labels: ["label1"],
|
||||||
|
},
|
||||||
|
lhs: testMetricA.slice(0, 3),
|
||||||
|
rhs: testMetricB.slice(1, 4),
|
||||||
|
result: {
|
||||||
|
groups: {
|
||||||
|
[fnv1a(["a"])]: {
|
||||||
|
groupLabels: { label1: "a" },
|
||||||
|
lhs: [
|
||||||
|
{
|
||||||
|
metric: {
|
||||||
|
__name__: "metric_a",
|
||||||
|
label1: "a",
|
||||||
|
label2: "x",
|
||||||
|
same: "same",
|
||||||
|
},
|
||||||
|
value: [0, "1"],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
metric: {
|
||||||
|
__name__: "metric_a",
|
||||||
|
label1: "a",
|
||||||
|
label2: "y",
|
||||||
|
same: "same",
|
||||||
|
},
|
||||||
|
value: [0, "2"],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
lhsCount: 2,
|
||||||
|
rhs: [
|
||||||
|
{
|
||||||
|
metric: {
|
||||||
|
__name__: "metric_b",
|
||||||
|
label1: "a",
|
||||||
|
label2: "y",
|
||||||
|
same: "same",
|
||||||
|
},
|
||||||
|
value: [0, "20"],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
rhsCount: 1,
|
||||||
|
result: [
|
||||||
|
{
|
||||||
|
sample: {
|
||||||
|
metric: {
|
||||||
|
__name__: "metric_a",
|
||||||
|
label1: "a",
|
||||||
|
label2: "x",
|
||||||
|
same: "same",
|
||||||
|
},
|
||||||
|
value: [0, "1"],
|
||||||
|
},
|
||||||
|
manySideIdx: 0,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
sample: {
|
||||||
|
metric: {
|
||||||
|
__name__: "metric_a",
|
||||||
|
label1: "a",
|
||||||
|
label2: "y",
|
||||||
|
same: "same",
|
||||||
|
},
|
||||||
|
value: [0, "2"],
|
||||||
|
},
|
||||||
|
manySideIdx: 1,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
error: null,
|
||||||
|
},
|
||||||
|
[fnv1a(["b"])]: {
|
||||||
|
groupLabels: { label1: "b" },
|
||||||
|
lhs: [
|
||||||
|
{
|
||||||
|
metric: {
|
||||||
|
__name__: "metric_a",
|
||||||
|
label1: "b",
|
||||||
|
label2: "x",
|
||||||
|
same: "same",
|
||||||
|
},
|
||||||
|
value: [0, "3"],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
lhsCount: 1,
|
||||||
|
rhs: [
|
||||||
|
{
|
||||||
|
metric: {
|
||||||
|
__name__: "metric_b",
|
||||||
|
label1: "b",
|
||||||
|
label2: "x",
|
||||||
|
same: "same",
|
||||||
|
},
|
||||||
|
value: [0, "30"],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
metric: {
|
||||||
|
__name__: "metric_b",
|
||||||
|
label1: "b",
|
||||||
|
label2: "y",
|
||||||
|
same: "same",
|
||||||
|
},
|
||||||
|
value: [0, "40"],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
rhsCount: 2,
|
||||||
|
result: [
|
||||||
|
{
|
||||||
|
sample: {
|
||||||
|
metric: {
|
||||||
|
__name__: "metric_a",
|
||||||
|
label1: "b",
|
||||||
|
label2: "x",
|
||||||
|
same: "same",
|
||||||
|
},
|
||||||
|
value: [0, "3"],
|
||||||
|
},
|
||||||
|
manySideIdx: 0,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
error: null,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
numGroups: 2,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
// metric_a[0...2] unless on(label1) metric_b[1...3]
|
||||||
|
desc: "unless operator with matching label and series on each side",
|
||||||
|
op: binaryOperatorType.unless,
|
||||||
|
matching: {
|
||||||
|
card: vectorMatchCardinality.manyToMany,
|
||||||
|
on: true,
|
||||||
|
include: [],
|
||||||
|
labels: ["label1"],
|
||||||
|
},
|
||||||
|
lhs: testMetricA.slice(0, 3),
|
||||||
|
rhs: testMetricB.slice(1, 4),
|
||||||
|
result: {
|
||||||
|
groups: {
|
||||||
|
[fnv1a(["a"])]: {
|
||||||
|
groupLabels: { label1: "a" },
|
||||||
|
lhs: [
|
||||||
|
{
|
||||||
|
metric: {
|
||||||
|
__name__: "metric_a",
|
||||||
|
label1: "a",
|
||||||
|
label2: "x",
|
||||||
|
same: "same",
|
||||||
|
},
|
||||||
|
value: [0, "1"],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
metric: {
|
||||||
|
__name__: "metric_a",
|
||||||
|
label1: "a",
|
||||||
|
label2: "y",
|
||||||
|
same: "same",
|
||||||
|
},
|
||||||
|
value: [0, "2"],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
lhsCount: 2,
|
||||||
|
rhs: [
|
||||||
|
{
|
||||||
|
metric: {
|
||||||
|
__name__: "metric_b",
|
||||||
|
label1: "a",
|
||||||
|
label2: "y",
|
||||||
|
same: "same",
|
||||||
|
},
|
||||||
|
value: [0, "20"],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
rhsCount: 1,
|
||||||
|
result: [],
|
||||||
|
error: null,
|
||||||
|
},
|
||||||
|
[fnv1a(["b"])]: {
|
||||||
|
groupLabels: { label1: "b" },
|
||||||
|
lhs: [
|
||||||
|
{
|
||||||
|
metric: {
|
||||||
|
__name__: "metric_a",
|
||||||
|
label1: "b",
|
||||||
|
label2: "x",
|
||||||
|
same: "same",
|
||||||
|
},
|
||||||
|
value: [0, "3"],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
lhsCount: 1,
|
||||||
|
rhs: [
|
||||||
|
{
|
||||||
|
metric: {
|
||||||
|
__name__: "metric_b",
|
||||||
|
label1: "b",
|
||||||
|
label2: "x",
|
||||||
|
same: "same",
|
||||||
|
},
|
||||||
|
value: [0, "30"],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
metric: {
|
||||||
|
__name__: "metric_b",
|
||||||
|
label1: "b",
|
||||||
|
label2: "y",
|
||||||
|
same: "same",
|
||||||
|
},
|
||||||
|
value: [0, "40"],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
rhsCount: 2,
|
||||||
|
result: [],
|
||||||
|
error: null,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
numGroups: 2,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
// metric_a[0...2] or on(label1) metric_b[1...3]
|
||||||
|
desc: "or operator with matching label and series on each side",
|
||||||
|
op: binaryOperatorType.or,
|
||||||
|
matching: {
|
||||||
|
card: vectorMatchCardinality.manyToMany,
|
||||||
|
on: true,
|
||||||
|
include: [],
|
||||||
|
labels: ["label1"],
|
||||||
|
},
|
||||||
|
lhs: testMetricA.slice(0, 3),
|
||||||
|
rhs: testMetricB.slice(1, 4),
|
||||||
|
result: {
|
||||||
|
groups: {
|
||||||
|
[fnv1a(["a"])]: {
|
||||||
|
groupLabels: { label1: "a" },
|
||||||
|
lhs: [
|
||||||
|
{
|
||||||
|
metric: {
|
||||||
|
__name__: "metric_a",
|
||||||
|
label1: "a",
|
||||||
|
label2: "x",
|
||||||
|
same: "same",
|
||||||
|
},
|
||||||
|
value: [0, "1"],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
metric: {
|
||||||
|
__name__: "metric_a",
|
||||||
|
label1: "a",
|
||||||
|
label2: "y",
|
||||||
|
same: "same",
|
||||||
|
},
|
||||||
|
value: [0, "2"],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
lhsCount: 2,
|
||||||
|
rhs: [
|
||||||
|
{
|
||||||
|
metric: {
|
||||||
|
__name__: "metric_b",
|
||||||
|
label1: "a",
|
||||||
|
label2: "y",
|
||||||
|
same: "same",
|
||||||
|
},
|
||||||
|
value: [0, "20"],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
rhsCount: 1,
|
||||||
|
result: [
|
||||||
|
{
|
||||||
|
sample: {
|
||||||
|
metric: {
|
||||||
|
__name__: "metric_a",
|
||||||
|
label1: "a",
|
||||||
|
label2: "x",
|
||||||
|
same: "same",
|
||||||
|
},
|
||||||
|
value: [0, "1"],
|
||||||
|
},
|
||||||
|
manySideIdx: 0,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
sample: {
|
||||||
|
metric: {
|
||||||
|
__name__: "metric_a",
|
||||||
|
label1: "a",
|
||||||
|
label2: "y",
|
||||||
|
same: "same",
|
||||||
|
},
|
||||||
|
value: [0, "2"],
|
||||||
|
},
|
||||||
|
manySideIdx: 1,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
error: null,
|
||||||
|
},
|
||||||
|
[fnv1a(["b"])]: {
|
||||||
|
groupLabels: { label1: "b" },
|
||||||
|
lhs: [
|
||||||
|
{
|
||||||
|
metric: {
|
||||||
|
__name__: "metric_a",
|
||||||
|
label1: "b",
|
||||||
|
label2: "x",
|
||||||
|
same: "same",
|
||||||
|
},
|
||||||
|
value: [0, "3"],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
lhsCount: 1,
|
||||||
|
rhs: [
|
||||||
|
{
|
||||||
|
metric: {
|
||||||
|
__name__: "metric_b",
|
||||||
|
label1: "b",
|
||||||
|
label2: "x",
|
||||||
|
same: "same",
|
||||||
|
},
|
||||||
|
value: [0, "30"],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
metric: {
|
||||||
|
__name__: "metric_b",
|
||||||
|
label1: "b",
|
||||||
|
label2: "y",
|
||||||
|
same: "same",
|
||||||
|
},
|
||||||
|
value: [0, "40"],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
rhsCount: 2,
|
||||||
|
result: [
|
||||||
|
{
|
||||||
|
sample: {
|
||||||
|
metric: {
|
||||||
|
__name__: "metric_a",
|
||||||
|
label1: "b",
|
||||||
|
label2: "x",
|
||||||
|
same: "same",
|
||||||
|
},
|
||||||
|
value: [0, "3"],
|
||||||
|
},
|
||||||
|
manySideIdx: 0,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
error: null,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
numGroups: 2,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
// metric_a[0...2] or metric_b[1...3]
|
||||||
|
desc: "or operator with only partial overlap",
|
||||||
|
op: binaryOperatorType.or,
|
||||||
|
matching: {
|
||||||
|
card: vectorMatchCardinality.manyToMany,
|
||||||
|
on: false,
|
||||||
|
include: [],
|
||||||
|
labels: [],
|
||||||
|
},
|
||||||
|
lhs: testMetricA.slice(0, 3),
|
||||||
|
rhs: testMetricB.slice(1, 4),
|
||||||
|
result: {
|
||||||
|
groups: {
|
||||||
|
[fnv1a(["a", "x", "same"])]: {
|
||||||
|
groupLabels: {
|
||||||
|
label1: "a",
|
||||||
|
label2: "x",
|
||||||
|
same: "same",
|
||||||
|
},
|
||||||
|
lhs: [
|
||||||
|
{
|
||||||
|
metric: {
|
||||||
|
__name__: "metric_a",
|
||||||
|
label1: "a",
|
||||||
|
label2: "x",
|
||||||
|
same: "same",
|
||||||
|
},
|
||||||
|
value: [0, "1"],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
lhsCount: 1,
|
||||||
|
rhs: [],
|
||||||
|
rhsCount: 0,
|
||||||
|
result: [
|
||||||
|
{
|
||||||
|
sample: {
|
||||||
|
metric: {
|
||||||
|
__name__: "metric_a",
|
||||||
|
label1: "a",
|
||||||
|
label2: "x",
|
||||||
|
same: "same",
|
||||||
|
},
|
||||||
|
value: [0, "1"],
|
||||||
|
},
|
||||||
|
manySideIdx: 0,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
error: null,
|
||||||
|
},
|
||||||
|
[fnv1a(["a", "y", "same"])]: {
|
||||||
|
groupLabels: {
|
||||||
|
label1: "a",
|
||||||
|
label2: "y",
|
||||||
|
same: "same",
|
||||||
|
},
|
||||||
|
lhs: [
|
||||||
|
{
|
||||||
|
metric: {
|
||||||
|
__name__: "metric_a",
|
||||||
|
label1: "a",
|
||||||
|
label2: "y",
|
||||||
|
same: "same",
|
||||||
|
},
|
||||||
|
value: [0, "2"],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
lhsCount: 1,
|
||||||
|
rhs: [
|
||||||
|
{
|
||||||
|
metric: {
|
||||||
|
__name__: "metric_b",
|
||||||
|
label1: "a",
|
||||||
|
label2: "y",
|
||||||
|
same: "same",
|
||||||
|
},
|
||||||
|
value: [0, "20"],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
rhsCount: 1,
|
||||||
|
result: [
|
||||||
|
{
|
||||||
|
sample: {
|
||||||
|
metric: {
|
||||||
|
__name__: "metric_a",
|
||||||
|
label1: "a",
|
||||||
|
label2: "y",
|
||||||
|
same: "same",
|
||||||
|
},
|
||||||
|
value: [0, "2"],
|
||||||
|
},
|
||||||
|
manySideIdx: 0,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
error: null,
|
||||||
|
},
|
||||||
|
[fnv1a(["b", "x", "same"])]: {
|
||||||
|
groupLabels: {
|
||||||
|
label1: "b",
|
||||||
|
label2: "x",
|
||||||
|
same: "same",
|
||||||
|
},
|
||||||
|
lhs: [
|
||||||
|
{
|
||||||
|
metric: {
|
||||||
|
__name__: "metric_a",
|
||||||
|
label1: "b",
|
||||||
|
label2: "x",
|
||||||
|
same: "same",
|
||||||
|
},
|
||||||
|
value: [0, "3"],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
lhsCount: 1,
|
||||||
|
rhs: [
|
||||||
|
{
|
||||||
|
metric: {
|
||||||
|
__name__: "metric_b",
|
||||||
|
label1: "b",
|
||||||
|
label2: "x",
|
||||||
|
same: "same",
|
||||||
|
},
|
||||||
|
value: [0, "30"],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
rhsCount: 1,
|
||||||
|
result: [
|
||||||
|
{
|
||||||
|
sample: {
|
||||||
|
metric: {
|
||||||
|
__name__: "metric_a",
|
||||||
|
label1: "b",
|
||||||
|
label2: "x",
|
||||||
|
same: "same",
|
||||||
|
},
|
||||||
|
value: [0, "3"],
|
||||||
|
},
|
||||||
|
manySideIdx: 0,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
error: null,
|
||||||
|
},
|
||||||
|
[fnv1a(["b", "y", "same"])]: {
|
||||||
|
groupLabels: {
|
||||||
|
label1: "b",
|
||||||
|
label2: "y",
|
||||||
|
same: "same",
|
||||||
|
},
|
||||||
|
lhs: [],
|
||||||
|
lhsCount: 0,
|
||||||
|
rhs: [
|
||||||
|
{
|
||||||
|
metric: {
|
||||||
|
__name__: "metric_b",
|
||||||
|
label1: "b",
|
||||||
|
label2: "y",
|
||||||
|
same: "same",
|
||||||
|
},
|
||||||
|
value: [0, "40"],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
rhsCount: 1,
|
||||||
|
result: [
|
||||||
|
{
|
||||||
|
sample: {
|
||||||
|
metric: {
|
||||||
|
__name__: "metric_b",
|
||||||
|
label1: "b",
|
||||||
|
label2: "y",
|
||||||
|
same: "same",
|
||||||
|
},
|
||||||
|
value: [0, "40"],
|
||||||
|
},
|
||||||
|
manySideIdx: 0,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
error: null,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
numGroups: 4,
|
||||||
|
},
|
||||||
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
describe("binOp", () => {
|
describe("binOp", () => {
|
||||||
|
|
|
@ -340,25 +340,35 @@ export const computeVectorVectorBinOp = (
|
||||||
|
|
||||||
// Annotate the match groups with errors (if any) and populate the results.
|
// Annotate the match groups with errors (if any) and populate the results.
|
||||||
Object.values(groups).forEach((mg) => {
|
Object.values(groups).forEach((mg) => {
|
||||||
// Do not populate results for set operators.
|
switch (matching.card) {
|
||||||
if (isSetOperator(op)) {
|
case vectorMatchCardinality.oneToOne:
|
||||||
return;
|
if (mg.lhs.length > 1 && mg.rhs.length > 1) {
|
||||||
}
|
mg.error = { type: MatchErrorType.multipleMatchesOnBothSides };
|
||||||
|
} else if (mg.lhs.length > 1 || mg.rhs.length > 1) {
|
||||||
if (matching.card === vectorMatchCardinality.oneToOne) {
|
mg.error = {
|
||||||
if (mg.lhs.length > 1 && mg.rhs.length > 1) {
|
type: MatchErrorType.multipleMatchesForOneToOneMatching,
|
||||||
mg.error = { type: MatchErrorType.multipleMatchesOnBothSides };
|
dupeSide: mg.lhs.length > 1 ? "left" : "right",
|
||||||
} else if (mg.lhs.length > 1 || mg.rhs.length > 1) {
|
};
|
||||||
mg.error = {
|
}
|
||||||
type: MatchErrorType.multipleMatchesForOneToOneMatching,
|
break;
|
||||||
dupeSide: mg.lhs.length > 1 ? "left" : "right",
|
case vectorMatchCardinality.oneToMany:
|
||||||
};
|
case vectorMatchCardinality.manyToOne:
|
||||||
}
|
if (mg.rhs.length > 1) {
|
||||||
} else if (mg.rhs.length > 1) {
|
mg.error = {
|
||||||
// Check for dupes on the "one" side in one-to-many or many-to-one matching.
|
type: MatchErrorType.multipleMatchesOnOneSide,
|
||||||
mg.error = {
|
};
|
||||||
type: MatchErrorType.multipleMatchesOnOneSide,
|
}
|
||||||
};
|
break;
|
||||||
|
case vectorMatchCardinality.manyToMany:
|
||||||
|
// Should be a set operator - these don't have errors that aren't caught during parsing.
|
||||||
|
if (!isSetOperator(op)) {
|
||||||
|
throw new Error(
|
||||||
|
"unexpected many-to-many matching for non-set operator"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new Error("unknown vector matching cardinality");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mg.error) {
|
if (mg.error) {
|
||||||
|
@ -368,42 +378,79 @@ export const computeVectorVectorBinOp = (
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Calculate the results for this match group.
|
if (isSetOperator(op)) {
|
||||||
mg.rhs.forEach((rs) => {
|
// Add LHS samples to the result, depending on specific operator condition and RHS length.
|
||||||
mg.lhs.forEach((ls, lIdx) => {
|
mg.lhs.forEach((ls, lIdx) => {
|
||||||
if (!ls.value || !rs.value) {
|
if (
|
||||||
// TODO: Implement native histogram support.
|
(op === binaryOperatorType.and && mg.rhs.length > 0) ||
|
||||||
throw new Error("native histogram support not implemented yet");
|
(op === binaryOperatorType.unless && mg.rhs.length === 0) ||
|
||||||
|
op === binaryOperatorType.or
|
||||||
|
) {
|
||||||
|
mg.result.push({
|
||||||
|
sample: {
|
||||||
|
metric: ls.metric,
|
||||||
|
value: ls.value,
|
||||||
|
},
|
||||||
|
manySideIdx: lIdx,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
});
|
||||||
|
|
||||||
const [vl, vr] =
|
// For OR, also add all RHS samples to the result if the LHS for the group is empty.
|
||||||
matching.card !== vectorMatchCardinality.oneToMany
|
if (op === binaryOperatorType.or) {
|
||||||
? [ls.value[1], rs.value[1]]
|
mg.rhs.forEach((rs, rIdx) => {
|
||||||
: [rs.value[1], ls.value[1]];
|
if (mg.lhs.length === 0) {
|
||||||
let { value, keep } = vectorElemBinop(
|
mg.result.push({
|
||||||
op,
|
sample: {
|
||||||
parsePrometheusFloat(vl),
|
metric: rs.metric,
|
||||||
parsePrometheusFloat(vr)
|
value: rs.value,
|
||||||
);
|
},
|
||||||
|
manySideIdx: rIdx,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Calculate the results for this match group.
|
||||||
|
mg.rhs.forEach((rs) => {
|
||||||
|
mg.lhs.forEach((ls, lIdx) => {
|
||||||
|
if (!ls.value || !rs.value) {
|
||||||
|
// TODO: Implement native histogram support.
|
||||||
|
throw new Error("native histogram support not implemented yet");
|
||||||
|
}
|
||||||
|
|
||||||
const metric = resultMetric(ls.metric, rs.metric, op, matching);
|
const [vl, vr] =
|
||||||
if (bool) {
|
matching.card !== vectorMatchCardinality.oneToMany
|
||||||
value = keep ? 1.0 : 0.0;
|
? [ls.value[1], rs.value[1]]
|
||||||
delete metric.__name__;
|
: [rs.value[1], ls.value[1]];
|
||||||
}
|
|
||||||
|
|
||||||
mg.result.push({
|
let { value, keep } = vectorElemBinop(
|
||||||
sample: {
|
op,
|
||||||
metric: metric,
|
parsePrometheusFloat(vl),
|
||||||
value: [
|
parsePrometheusFloat(vr)
|
||||||
ls.value[0],
|
);
|
||||||
keep || bool ? formatPrometheusFloat(value) : filteredSampleValue,
|
|
||||||
],
|
const metric = resultMetric(ls.metric, rs.metric, op, matching);
|
||||||
},
|
if (bool) {
|
||||||
manySideIdx: lIdx,
|
value = keep ? 1.0 : 0.0;
|
||||||
|
delete metric.__name__;
|
||||||
|
}
|
||||||
|
|
||||||
|
mg.result.push({
|
||||||
|
sample: {
|
||||||
|
metric: metric,
|
||||||
|
value: [
|
||||||
|
ls.value[0],
|
||||||
|
keep || bool
|
||||||
|
? formatPrometheusFloat(value)
|
||||||
|
: filteredSampleValue,
|
||||||
|
],
|
||||||
|
},
|
||||||
|
manySideIdx: lIdx,
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// If we originally swapped the LHS and RHS, swap them back to the original order.
|
// If we originally swapped the LHS and RHS, swap them back to the original order.
|
||||||
|
|
Loading…
Reference in a new issue