Skip to content

feat: Update covered queries with new links, esr order swap, copy change CLOUDP-323671 #7012

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

Merged
merged 6 commits into from
Jun 13, 2025
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 @@ -96,7 +96,7 @@ function CreateIndexFields({
track('New Index Field Added', {
context: 'Create Index Modal',
});
}, []);
}, [onAddFieldClick]);

const comboboxOptions = schemaFields.map((value) => ({ value }));

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,55 @@ describe('IndexFlowSection', () => {
});
});

describe('when 4 index fields are filled in and user clicks on covered queries button', () => {
const fields: Field[] = [
{ name: 'field1', type: '1 (asc)' },
{ name: 'field2', type: '-1 (desc)' },
{ name: 'field3', type: '1 (asc)' },
{ name: 'field4', type: '1 (asc)' },
];

beforeEach(() => {
renderComponent({ fields });
screen.getByTestId('index-flow-section-covered-queries-button').click();
});

it('renders the covered queries examples', () => {
const coveredQueriesExamples = screen.getByTestId(
'index-flow-section-covered-queries-examples'
);
expect(coveredQueriesExamples).to.exist;
expect(coveredQueriesExamples).to.contain.text(
JSON.stringify({
field1: 1,
field2: 2,
field3: 3,
field4: 4,
})
);
});

it('renders the optimal query examples', () => {
const optimalQueriesExamples = screen.getByTestId(
'index-flow-section-optimal-queries-examples'
);
expect(optimalQueriesExamples).to.exist;
expect(optimalQueriesExamples).to.contain.text(
`{"field1":1,"field2":2,"field4":{"$gt":3}}.sort({"field3": 1})`
);
});

it('renders the Covered Queries Learn More link', () => {
const link = screen.getByText('Learn about covered queries');
expect(link).to.be.visible;
});

it('renders the ESR Learn More link', () => {
const link = screen.getByText('Learn about ESR');
expect(link).to.be.visible;
});
});

