From e0a49a382f679bda7a998269d66978f5b3bbcbc6 Mon Sep 17 00:00:00 2001 From: Basit Date: Thu, 25 Aug 2022 15:52:46 +0200 Subject: [PATCH 1/5] feat(indexes): add sparse option --- .../create-index-form/create-index-form.tsx | 7 +++ .../create-index-form/sparse-index.tsx | 50 +++++++++++++++++++ .../create-index-modal/create-index-modal.tsx | 4 ++ .../src/modules/create-index/index.ts | 5 ++ .../src/modules/create-index/is-sparse.ts | 24 +++++++++ 5 files changed, 90 insertions(+) create mode 100644 packages/compass-indexes/src/components/create-index-form/sparse-index.tsx create mode 100644 packages/compass-indexes/src/modules/create-index/is-sparse.ts diff --git a/packages/compass-indexes/src/components/create-index-form/create-index-form.tsx b/packages/compass-indexes/src/components/create-index-form/create-index-form.tsx index 23bc22078fe..d6afd8d68cf 100644 --- a/packages/compass-indexes/src/components/create-index-form/create-index-form.tsx +++ b/packages/compass-indexes/src/components/create-index-form/create-index-form.tsx @@ -16,6 +16,7 @@ import CustomCollationCollapsibleFieldSet from './custom-collation'; import WildcardProjectionCollapsibleFieldSet from './wildcard-projection'; import ColumnstoreProjectionCollapsibleFieldSet from './columnstore-projection'; import IndexNameCollapsibleFieldSet from './index-name'; +import SparseIndexCheckbox from './sparse-index'; const createIndexModalFieldsStyles = css({ margin: `${spacing[4]}px 0 ${spacing[5]}px 0`, @@ -34,6 +35,7 @@ export type CreateIndexProps = { schemaFields: string[]; isUnique: boolean; + isSparse: boolean; useIndexName: boolean; useTtl: boolean; @@ -52,6 +54,7 @@ export type CreateIndexProps = { serverVersion: string; toggleIsUnique: (isUnique: boolean) => void; + toggleIsSparse: (isSparse: boolean) => void; toggleUseIndexName: (useIndexName: boolean) => void; toggleUseTtl: (useTtl: boolean) => void; @@ -172,6 +175,10 @@ class CreateIndexForm extends Component { } /> )} + ); } diff --git a/packages/compass-indexes/src/components/create-index-form/sparse-index.tsx b/packages/compass-indexes/src/components/create-index-form/sparse-index.tsx new file mode 100644 index 00000000000..33630b3b505 --- /dev/null +++ b/packages/compass-indexes/src/components/create-index-form/sparse-index.tsx @@ -0,0 +1,50 @@ +import React from 'react'; +import { + Checkbox, + Label, + Description, + css, + spacing, +} from '@mongodb-js/compass-components'; + +type SparseIndex = { + isSparse: boolean; + toggleIsSparse: (isSparse: boolean) => void; +}; + +const sparseIndexStyles = css({ + margin: `${spacing[3]}px 0`, + fieldset: { + paddingLeft: `${spacing[4]}px`, + }, +}); + +const SparseIndexCheckbox = ({ isSparse, toggleIsSparse }: SparseIndex) => { + const labelId = 'create-index-modal-is-sparse-checkbox'; + return ( +
+ { + toggleIsSparse(event.target.checked); + }} + label={} + // LG Checkbox expects a string description, but we use Description component + // to alight with styles from CollapsibleFieldSet that are used on the same form. + description={ + ( + + Sparse indexes only contain entries for documents that have the + indexed field, even if the index field contains a null value. The + index skips over any document that is missing the indexed field. + + ) as any + } + checked={isSparse} + id={labelId} + /> +
+ ); +}; + +export default SparseIndexCheckbox; diff --git a/packages/compass-indexes/src/components/create-index-modal/create-index-modal.tsx b/packages/compass-indexes/src/components/create-index-modal/create-index-modal.tsx index 5035ac206d9..af2c819001f 100644 --- a/packages/compass-indexes/src/components/create-index-modal/create-index-modal.tsx +++ b/packages/compass-indexes/src/components/create-index-modal/create-index-modal.tsx @@ -46,6 +46,7 @@ import { resetForm } from '../../modules/reset-form'; import CreateIndexForm from '../create-index-form'; import { toggleUseIndexName } from '../../modules/create-index/use-index-name'; import type { RootState } from '../../modules/create-index'; +import { toggleIsSparse } from '../../modules/create-index/is-sparse'; const { track } = createLoggerAndTelemetry('COMPASS-IMPORT-EXPORT-UI'); @@ -229,6 +230,7 @@ const mapState = ({ namespace, serverVersion, newIndexField, + isSparse, }: RootState) => ({ fields, inProgress, @@ -251,6 +253,7 @@ const mapState = ({ namespace, serverVersion, newIndexField, + isSparse, }); const mapDispatch = { @@ -281,5 +284,6 @@ const mapDispatch = { removeField, updateFieldName, updateFieldType, + toggleIsSparse, }; export default connect(mapState, mapDispatch)(CreateIndexModal); diff --git a/packages/compass-indexes/src/modules/create-index/index.ts b/packages/compass-indexes/src/modules/create-index/index.ts index fde3a429831..1038717268c 100644 --- a/packages/compass-indexes/src/modules/create-index/index.ts +++ b/packages/compass-indexes/src/modules/create-index/index.ts @@ -66,6 +66,9 @@ import name, { } from '../create-index/name'; import namespace from '../namespace'; import serverVersion from '../server-version'; +import isSparse, { + INITIAL_STATE as IS_SPARSE_INITIAL_STATE, +} from './is-sparse'; import schemaFields from '../create-index/schema-fields'; import newIndexField from '../create-index/new-index-field'; @@ -104,6 +107,7 @@ const reducer = combineReducers({ name, namespace, serverVersion, + isSparse, }); export type RootState = ReturnType; @@ -137,6 +141,7 @@ const rootReducer = (state: RootState, action: AnyAction): RootState => { wildcardProjection: WILDCARD_PROJECTION_INITIAL_STATE, partialFilterExpression: PARTIAL_FILTER_EXPRESSION_INITIAL_STATE, name: NAME_INITIAL_STATE, + isSparse: IS_SPARSE_INITIAL_STATE, }; } return reducer(state, action); diff --git a/packages/compass-indexes/src/modules/create-index/is-sparse.ts b/packages/compass-indexes/src/modules/create-index/is-sparse.ts new file mode 100644 index 00000000000..6c021753821 --- /dev/null +++ b/packages/compass-indexes/src/modules/create-index/is-sparse.ts @@ -0,0 +1,24 @@ +enum ActionTypes { + ToggleIsSparse = 'indexes/create-indexes/is-sparse/ToggleIsSparse', +}; + +type ToggleIsSparseAction = { + type: ActionTypes.ToggleIsSparse; + isSparse: boolean; +} + +type State = boolean; + +export const INITIAL_STATE: State = false; + +export default function reducer(state: State = INITIAL_STATE, action: ToggleIsSparseAction) { + if (action.type === ActionTypes.ToggleIsSparse) { + return action.isSparse; + } + return state; +} + +export const toggleIsSparse = (isSparse: boolean): ToggleIsSparseAction => ({ + type: ActionTypes.ToggleIsSparse, + isSparse, +}); From d44ce2c8cd4f34ee81f8a2b9618dbc5b026782ee Mon Sep 17 00:00:00 2001 From: Basit Date: Thu, 25 Aug 2022 17:52:43 +0200 Subject: [PATCH 2/5] test --- .../create-index-form/create-index-form.spec.jsx | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/packages/compass-indexes/src/components/create-index-form/create-index-form.spec.jsx b/packages/compass-indexes/src/components/create-index-form/create-index-form.spec.jsx index 41a5591cb8c..55ffdd3959f 100644 --- a/packages/compass-indexes/src/components/create-index-form/create-index-form.spec.jsx +++ b/packages/compass-indexes/src/components/create-index-form/create-index-form.spec.jsx @@ -28,6 +28,7 @@ describe('CreateIndexForm Component', function () { let wildcardProjectionChangedSpy; let createNewIndexFieldSpy; let toggleUseIndexNameSpy; + let toggleIsSparseSpy; const spyComponentProps = () => { updateFiedTypeSpy = sinon.spy(); @@ -49,6 +50,7 @@ describe('CreateIndexForm Component', function () { wildcardProjectionChangedSpy = sinon.spy(); createNewIndexFieldSpy = sinon.spy(); toggleUseIndexNameSpy = sinon.spy(); + toggleIsSparseSpy = sinon.spy(); }; const resetSpyComponentProps = () => { @@ -71,6 +73,7 @@ describe('CreateIndexForm Component', function () { wildcardProjectionChangedSpy = null; createNewIndexFieldSpy = null; toggleUseIndexNameSpy = null; + toggleIsSparseSpy = null; }; before(function () { @@ -162,6 +165,17 @@ describe('CreateIndexForm Component', function () { }); }); + context('sparse', function () { + it('calls the toggleIsSparse function', function () { + const checkbox = screen.getByTestId( + 'create-index-modal-is-sparse-checkbox' + ); + expect(toggleIsSparseSpy).to.have.been.calledWith(false); + fireEvent.click(checkbox); + expect(toggleIsSparseSpy).to.have.been.calledWith(true); + }); + }); + context('index name', function () { it('calls the toggleUseIndexName functions', function () { const checkbox = screen.getByTestId( From eac32091b5495495471a51a2d377e66008f4f432 Mon Sep 17 00:00:00 2001 From: Basit Date: Fri, 26 Aug 2022 13:11:19 +0200 Subject: [PATCH 3/5] sparse index --- packages/compass-indexes/src/modules/create-index/index.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/compass-indexes/src/modules/create-index/index.ts b/packages/compass-indexes/src/modules/create-index/index.ts index 5cc806ed21c..cdb9ad5c001 100644 --- a/packages/compass-indexes/src/modules/create-index/index.ts +++ b/packages/compass-indexes/src/modules/create-index/index.ts @@ -187,6 +187,7 @@ export const createIndex = () => { const options: CreateIndexesOptions = {}; options.unique = state.isUnique; + options.sparse = state.isSparse; // The server will generate a name when we don't provide one. if (state.name !== '') { options.name = state.name; From 391a33f0c437335ce387f0043a81d9a1494f648c Mon Sep 17 00:00:00 2001 From: Basit Date: Fri, 26 Aug 2022 13:53:31 +0200 Subject: [PATCH 4/5] reformat --- .../src/modules/create-index/is-sparse.ts | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/packages/compass-indexes/src/modules/create-index/is-sparse.ts b/packages/compass-indexes/src/modules/create-index/is-sparse.ts index 6c021753821..691cf533dfe 100644 --- a/packages/compass-indexes/src/modules/create-index/is-sparse.ts +++ b/packages/compass-indexes/src/modules/create-index/is-sparse.ts @@ -1,17 +1,20 @@ enum ActionTypes { ToggleIsSparse = 'indexes/create-indexes/is-sparse/ToggleIsSparse', -}; +} type ToggleIsSparseAction = { type: ActionTypes.ToggleIsSparse; isSparse: boolean; -} +}; type State = boolean; export const INITIAL_STATE: State = false; -export default function reducer(state: State = INITIAL_STATE, action: ToggleIsSparseAction) { +export default function reducer( + state: State = INITIAL_STATE, + action: ToggleIsSparseAction +) { if (action.type === ActionTypes.ToggleIsSparse) { return action.isSparse; } From 2a862557be4097db0447bbbc26f86f44efd48970 Mon Sep 17 00:00:00 2001 From: Basit Date: Fri, 26 Aug 2022 16:43:14 +0200 Subject: [PATCH 5/5] fix tests --- .../components/create-index-form/create-index-form.spec.jsx | 4 +++- .../compass-indexes/src/modules/create-index/index.spec.js | 6 ++++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/packages/compass-indexes/src/components/create-index-form/create-index-form.spec.jsx b/packages/compass-indexes/src/components/create-index-form/create-index-form.spec.jsx index 55ffdd3959f..a61960b6dfe 100644 --- a/packages/compass-indexes/src/components/create-index-form/create-index-form.spec.jsx +++ b/packages/compass-indexes/src/components/create-index-form/create-index-form.spec.jsx @@ -91,6 +91,7 @@ describe('CreateIndexForm Component', function () { schemaFields={[]} fields={[{ name: '', type: '' }]} isUnique={false} + isSparse={false} useTtl={false} ttl="" usePartialFilterExpression={false} @@ -125,6 +126,7 @@ describe('CreateIndexForm Component', function () { wildcardProjectionChanged={wildcardProjectionChangedSpy} createNewIndexField={createNewIndexFieldSpy} toggleUseIndexName={toggleUseIndexNameSpy} + toggleIsSparse={toggleIsSparseSpy} /> ); }); @@ -170,7 +172,7 @@ describe('CreateIndexForm Component', function () { const checkbox = screen.getByTestId( 'create-index-modal-is-sparse-checkbox' ); - expect(toggleIsSparseSpy).to.have.been.calledWith(false); + expect(toggleIsSparseSpy.callCount).to.equal(0); fireEvent.click(checkbox); expect(toggleIsSparseSpy).to.have.been.calledWith(true); }); diff --git a/packages/compass-indexes/src/modules/create-index/index.spec.js b/packages/compass-indexes/src/modules/create-index/index.spec.js index ad49af76dea..2d4d70c89e3 100644 --- a/packages/compass-indexes/src/modules/create-index/index.spec.js +++ b/packages/compass-indexes/src/modules/create-index/index.spec.js @@ -109,6 +109,7 @@ describe('create index module', function () { usePartialFilterExpression: true, partialFilterExpression: '{"a": 1}', isUnique: true, + isSparse: true, name: 'test name', useCustomCollation: true, collationString: "{locale: 'en'}", @@ -130,6 +131,7 @@ describe('create index module', function () { name: 'test name', partialFilterExpression: { a: 1 }, unique: true, + sparse: true, }); cb(null); }, @@ -177,6 +179,7 @@ describe('create index module', function () { usePartialFilterExpression: true, partialFilterExpression: '{"a": 1}', isUnique: true, + isSparse: false, name: '', useCustomCollation: true, collationString: "{locale: 'en'}", @@ -197,6 +200,7 @@ describe('create index module', function () { expireAfterSeconds: 100, partialFilterExpression: { a: 1 }, unique: true, + sparse: false, }); cb(null); }, @@ -240,6 +244,7 @@ describe('create index module', function () { usePartialFilterExpression: false, useTtl: false, isUnique: false, + isSparse: false, name: 'test name', namespace: 'db.coll', appRegistry: {}, @@ -250,6 +255,7 @@ describe('create index module', function () { expect(options).to.deep.equal({ name: 'test name', unique: false, + sparse: false, }); cb({ message: 'test err' }); },