Skip to content

Commit

Permalink
feat(search-indexes): fields autocomplete COMPASS-7174 (#4927)
Browse files Browse the repository at this point in the history
  • Loading branch information
mabaasit authored Oct 2, 2023
1 parent fa92670 commit 6351f72
Show file tree
Hide file tree
Showing 11 changed files with 159 additions and 3 deletions.
4 changes: 4 additions & 0 deletions packages/compass-components/src/components/modals/modal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,10 @@ const contentStyles = css({
width: '600px',
letterSpacing: 0,
padding: 0,
// The LG modal applies transform: translate3d(0, 0, 0) style to the modal
// content and this messes up the autocompleter within the modal. So we clear
// the transform here.
transform: 'none',
});

const modalFullScreenStyles = css({
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import { expect } from 'chai';
import { createSearchIndexAutocompleter } from './search-index-autocompleter';
import { setupCodemirrorCompleter } from '../../test/completer';

describe('search-index autocompleter', function () {
const { getCompletions, cleanup } = setupCodemirrorCompleter(
createSearchIndexAutocompleter
);

after(cleanup);

it('returns words in context when its not completing fields', function () {
const completions = getCompletions('{ dynamic: true, type: "dy', {
fields: ['_id', 'name', 'age'],
});
expect(completions.map((x) => x.label)).to.deep.equal([
'dynamic',
'true',
'type',
]);
});

it('returns field names when autocompleting fields', function () {
const completions = getCompletions('{ fields: { "a', {
fields: ['_id', 'name', 'age'],
});
expect(completions.map((x) => x.label)).to.deep.equal([
'_id',
'name',
'age',
]);
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import type { CompletionSource } from '@codemirror/autocomplete';
import type { CompletionOptions } from '../autocompleter';
import { completer } from '../autocompleter';
import {
ID_REGEX,
createCompletionResultForIdPrefix,
} from './ace-compat-autocompleter';
import {
completeWordsInString,
getAncestryOfToken,
resolveTokenAtCursor,
} from './utils';

const isCompletingFields = (ancestors: string[]) => {
return ancestors[ancestors.length - 1] === 'fields';
};

export const createSearchIndexAutocompleter = (
options: Pick<CompletionOptions, 'fields'> = {}
): CompletionSource => {
const completions = completer('', {
meta: ['field:identifier'],
...options,
});

return (context) => {
const token = resolveTokenAtCursor(context);
const document = context.state.sliceDoc(0);
const prefix = context.matchBefore(ID_REGEX);
if (!prefix) {
return null;
}

const ancestors = getAncestryOfToken(token, document);

if (isCompletingFields(ancestors)) {
return createCompletionResultForIdPrefix({
prefix,
completions,
});
}

return completeWordsInString(context);
};
};
1 change: 1 addition & 0 deletions packages/compass-editor/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,3 +20,4 @@ export { createValidationAutocompleter } from './codemirror/validation-autocompl
export { createQueryAutocompleter } from './codemirror/query-autocompleter';
export { createStageAutocompleter } from './codemirror/stage-autocompleter';
export { createAggregationAutocompleter } from './codemirror/aggregation-autocompleter';
export { createSearchIndexAutocompleter } from './codemirror/search-index-autocompleter';
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ describe('Create Search Index Modal', function () {
onSubmit={onSubmitSpy}
onClose={onCloseSpy}
error={'Invalid index definition.'}
fields={[]}
/>
);
});
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,10 @@
import React, { useCallback, useEffect, useRef, useState } from 'react';
import React, {
useCallback,
useEffect,
useRef,
useState,
useMemo,
} from 'react';
import {
Modal,
ModalFooter,
Expand All @@ -18,13 +24,17 @@ import {
Banner,
rafraf,
} from '@mongodb-js/compass-components';
import { CodemirrorMultilineEditor } from '@mongodb-js/compass-editor';
import {
CodemirrorMultilineEditor,
createSearchIndexAutocompleter,
} from '@mongodb-js/compass-editor';
import type { EditorRef } from '@mongodb-js/compass-editor';
import _parseShellBSON, { ParseMode } from 'ejson-shell-parser';
import type { Document } from 'mongodb';
import { useTrackOnChange } from '@mongodb-js/compass-logging';
import { SearchIndexTemplateDropdown } from '../search-index-template-dropdown';
import type { SearchTemplate } from '@mongodb-js/mongodb-constants';
import type { Field } from '../../modules/fields';

// Copied from packages/compass-aggregations/src/modules/pipeline-builder/pipeline-parser/utils.ts
function parseShellBSON(source: string): Document[] {
Expand Down Expand Up @@ -82,6 +92,7 @@ type BaseSearchIndexModalProps = {
isModalOpen: boolean;
isBusy: boolean;
error: string | undefined;
fields: Field[];
onSubmit: (indexName: string, indexDefinition: Document) => void;
onClose: () => void;
};
Expand All @@ -95,6 +106,7 @@ export const BaseSearchIndexModal: React.FunctionComponent<
isModalOpen,
isBusy,
error,
fields,
onSubmit,
onClose,
}) => {
Expand Down Expand Up @@ -171,6 +183,14 @@ export const BaseSearchIndexModal: React.FunctionComponent<
[editorRef]
);

const completer = useMemo(
() =>
createSearchIndexAutocompleter({
fields,
}),
[fields]
);

return (
<Modal
open={isModalOpen}
Expand Down Expand Up @@ -248,6 +268,7 @@ export const BaseSearchIndexModal: React.FunctionComponent<
text={indexDefinition}
onChangeText={onSearchIndexDefinitionChanged}
minLines={16}
completer={completer}
/>
</div>
{parsingError && <WarningSummary warnings={parsingError} />}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { connect } from 'react-redux';
import type { RootState } from '../../modules';
import type { Document } from 'mongodb';
import { BaseSearchIndexModal } from './base-search-index-modal';
import type { Field } from '../../modules/fields';

export const DEFAULT_INDEX_DEFINITION = `{
mappings: {
Expand All @@ -15,13 +16,14 @@ type CreateSearchIndexModalProps = {
isModalOpen: boolean;
isBusy: boolean;
error: string | undefined;
fields: Field[];
onCreateIndex: (indexName: string, indexDefinition: Document) => void;
onCloseModal: () => void;
};

export const CreateSearchIndexModal: React.FunctionComponent<
CreateSearchIndexModalProps
> = ({ isModalOpen, isBusy, error, onCreateIndex, onCloseModal }) => {
> = ({ isModalOpen, isBusy, error, fields, onCreateIndex, onCloseModal }) => {
return (
<BaseSearchIndexModal
mode={'create'}
Expand All @@ -30,6 +32,7 @@ export const CreateSearchIndexModal: React.FunctionComponent<
isModalOpen={isModalOpen}
isBusy={isBusy}
error={error}
fields={fields}
onSubmit={onCreateIndex}
onClose={onCloseModal}
/>
Expand All @@ -40,10 +43,12 @@ const mapState = ({
searchIndexes: {
createIndex: { isBusy, isModalOpen, error },
},
fields,
}: RootState) => ({
isModalOpen,
isBusy,
error,
fields,
});

const mapDispatch = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,15 @@ import { connect } from 'react-redux';
import type { RootState } from '../../modules';
import type { Document } from 'mongodb';
import { BaseSearchIndexModal } from './base-search-index-modal';
import type { Field } from '../../modules/fields';

type UpdateSearchIndexModalProps = {
indexName: string;
indexDefinition: string;
isModalOpen: boolean;
isBusy: boolean;
error: string | undefined;
fields: Field[];
onUpdateIndex: (indexName: string, indexDefinition: Document) => void;
onCloseModal: () => void;
};
Expand All @@ -23,6 +25,7 @@ export const UpdateSearchIndexModal: React.FunctionComponent<
isModalOpen,
isBusy,
error,
fields,
onUpdateIndex,
onCloseModal,
}) => {
Expand All @@ -34,6 +37,7 @@ export const UpdateSearchIndexModal: React.FunctionComponent<
isModalOpen={isModalOpen}
isBusy={isBusy}
error={error}
fields={fields}
onSubmit={onUpdateIndex}
onClose={onCloseModal}
/>
Expand All @@ -45,6 +49,7 @@ const mapState = ({
indexes,
updateIndex: { indexName, isBusy, isModalOpen, error },
},
fields,
}: RootState) => ({
isModalOpen,
isBusy,
Expand All @@ -55,6 +60,7 @@ const mapState = ({
2
),
error,
fields,
});

const mapDispatch = {
Expand Down
32 changes: 32 additions & 0 deletions packages/compass-indexes/src/modules/fields.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import type { AnyAction } from 'redux';
import { isAction } from './../utils/is-action';

export enum ActionTypes {
SetFields = 'indexes/SetFields',
}

export type Field = {
name: string;
description: string;
};

type SetFieldsAction = {
type: ActionTypes.SetFields;
fields: Field[];
};

type State = Field[];

export const INITIAL_STATE: State = [];

export default function reducer(state = INITIAL_STATE, action: AnyAction) {
if (isAction<SetFieldsAction>(action, ActionTypes.SetFields)) {
return action.fields;
}
return state;
}

export const setFields = (fields: Field[]): SetFieldsAction => ({
type: ActionTypes.SetFields,
fields,
});
2 changes: 2 additions & 0 deletions packages/compass-indexes/src/modules/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import regularIndexes from './regular-indexes';
import searchIndexes from './search-indexes';
import serverVersion from './server-version';
import namespace from './namespace';
import fields from './fields';
import type { ThunkAction, ThunkDispatch } from 'redux-thunk';

const reducer = combineReducers({
Expand All @@ -22,6 +23,7 @@ const reducer = combineReducers({
namespace,
regularIndexes,
searchIndexes,
fields,
});

export type SortDirection = 'asc' | 'desc';
Expand Down
6 changes: 6 additions & 0 deletions packages/compass-indexes/src/stores/store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import {
} from '../modules/search-indexes';
import type { DataService } from 'mongodb-data-service';
import type AppRegistry from 'hadron-app-registry';
import { setFields } from '../modules/fields';
import { switchToRegularIndexes } from '../modules/index-view';

export type IndexesDataService = Pick<
Expand Down Expand Up @@ -62,6 +63,7 @@ const configureStore = (options: ConfigureStoreOptions) => {
namespace: options.namespace,
serverVersion: options.serverVersion,
isReadonlyView: options.isReadonly,
fields: [],
indexView: INDEX_LIST_INITIAL_STATE,
searchIndexes: {
...SEARCH_INDEXES_INITIAL_STATE,
Expand Down Expand Up @@ -105,6 +107,10 @@ const configureStore = (options: ConfigureStoreOptions) => {
store.dispatch(inProgressIndexFailed(data));
}
);

localAppRegistry.on('fields-changed', (fields) => {
store.dispatch(setFields(fields.autocompleteFields));
});
}

if (options.globalAppRegistry) {
Expand Down

0 comments on commit 6351f72

Please sign in to comment.