@@ -8,30 +8,15 @@ import {
88import type { PipelineBuilderThunkAction } from '.' ;
99import { isAction } from '../utils/is-action' ;
1010import type { AnyAction } from 'redux' ;
11+ import { showConfirmation } from '@mongodb-js/compass-components' ;
12+ import { fetchIndexes } from './search-indexes' ;
1113
12- export type UpdateViewState = {
13- isOpen : boolean ;
14- updateViewError : null | string ;
15- } ;
16-
17- export const INITIAL_STATE : UpdateViewState = {
18- isOpen : false ,
19- updateViewError : null ,
20- } ;
21-
22- // Action for opening model.
23- export const OPEN_CONFIRM_UPDATE_MODEL =
24- 'aggregations/update-view/OPEN_CONFIRM_UPDATE_MODEL' as const ;
25- interface OpenConfirmUpdateModel {
26- type : typeof OPEN_CONFIRM_UPDATE_MODEL ;
27- }
14+ export type UpdateViewState = null | string ;
2815
29- // Action for closing model.
30- export const CLOSE_CONFIRM_UPDATE_MODEL =
31- 'aggregations/update-view/CLOSE_CONFIRM_UPDATE_MODEL' as const ;
32- interface CloseConfirmUpdateModel {
33- type : typeof CLOSE_CONFIRM_UPDATE_MODEL ;
34- }
16+ /**
17+ * State `null` when there is no error, or string if there's an error.
18+ */
19+ export const INITIAL_STATE : UpdateViewState = null ;
3520
3621// Action for when an error occurs when updating a view.
3722export const ERROR_UPDATING_VIEW =
@@ -49,34 +34,15 @@ interface DismissViewUpdateErrorAction {
4934}
5035
5136export type UpdateViewAction =
52- | OpenConfirmUpdateModel
53- | CloseConfirmUpdateModel
5437 | ErrorUpdatingViewAction
5538 | DismissViewUpdateErrorAction ;
5639
5740export default function reducer (
5841 state : UpdateViewState = INITIAL_STATE ,
5942 action : AnyAction
6043) : UpdateViewState {
61- if ( isAction < OpenConfirmUpdateModel > ( action , OPEN_CONFIRM_UPDATE_MODEL ) ) {
62- return {
63- ...state ,
64- isOpen : true ,
65- } ;
66- }
67-
68- if ( isAction < CloseConfirmUpdateModel > ( action , CLOSE_CONFIRM_UPDATE_MODEL ) ) {
69- return {
70- ...state ,
71- isOpen : false ,
72- } ;
73- }
74-
7544 if ( isAction < ErrorUpdatingViewAction > ( action , ERROR_UPDATING_VIEW ) ) {
76- return {
77- ...state ,
78- updateViewError : action . error ,
79- } ;
45+ return action . error ;
8046 }
8147 if (
8248 isAction < DismissViewUpdateErrorAction > ( action , DISMISS_VIEW_UPDATE_ERROR ) ||
@@ -90,23 +56,6 @@ export default function reducer(
9056 return state ;
9157}
9258
93- /**
94- * Action creator for opening the confirmation modal.
95- *
96- * @returns {Object } The action to open the modal.
97- */
98- export const openConfirmUpdateModal = ( ) : OpenConfirmUpdateModel => ( {
99- type : OPEN_CONFIRM_UPDATE_MODEL ,
100- } ) ;
101-
102- /**
103- * Action creator for closing the confirmation modal.
104- *
105- * @returns {Object } The action to close the modal.
106- */
107- export const closeConfirmUpdateModal = ( ) : CloseConfirmUpdateModel => ( {
108- type : CLOSE_CONFIRM_UPDATE_MODEL ,
109- } ) ;
11059/**
11160 * Action creator for showing the error that occured with updating the view.
11261 */
@@ -126,6 +75,37 @@ export const dismissViewError = (): DismissViewUpdateErrorAction => ({
12675 type : DISMISS_VIEW_UPDATE_ERROR ,
12776} ) ;
12877
78+ const isPipelineSearchQueryable = (
79+ pipeline : Array < Record < string , any > >
80+ ) : boolean => {
81+ for ( const stage of pipeline ) {
82+ const stageKey = Object . keys ( stage ) [ 0 ] ;
83+
84+ // Check if the stage is $addFields, $set, or $match
85+ if (
86+ ! (
87+ stageKey === '$addFields' ||
88+ stageKey === '$set' ||
89+ stageKey === '$match'
90+ )
91+ ) {
92+ return false ; // Not searchable
93+ }
94+
95+ // If the stage is $match, check if uses $expr
96+ if ( stageKey === '$match' ) {
97+ const matchStage = stage [ '$match' ] ;
98+ const allKeys = Object . keys ( matchStage ) ;
99+
100+ if ( ! ( allKeys . length === 1 && allKeys . includes ( '$expr' ) ) ) {
101+ return false ; // Not searchable
102+ }
103+ }
104+ }
105+
106+ return true ;
107+ } ;
108+
129109/**
130110 * Updates a view.
131111 *
@@ -145,7 +125,6 @@ export const updateView = (): PipelineBuilderThunkAction<Promise<void>> => {
145125 }
146126 ) => {
147127 dispatch ( dismissViewError ( ) ) ;
148- dispatch ( closeConfirmUpdateModal ( ) ) ;
149128
150129 const state = getState ( ) ;
151130 const ds = state . dataService . dataService ;
@@ -161,6 +140,24 @@ export const updateView = (): PipelineBuilderThunkAction<Promise<void>> => {
161140 getState ( ) ,
162141 pipelineBuilder
163142 ) ;
143+
144+ await dispatch ( fetchIndexes ( ) ) ;
145+ if ( state . searchIndexes . indexes . length > 0 ) {
146+ const pipelineIsSearchQueryable = isPipelineSearchQueryable ( viewPipeline ) ;
147+ const confirmed = await showConfirmation ( {
148+ title : `Are you sure you want to update the view?` ,
149+ description : pipelineIsSearchQueryable
150+ ? 'There are search indexes created on this view. Updating the view will result in an index rebuild, which will consume additional resources on your cluster.'
151+ : 'This update will make the view incompatible with search indexes and will cause all search indexes to fail. Only views containing $addFields, $set or $match stages with the $expr operator are compatible with search indexes.' ,
152+ buttonText : 'Update' ,
153+ variant : pipelineIsSearchQueryable ? 'primary' : 'danger' ,
154+ } ) ;
155+
156+ if ( ! confirmed ) {
157+ return ;
158+ }
159+ }
160+
164161 const options = {
165162 viewOn : toNS ( state . namespace ) . collection ,
166163 pipeline : viewPipeline ,
0 commit comments