Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ui: allow stmts page to search across explain plan page #75097

Merged
merged 1 commit into from
Jan 25, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ import {
flattenTreeAttributes,
flattenAttributes,
standardizeKey,
planNodeToString,
planNodeAttrsToString,
} from "./planView";
import IAttr = cockroach.sql.ExplainTreePlanNode.IAttr;

Expand Down Expand Up @@ -260,4 +262,94 @@ describe("planView", () => {
assert.equal(standardizeKey("(anti) hello world"), "helloWorld");
});
});

describe("planNodeAttrsToString", () => {
it("should convert an array of FlatPlanNodeAttribute[] into a string", () => {
const testNodeAttrs: FlatPlanNodeAttribute[] = [
{
key: "Into",
values: ["users(id, city, name, address, credit_card)"],
warn: false,
},
{
key: "Size",
values: ["5 columns, 3 rows"],
warn: false,
},
];

const expectedString =
"Into users(id, city, name, address, credit_card) Size 5 columns, 3 rows";

assert.equal(planNodeAttrsToString(testNodeAttrs), expectedString);
});
});

describe("planNodeToString", () => {
it("should recursively convert a FlatPlanNode into a string.", () => {
const testPlanNode: FlatPlanNode = {
name: "insert fast path",
attrs: [
{
key: "Into",
values: ["users(id, city, name, address, credit_card)"],
warn: false,
},
{
key: "Size",
values: ["5 columns, 3 rows"],
warn: false,
},
],
children: [],
};

const expectedString =
"insert fast path Into users(id, city, name, address, credit_card) Size 5 columns, 3 rows";

assert.equal(planNodeToString(testPlanNode), expectedString);
});

it("should recursively convert a FlatPlanNode (with children) into a string.", () => {
const testPlanNode: FlatPlanNode = {
name: "render",
attrs: [],
children: [
{
name: "group (scalar)",
attrs: [],
children: [
{
name: "filter",
attrs: [
{
key: "filter",
values: ["variable = _"],
warn: false,
},
],
children: [
{
name: "virtual table",
attrs: [
{
key: "table",
values: ["cluster_settings@primary"],
warn: false,
},
],
children: [],
},
],
},
],
},
],
};

const expectedString =
"render group (scalar) filter filter variable = _ virtual table table cluster_settings@primary";
assert.equal(planNodeToString(testPlanNode), expectedString);
});
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,24 @@ function warnForAttribute(attr: IAttr): boolean {
return false;
}

// planNodeAttrsToString converts an array of FlatPlanNodeAttribute[] into a string.
export function planNodeAttrsToString(attrs: FlatPlanNodeAttribute[]): string {
return attrs.map(attr => `${attr.key} ${attr.values.join(" ")}`).join(" ");
}

// planNodeToString recursively converts a FlatPlanNode into a string.
export function planNodeToString(plan: FlatPlanNode): string {
const str = `${plan.name} ${planNodeAttrsToString(plan.attrs)}`;

if (plan.children.length > 0) {
return `${str} ${plan.children
.map(child => planNodeToString(child))
.join(" ")}`;
}

return str;
}

// flattenAttributes takes a list of attrs (IAttr[]) and collapses
// all the values for the same key (FlatPlanNodeAttribute). For example,
// if attrs was:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,14 @@ import { ReactWrapper, mount } from "enzyme";
import { MemoryRouter } from "react-router-dom";

import {
filterBySearchQuery,
StatementsPage,
StatementsPageProps,
StatementsPageState,
} from "src/statementsPage";
import statementsPagePropsFixture from "./statementsPage.fixture";
import { AggregateStatistics } from "../statementsTable";
import { FlatPlanNode } from "../statementDetails";

describe("StatementsPage", () => {
describe("Statements table", () => {
Expand All @@ -43,4 +46,65 @@ describe("StatementsPage", () => {
assert.equal(statementsPageInstance.props.sortSetting.ascending, false);
});
});

describe("filterBySearchQuery", () => {
const testPlanNode: FlatPlanNode = {
name: "render",
attrs: [],
children: [
{
name: "group (scalar)",
attrs: [],
children: [
{
name: "filter",
attrs: [
{
key: "filter",
values: ["variable = _"],
warn: false,
},
],
children: [
{
name: "virtual table",
attrs: [
{
key: "table",
values: ["cluster_settings@primary"],
warn: false,
},
],
children: [],
},
],
},
],
},
],
};

const statement: AggregateStatistics = {
aggregatedFingerprintID: "",
aggregatedTs: 0,
aggregationInterval: 0,
database: "",
fullScan: false,
implicitTxn: false,
summary: "",
label:
"SELECT count(*) > _ FROM [SHOW ALL CLUSTER SETTINGS] AS _ (v) WHERE v = '_'",
stats: {
sensitive_info: {
most_recent_plan_description: testPlanNode,
},
},
};

assert.equal(filterBySearchQuery(statement, "select"), true);
assert.equal(filterBySearchQuery(statement, "virtual table"), true);
assert.equal(filterBySearchQuery(statement, "group (scalar)"), true);
assert.equal(filterBySearchQuery(statement, "node_build_info"), false);
assert.equal(filterBySearchQuery(statement, "crdb_internal"), false);
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ import {
} from "../timeScaleDropdown";

import { commonStyles } from "../common";
import { flattenTreeAttributes, planNodeToString } from "../statementDetails";
const cx = classNames.bind(styles);
const sortableTableCx = classNames.bind(sortableTableStyles);

Expand Down Expand Up @@ -146,6 +147,25 @@ function statementsRequestFromProps(
});
}

// filterBySearchQuery returns true if a search query matches the statement.
export function filterBySearchQuery(
statement: AggregateStatistics,
search: string,
): boolean {
const label = statement.label;
const plan = planNodeToString(
flattenTreeAttributes(
statement.stats.sensitive_info &&
statement.stats.sensitive_info.most_recent_plan_description,
),
);
const matchString = `${label} ${plan}`.toLowerCase();
return search
.toLowerCase()
.split(" ")
.every(val => matchString.includes(val));
}

export class StatementsPage extends React.Component<
StatementsPageProps,
StatementsPageState
Expand Down Expand Up @@ -408,15 +428,6 @@ export class StatementsPage extends React.Component<
databases.length == 0 || databases.includes(statement.database),
)
.filter(statement => (filters.fullScan ? statement.fullScan : true))
.filter(statement =>
search
? search
.split(" ")
.every(val =>
statement.label.toLowerCase().includes(val.toLowerCase()),
)
: true,
)
.filter(
statement =>
statement.stats.service_lat.mean >= timeValue ||
Expand Down Expand Up @@ -456,6 +467,9 @@ export class StatementsPage extends React.Component<
statement.stats.nodes.map(node => "n" + node),
nodes,
)),
)
.filter(statement =>
search ? filterBySearchQuery(statement, search) : true,
);
};

Expand Down