describe('when 3 index fields are filled in and user clicks on covered queries button', () => {
const fields: Field[] = [
{ name: 'field1', type: '1 (asc)' },
Expand Down Expand Up @@ -107,12 +156,17 @@ describe('IndexFlowSection', () => {
);
expect(optimalQueriesExamples).to.exist;
expect(optimalQueriesExamples).to.contain.text(
`{"field1":1,"field2":{"$gt":2}}.sort(field3: 1})`
`{"field1":1,"field3":{"$gt":2}}.sort({"field2": 1})`
);
});

it('renders the Learn More link', () => {
const link = screen.getByText('Learn More');
it('renders the Covered Queries Learn More link', () => {
const link = screen.getByText('Learn about covered queries');
expect(link).to.be.visible;
});

it('renders the ESR Learn More link', () => {
const link = screen.getByText('Learn about ESR');
expect(link).to.be.visible;
});
});
Expand Down Expand Up @@ -141,7 +195,7 @@ describe('IndexFlowSection', () => {
);
expect(optimalQueriesExamples).to.exist;
expect(optimalQueriesExamples).to.contain.text(
`{"field1":1,"field2":{"$gt":2}}}`
`{"field1":1,"field2":{"$gt":2}}`
);
expect(optimalQueriesExamples).to.contain.text(
`{"field1":1}.sort({"field2":2})`
Expand All @@ -167,5 +221,9 @@ describe('IndexFlowSection', () => {
screen.queryByTestId('index-flow-section-optimal-queries-examples')
).not.to.exist;
});

it('does not render ESR Learn More link', () => {
expect(screen.queryByText('Learn about ESR')).not.to.exist;
});
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,14 @@ const coveredQueriesHeaderStyles = css({
marginRight: spacing[200],
});

const coveredQueriesLinkStyles = css({
marginTop: spacing[200],
});

const optimalQueriesStyles = css({
marginTop: spacing[400],
});

export type IndexFlowSectionProps = {
fields: Field[];
createIndexFieldsComponent: JSX.Element | null;
Expand Down Expand Up @@ -148,31 +156,35 @@ const generateOptimalQueries = (

return (
<>
{`{"${firstFieldKey}":1,"${lastFieldKey}":{"$gt":2}}}`}
{`{"${firstFieldKey}":1,"${lastFieldKey}":{"$gt":2}}`}
<br />
{`{"${firstFieldKey}":1}.sort({"${lastFieldKey}":2})`}
</>
);
}

// If there are more than two fields, we want to show a longer optimal query with gt and sort
// i.e. {a:1, b:2, c:{gt:3}}.sort({d:1})
// i.e. {a:1, b:2, d:{gt:3}}.sort({c:1})

const secondToLastField = coveredQueriesArr[numOfFields - 2];
const secondToLastFieldKey = Object.keys(secondToLastField)[0];

const optimalQueries = coveredQueriesArr
.slice(0, -1)
.reduce<Record<string, unknown>>((acc, obj, index) => {
.slice(0, -2)
.reduce<Record<string, unknown>>((acc, obj) => {
const key = Object.keys(obj)[0];
const value = obj[key];

if (index === numOfFields - 2) {
acc[key] = { $gt: value };
} else {
acc[key] = value;
}
acc[key] = value;

return acc;
}, {});

return JSON.stringify(optimalQueries) + `.sort(${lastFieldKey}: 1})`;
// Put last field in range and second to last field in sort
optimalQueries[lastFieldKey] = { $gt: coveredQueriesArr.length - 1 };
return (
JSON.stringify(optimalQueries) + `.sort({"${secondToLastFieldKey}": 1})`
);
};

const IndexFlowSection = ({
Expand Down Expand Up @@ -354,28 +366,15 @@ const IndexFlowSection = ({
)}
>
{/* Covered Queries */}
<Body
className={codeStyles}
data-testid="index-flow-section-covered-queries-examples"
>
{coveredQueries}
</Body>

{!!optimalQueries && (
<>
<p>
<span className={underlineStyles}>
Follow the Equality, Sort, Range (ESR) Rule. This index is
optimal for queries that have this pattern:
</span>
{/* Optimal queries */}
<Body
className={codeStyles}
data-testid="index-flow-section-optimal-queries-examples"
>
{optimalQueries}
</Body>
</p>
<div>
<Body
className={codeStyles}
data-testid="index-flow-section-covered-queries-examples"
>
{coveredQueries}
</Body>
<div className={coveredQueriesLinkStyles}>
<Link
href="https://www.mongodb.com/docs/manual/core/query-optimization/"
onClick={() => {
Expand All @@ -384,9 +383,37 @@ const IndexFlowSection = ({
});
}}
>
Learn More
Learn about covered queries
</Link>
</>
</div>
</div>

{!!optimalQueries && (
<div className={optimalQueriesStyles}>
<span className={underlineStyles}>
Follow the Equality, Sort, Range (ESR) Rule. This index is
great for queries that have this pattern:
</span>
{/* Optimal queries */}
<Body
className={codeStyles}
data-testid="index-flow-section-optimal-queries-examples"
>
{optimalQueries}
</Body>
<div className={coveredQueriesLinkStyles}>
<Link
href="https://www.mongodb.com/docs/manual/tutorial/equality-sort-range-guideline/"
onClick={() => {
track('ESR Learn More Clicked', {
context: 'Create Index Modal',
});
}}
>
Learn about ESR
</Link>
</div>
</div>
)}
</div>
</>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -160,7 +160,7 @@ const QueryFlowSection = ({
});

setHasNewChanges(false);
}, [inputQuery, dbName, collectionName, onSuggestedIndexButtonClick]);
}, [inputQuery, dbName, collectionName, onSuggestedIndexButtonClick, track]);

const handleQueryInputChange = useCallback((text: string) => {
setInputQuery(text);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ function CreateIndexModal({
});
}
},
[onCancelCreateIndexClick]
[onCancelCreateIndexClick, track]
);

useTrackOnChange(
Expand Down
8 changes: 8 additions & 0 deletions packages/compass-telemetry/src/telemetry-events.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2849,6 +2849,13 @@ type CreateIndexCoveredQueriesLearnMoreClicked = CommonEvent<{
};
}>;

type CreateIndexESRLearnMoreClicked = CommonEvent<{
name: 'ESR Learn More Clicked';
payload: {
context: CreateIndexModalContext;
};
}>;

type CreateIndexInputIndexCopied = CommonEvent<{
name: 'Input Index Copied';
payload: {
Expand Down Expand Up @@ -3003,6 +3010,7 @@ export type TelemetryEvent =
| CreateIndexCodeEquivalentToggled
| CreateIndexCoveredQueriesButtonClicked
| CreateIndexCoveredQueriesLearnMoreClicked
| CreateIndexESRLearnMoreClicked
| CreateIndexIndexTabClicked
| CreateIndexModalCancelled
| CreateIndexModalClosed
Expand Down
Loading