Skip to content

feat: Create actual indexes and fix up state + error logic CLOUDP-317945 #6933

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

Open
wants to merge 12 commits into
base: main
Choose a base branch
from
Open
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
2 changes: 1 addition & 1 deletion packages/compass-crud/src/stores/crud-store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1834,7 +1834,7 @@ class CrudStoreImpl

openCreateIndexModal() {
this.localAppRegistry.emit('open-create-index-modal', {
query: EJSON.serialize(this.queryBar.getLastAppliedQuery('crud')),
query: EJSON.serialize(this.queryBar.getLastAppliedQuery('crud')?.filter),
});
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
import React from 'react';
import { css, Banner, spacing, Button } from '@mongodb-js/compass-components';
import { connect } from 'react-redux';
import { areAllFieldsFilledIn } from '../../utils/create-index-modal-validation';
import type { Field, Tab } from '../../modules/create-index';
import type { RootState } from '../../modules';

const containerStyles = css({
display: 'flex',
Expand Down Expand Up @@ -27,12 +31,37 @@ function CreateIndexActions({
onErrorBannerCloseClick,
onCreateIndexClick,
onCancelCreateIndexClick,
fields,
currentTab,
showIndexesGuidanceVariant,
indexSuggestions,
}: {
error: string | null;
onErrorBannerCloseClick: () => void;
onCreateIndexClick: () => void;
onCancelCreateIndexClick: () => void;
fields: Field[];
currentTab: Tab;
showIndexesGuidanceVariant: boolean;
indexSuggestions: Record<string, number> | null;
}) {
let isCreateIndexButtonDisabled = false;

if (showIndexesGuidanceVariant) {
// Disable create index button if the user is in Query Flow and has no suggestions
if (currentTab === 'QueryFlow') {
if (indexSuggestions === null) {
isCreateIndexButtonDisabled = true;
}
}
// Or if they are in the Index Flow but have not completed the fields
else {
if (!areAllFieldsFilledIn(fields)) {
isCreateIndexButtonDisabled = true;
}
}
}

return (
<div className={containerStyles}>
{error && (
Expand Down Expand Up @@ -61,11 +90,21 @@ function CreateIndexActions({
onClick={onCreateIndexClick}
variant="primary"
className={createIndexButtonStyles}
disabled={isCreateIndexButtonDisabled}
>
Create Index
</Button>
</div>
);
}

export default CreateIndexActions;
const mapState = ({ createIndex }: RootState) => {
const { fields, currentTab, indexSuggestions } = createIndex;
return {
fields,
currentTab,
indexSuggestions,
};
};

export default connect(mapState)(CreateIndexActions);
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ import { usePreference } from 'compass-preferences-model/provider';
import IndexFlowSection from './index-flow-section';
import QueryFlowSection from './query-flow-section';
import toNS from 'mongodb-ns';
import type { Document } from 'bson';
import type { Document as BsonDocument } from 'bson';

const createIndexModalFieldsStyles = css({
margin: `${spacing[600]}px 0 ${spacing[800]}px 0`,
Expand Down Expand Up @@ -50,7 +50,7 @@ export type CreateIndexFormProps = {
onRemoveFieldClick: (idx: number) => void; // Minus icon.
onTabClick: (tab: Tab) => void;
showIndexesGuidanceVariant?: boolean;
query: Document | null;
query: BsonDocument | null;
};

function CreateIndexForm({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,17 @@ import {
InfoSprinkle,
Tooltip,
} from '@mongodb-js/compass-components';
import React, { useState, useCallback } from 'react';
import type { Field } from '../../modules/create-index';
import React, { useState, useCallback, useEffect } from 'react';
import {
errorCleared,
errorEncountered,
type Field,
} from '../../modules/create-index';
import MDBCodeViewer from './mdb-code-viewer';
import { areAllFieldsFilledIn } from '../../utils/create-index-modal-validation';
import { connect } from 'react-redux';
import type { TrackFunction } from '@mongodb-js/compass-telemetry/provider';
import { useTelemetry } from '@mongodb-js/compass-telemetry/provider';

const flexContainerStyles = css({
display: 'flex',
Expand Down Expand Up @@ -77,10 +85,13 @@ export type IndexFlowSectionProps = {
createIndexFieldsComponent: JSX.Element | null;
dbName: string;
collectionName: string;
onErrorEncountered: (error: string) => void;
onErrorCleared: () => void;
};

const generateCoveredQueries = (
coveredQueriesArr: Array<Record<string, number>>
coveredQueriesArr: Array<Record<string, number>>,
track: TrackFunction
) => {
const rows = [];
for (let i = 0; i < coveredQueriesArr.length; i++) {
Expand All @@ -92,6 +103,15 @@ const generateCoveredQueries = (
);
}

if (rows.length === 0) {
// TODO: remove this in CLOUDP-320224
track('Error generating covered queries', {
context: 'Create Index Modal',
});
throw new Error(
'Error generating covered query examples. Please try again later.'
);
}
return <>{rows}</>;
};

Expand Down Expand Up @@ -148,20 +168,22 @@ const IndexFlowSection = ({
fields,
dbName,
collectionName,
onErrorEncountered,
onErrorCleared,
}: IndexFlowSectionProps) => {
const [isCodeEquivalentToggleChecked, setIsCodeEquivalentToggleChecked] =
useState(false);

const areAllFieldsFilledIn = fields.every((field) => {
return field.name && field.type;
});
const [hasFieldChanges, setHasFieldChanges] = useState(false);

const hasUnsupportedQueryTypes = fields.some((field) => {
return field.type === '2dsphere' || field.type === 'text';
});
const track = useTelemetry();

const isCoveredQueriesButtonDisabled =
!areAllFieldsFilledIn || hasUnsupportedQueryTypes;
!areAllFieldsFilledIn(fields) ||
hasUnsupportedQueryTypes ||
!hasFieldChanges;

const indexNameTypeMap = fields.reduce<Record<string, string>>(
(accumulator, currentValue) => {
Expand All @@ -188,12 +210,23 @@ const IndexFlowSection = ({
return { [field.name]: index + 1 };
});

setCoveredQueriesObj({
coveredQueries: generateCoveredQueries(coveredQueriesArr),
optimalQueries: generateOptimalQueries(coveredQueriesArr),
showCoveredQueries: true,
});
}, [fields]);
try {
setCoveredQueriesObj({
coveredQueries: generateCoveredQueries(coveredQueriesArr, track),
optimalQueries: generateOptimalQueries(coveredQueriesArr),
showCoveredQueries: true,
});
} catch (e) {
onErrorEncountered(e instanceof Error ? e.message : String(e));
}

setHasFieldChanges(false);
}, [fields, onErrorEncountered, track]);

useEffect(() => {
setHasFieldChanges(true);
onErrorCleared();
}, [fields, onErrorCleared]);

const { coveredQueries, optimalQueries, showCoveredQueries } =
coveredQueriesObj;
Expand Down Expand Up @@ -315,4 +348,13 @@ const IndexFlowSection = ({
);
};

export default IndexFlowSection;
const mapState = () => {
return {};
};

const mapDispatch = {
onErrorEncountered: errorEncountered,
onErrorCleared: errorCleared,
};

export default connect(mapState, mapDispatch)(IndexFlowSection);
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ describe('QueryFlowSection', () => {
type: ActionTypes.SuggestedIndexesFetched,
sampleDocs: [],
indexSuggestions: { a: 1, b: 2 },
fetchingSuggestionsError: null,
error: null,
indexSuggestionsState: 'success',
});
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {
useFocusRing,
ParagraphSkeleton,
} from '@mongodb-js/compass-components';
import type { Document as BsonDocument } from 'bson';
import React, { useMemo, useCallback } from 'react';
import { css, spacing } from '@mongodb-js/compass-components';
import {
Expand All @@ -21,7 +22,7 @@ import type {
SuggestedIndexFetchedProps,
} from '../../modules/create-index';
import { connect } from 'react-redux';
import type { Document } from 'bson';
import { parseFilter } from 'mongodb-query-parser';

const inputQueryContainerStyles = css({
display: 'flex',
Expand Down Expand Up @@ -81,6 +82,8 @@ const insightStyles = css({
display: 'flex',
alignItems: 'center',
gap: spacing[100],
marginBottom: spacing[200],
height: spacing[500],
});

const QueryFlowSection = ({
Expand All @@ -104,11 +107,17 @@ const QueryFlowSection = ({
}: SuggestedIndexFetchedProps) => Promise<void>;
indexSuggestions: Record<string, number> | null;
fetchingSuggestionsState: IndexSuggestionState;
initialQuery: Document | null;
initialQuery: BsonDocument | null;
}) => {
const [inputQuery, setInputQuery] = React.useState(
JSON.stringify(initialQuery?.filter ?? {}, null, 2)
JSON.stringify(initialQuery ?? '', null, 2)
);
const [hasNewChanges, setHasNewChanges] = React.useState(
initialQuery !== null
);
const [isShowSuggestionsButtonDisabled, setIsShowSuggestionsButtonDisabled] =
React.useState(true);

const completer = useMemo(
() =>
createQueryAutocompleter({
Expand All @@ -133,10 +142,33 @@ const QueryFlowSection = ({
collectionName,
inputQuery: sanitizedInputQuery,
});

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

const handleQueryInputChange = useCallback((text: string) => {
setInputQuery(text);
setHasNewChanges(true);
}, []);

const isFetchingIndexSuggestions = fetchingSuggestionsState === 'fetching';

// Validate query upon typing
useMemo(() => {
let _isShowSuggestionsButtonDisabled = !hasNewChanges;
try {
parseFilter(inputQuery);

if (!inputQuery.startsWith('{') || !inputQuery.endsWith('}')) {
_isShowSuggestionsButtonDisabled = true;
}
} catch (e) {
_isShowSuggestionsButtonDisabled = true;
} finally {
setIsShowSuggestionsButtonDisabled(_isShowSuggestionsButtonDisabled);
}
}, [hasNewChanges, inputQuery]);

return (
<>
{initialQuery && (
Expand Down Expand Up @@ -164,7 +196,7 @@ const QueryFlowSection = ({
copyable={false}
formattable={false}
text={inputQuery}
onChangeText={(text) => setInputQuery(text)}
onChangeText={(text) => handleQueryInputChange(text)}
placeholder="Type a query: { field: 'value' }"
completer={completer}
className={codeEditorStyles}
Expand All @@ -176,6 +208,7 @@ const QueryFlowSection = ({
onClick={handleSuggestedIndexButtonClick}
className={suggestedIndexButtonStyles}
size="small"
disabled={isShowSuggestionsButtonDisabled}
>
Show suggested index
</Button>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,14 +29,14 @@ import {
import { useConnectionInfoRef } from '@mongodb-js/compass-connections/provider';
import { usePreference } from 'compass-preferences-model/provider';
import CreateIndexModalHeader from './create-index-modal-header';
import type { Document } from 'bson';
import type { Document as BsonDocument } from 'bson';

type CreateIndexModalProps = React.ComponentProps<typeof CreateIndexForm> & {
isVisible: boolean;
namespace: string;
error: string | null;
currentTab: Tab;
query: Document | null;
query: BsonDocument | null;
onErrorBannerCloseClick: () => void;
onCreateIndexClick: () => void;
onCancelCreateIndexClick: () => void;
Expand Down Expand Up @@ -121,6 +121,7 @@ function CreateIndexModal({
onErrorBannerCloseClick={onErrorBannerCloseClick}
onCreateIndexClick={onCreateIndexClick}
onCancelCreateIndexClick={onCancelCreateIndexClick}
showIndexesGuidanceVariant={showIndexesGuidanceVariant}
/>
</ModalFooter>
</Modal>
Expand Down
Loading
Loading