Skip to content

Commit fe7c781

Browse files
add version incompatible banner
1 parent acf5e6b commit fe7c781

File tree

3 files changed

+78
-15
lines changed

3 files changed

+78
-15
lines changed

packages/compass-indexes/src/components/indexes-toolbar/indexes-toolbar.tsx

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,8 @@ import { getAtlasSearchIndexesLink } from '../../utils/atlas-search-indexes-link
3131
import { createIndexOpened } from '../../modules/create-index';
3232
import type { IndexView } from '../../modules/index-view';
3333
import { indexViewChanged } from '../../modules/index-view';
34+
import { CollectionStats } from '../../modules/collection-stats';
35+
import { isPipelineSearchQueryable } from 'mongodb-compass/src/app/utils/view-search-queryable';
3436

3537
const toolbarButtonsContainer = css({
3638
display: 'flex',
@@ -88,6 +90,7 @@ type IndexesToolbarProps = {
8890
isSearchIndexesSupported: boolean;
8991
// via withPreferences:
9092
readOnly?: boolean;
93+
collectionStats: CollectionStats;
9194
};
9295

9396
export const IndexesToolbar: React.FunctionComponent<IndexesToolbarProps> = ({
@@ -107,6 +110,7 @@ export const IndexesToolbar: React.FunctionComponent<IndexesToolbarProps> = ({
107110
onIndexViewChanged,
108111
serverVersion,
109112
readOnly, // preferences readOnly.
113+
collectionStats,
110114
}) => {
111115
const isSearchManagementActive = usePreference('enableAtlasSearchIndexes');
112116
const { atlasMetadata } = useConnectionInfo();
@@ -122,7 +126,14 @@ export const IndexesToolbar: React.FunctionComponent<IndexesToolbarProps> = ({
122126
) : (
123127
<Icon glyph="Refresh" title="Refresh Indexes" />
124128
);
125-
129+
const isViewPipelineSearchQueryable =
130+
isReadonlyView &&
131+
collectionStats?.pipeline &&
132+
isPipelineSearchQueryable(
133+
collectionStats.pipeline as Array<Record<string, any>>
134+
);
135+
const pipelineNotSearchQueryableDescription =
136+
'Search indexes can only be created on views containing $addFields, $set or $match stages with the $expr operator.';
126137
return (
127138
<div
128139
className={indexesToolbarContainerStyles}
@@ -135,7 +146,7 @@ export const IndexesToolbar: React.FunctionComponent<IndexesToolbarProps> = ({
135146
<div className={toolbarButtonsContainer}>
136147
{showCreateIndexButton && (
137148
<Tooltip
138-
enabled={!isWritable}
149+
enabled={!isWritable || !isViewPipelineSearchQueryable}
139150
align="top"
140151
justify="middle"
141152
trigger={
@@ -148,11 +159,16 @@ export const IndexesToolbar: React.FunctionComponent<IndexesToolbarProps> = ({
148159
onCreateSearchIndexClick={onCreateSearchIndexClick}
149160
isReadonlyView={isReadonlyView}
150161
indexView={indexView}
162+
isViewPipelineSearchQueryable={
163+
isViewPipelineSearchQueryable
164+
}
151165
/>
152166
</div>
153167
}
154168
>
155-
{writeStateDescription}
169+
{(!isWritable && writeStateDescription) ||
170+
(!isViewPipelineSearchQueryable &&
171+
pipelineNotSearchQueryableDescription)}
156172
</Tooltip>
157173
)}
158174
<Button
@@ -286,6 +302,7 @@ type CreateIndexButtonProps = {
286302
onCreateSearchIndexClick: () => void;
287303
isReadonlyView: boolean;
288304
indexView: IndexView;
305+
isViewPipelineSearchQueryable: boolean;
289306
};
290307

291308
type CreateIndexActions = 'createRegularIndex' | 'createSearchIndex';
@@ -300,6 +317,7 @@ export const CreateIndexButton: React.FunctionComponent<
300317
onCreateSearchIndexClick,
301318
isReadonlyView,
302319
indexView,
320+
isViewPipelineSearchQueryable,
303321
}) => {
304322
const onActionDispatch = useCallback(
305323
(action: CreateIndexActions) => {
@@ -317,7 +335,7 @@ export const CreateIndexButton: React.FunctionComponent<
317335
if (indexView === 'search-indexes') {
318336
return (
319337
<Button
320-
disabled={!isWritable}
338+
disabled={!isWritable || !isViewPipelineSearchQueryable}
321339
onClick={onCreateSearchIndexClick}
322340
variant="primary"
323341
size="small"
@@ -371,6 +389,7 @@ const mapState = ({
371389
serverVersion,
372390
searchIndexes,
373391
indexView,
392+
collectionStats,
374393
}: RootState) => ({
375394
namespace,
376395
isWritable,
@@ -380,6 +399,7 @@ const mapState = ({
380399
indexView,
381400
serverVersion,
382401
searchIndexes,
402+
collectionStats,
383403
});
384404

385405
const mapDispatch = {

packages/compass-indexes/src/components/indexes/indexes.tsx

Lines changed: 51 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,9 @@ import CreateIndexModal from '../create-index-modal/create-index-modal';
3636
import { ZeroGraphic } from '../search-indexes-table/zero-graphic';
3737
import { ViewVersionIncompatibleBanner } from '../view-version-incompatible-banners/view-version-incompatible-banners';
3838
import semver from 'semver';
39+
import type { SearchIndex } from 'mongodb-data-service';
40+
import { isPipelineSearchQueryable } from 'mongodb-compass/src/app/utils/view-search-queryable';
41+
import type { CollectionStats } from '../../modules/collection-stats';
3942

4043
// This constant is used as a trigger to show an insight whenever number of
4144
// indexes in a collection is more than what is specified here.
@@ -100,6 +103,30 @@ const ViewVersionIncompatibleEmptyState = ({
100103
);
101104
};
102105

106+
const ViewNotSearchCompatibleBanner = ({
107+
searchIndexes,
108+
}: {
109+
searchIndexes: SearchIndex[];
110+
}) => {
111+
const hasNoSearchIndexes = searchIndexes.length === 0;
112+
const variant = hasNoSearchIndexes ? 'warning' : 'danger';
113+
return (
114+
<Banner variant={variant}>
115+
{hasNoSearchIndexes && (
116+
<>
117+
<b>Looking for search indexes?</b> <br />
118+
</>
119+
)}
120+
This view is incompatible with search indexes. Only views containing
121+
$addFields, $set or $match stages with the $expr operator are compatible
122+
with search indexes. Edit the view to rebuild search indexes.{' '}
123+
<Link href={''} hideExternalIcon>
124+
Learn more.
125+
</Link>
126+
</Banner>
127+
);
128+
};
129+
103130
const AtlasIndexesBanner = ({
104131
namespace,
105132
dismissed,
@@ -145,6 +172,7 @@ type IndexesProps = {
145172
refreshRegularIndexes: () => void;
146173
refreshSearchIndexes: () => void;
147174
serverVersion: string;
175+
collectionStats: CollectionStats;
148176
};
149177

150178
function isRefreshingStatus(status: FetchStatus) {
@@ -171,6 +199,7 @@ export function Indexes({
171199
refreshRegularIndexes,
172200
refreshSearchIndexes,
173201
serverVersion,
202+
collectionStats,
174203
}: IndexesProps) {
175204
const [atlasBannerDismissed, setDismissed] = usePersistedState(
176205
DISMISSED_SEARCH_INDEXES_BANNER_LOCAL_STORAGE_KEY,
@@ -198,6 +227,11 @@ export function Indexes({
198227

199228
const enableAtlasSearchIndexes = usePreference('enableAtlasSearchIndexes');
200229
const { atlasMetadata } = useConnectionInfo();
230+
const isViewPipelineSearchQueryable =
231+
collectionStats?.pipeline &&
232+
isPipelineSearchQueryable(
233+
collectionStats.pipeline as Array<Record<string, any>>
234+
);
201235

202236
return (
203237
<div className={containerStyles}>
@@ -224,16 +258,22 @@ export function Indexes({
224258
atlasMetadata={atlasMetadata}
225259
/>
226260
)}
227-
{(!isReadonlyView ||
228-
(isVersionSearchCompatibleForViewsDataExplorer(serverVersion) &&
229-
!enableAtlasSearchIndexes)) && (
230-
<AtlasIndexesBanner // cta to Atlas Search Indexes Page
231-
namespace={namespace}
232-
dismissed={atlasBannerDismissed}
233-
onDismissClick={() => {
234-
setDismissed(true);
235-
}}
261+
{isReadonlyView && !isViewPipelineSearchQueryable ? (
262+
<ViewNotSearchCompatibleBanner
263+
searchIndexes={searchIndexes.indexes}
236264
/>
265+
) : (
266+
(!isReadonlyView ||
267+
(isVersionSearchCompatibleForViewsDataExplorer(serverVersion) &&
268+
!enableAtlasSearchIndexes)) && (
269+
<AtlasIndexesBanner // cta to Atlas Search Indexes Page
270+
namespace={namespace}
271+
dismissed={atlasBannerDismissed}
272+
onDismissClick={() => {
273+
setDismissed(true);
274+
}}
275+
/>
276+
)
237277
)}
238278
{!isReadonlyView && currentIndexesView === 'regular-indexes' && (
239279
<RegularIndexesTable />
@@ -264,13 +304,15 @@ const mapState = ({
264304
searchIndexes,
265305
indexView,
266306
serverVersion,
307+
collectionStats,
267308
}: RootState) => ({
268309
namespace,
269310
isReadonlyView,
270311
regularIndexes,
271312
searchIndexes,
272313
currentIndexesView: indexView,
273314
serverVersion,
315+
collectionStats,
274316
});
275317

276318
const mapDispatch = {

packages/compass-indexes/src/modules/collection-stats.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,16 +11,17 @@ function isAction<A extends AnyAction>(
1111
export function extractCollectionStats(
1212
collection: Collection
1313
): CollectionStats {
14-
const { index_count, index_size } = collection.toJSON();
14+
const { index_count, index_size, pipeline } = collection.toJSON();
1515
return {
1616
index_count,
1717
index_size,
18+
pipeline,
1819
};
1920
}
2021

2122
export type CollectionStats = Pick<
2223
Collection,
23-
'index_count' | 'index_size'
24+
'index_count' | 'index_size' | 'pipeline'
2425
> | null;
2526

2627
enum StatsActions {

0 commit comments

Comments
 (0)