| // Licensed under the Apache License, Version 2.0 (the "License"); you may not |
| // use this file except in compliance with the License. You may obtain a copy of |
| // the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT |
| // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the |
| // License for the specific language governing permissions and limitations under |
| // the License. |
| |
| import FauxtonAPI from '../../core/api'; |
| import ActionTypes from './actiontypes'; |
| import Search from './resources'; |
| import SidebarActions from '../documents/sidebar/actions'; |
| import IndexEditorActions from '../documents/index-editor/actions'; |
| import * as API from './api'; |
| |
| const dispatchInitNewSearchIndex = (params) => { |
| // ensure we start with a clear slate |
| FauxtonAPI.reduxDispatch({ type: ActionTypes.SEARCH_INDEX_CLEAR }); |
| FauxtonAPI.reduxDispatch({ |
| type: ActionTypes.SEARCH_INDEX_SET_LOADING, |
| options: { |
| loading: true |
| } |
| }); |
| |
| params.designDocs.fetch().then(() => { |
| FauxtonAPI.reduxDispatch({ |
| type: ActionTypes.SEARCH_INDEX_DESIGN_DOCS_LOADED, |
| options: { |
| designDocs: params.designDocs, |
| defaultDDoc: params.defaultDDoc, |
| database: params.database |
| } |
| }); |
| }); |
| }; |
| |
| const dispatchInitSearchIndex = (params) => { |
| FauxtonAPI.reduxDispatch({ |
| type: ActionTypes.SEARCH_INDEX_SET_LOADING, |
| options: { |
| loading: true |
| } |
| }); |
| |
| FauxtonAPI.reduxDispatch({ |
| type: ActionTypes.SEARCH_INDEX_INIT, |
| options: { |
| databaseName: params.databaseName, |
| partitionKey: params.partitionKey, |
| ddocName: params.designDoc, |
| indexName: params.indexName, |
| searchQuery: params.query ? params.query : '' |
| } |
| }); |
| |
| if (params.query) { |
| return executSearchQuery(params.databaseName, params.partitionKey, params.designDoc, |
| params.indexName, params.query, FauxtonAPI.reduxDispatch); |
| } |
| }; |
| |
| const dispatchEditSearchIndex = (params) => { |
| const {database, ddocID, designDocs, indexName} = params; |
| FauxtonAPI.reduxDispatch({ |
| type: ActionTypes.SEARCH_INDEX_SET_LOADING, |
| options: { |
| loading: true |
| } |
| }); |
| |
| designDocs.fetch().then(ddocs => { |
| const ddoc = ddocs.rows.find(ddoc => ddoc._id === ddocID).doc; |
| if (!ddoc.indexes || !ddoc.indexes[indexName]) { |
| throw Error(`Index "${indexName}" not found`); |
| } |
| FauxtonAPI.reduxDispatch({ |
| type: ActionTypes.SEARCH_INDEX_INIT_EDIT_SEARCH_INDEX, |
| options: { |
| indexName, |
| database, |
| ddocID, |
| designDocs, |
| } |
| }); |
| }).catch(err => { |
| const details = err.message ? err.message : ''; |
| FauxtonAPI.addNotification({ |
| msg: `There was a problem editing the search index "${indexName}". ` + details, |
| type: 'error', |
| clear: true |
| }); |
| }); |
| }; |
| |
| const selectTab = (tab) => (dispatch) => { |
| dispatch({ |
| type: ActionTypes.SEARCH_INDEX_SELECT_TAB, |
| options: { |
| tab: tab |
| } |
| }); |
| }; |
| |
| const setSearchIndexName = (str) => (dispatch) => { |
| dispatch({ |
| type: ActionTypes.SEARCH_INDEX_SET_NAME, |
| options: { |
| value: str |
| } |
| }); |
| }; |
| |
| const setAnalyzerType = (type) => (dispatch) => { |
| dispatch({ |
| type: ActionTypes.SEARCH_INDEX_SET_ANALYZER_TYPE, |
| options: { |
| value: type |
| } |
| }); |
| }; |
| |
| const addAnalyzerRow = (analyzer) => (dispatch) => { |
| dispatch({ |
| type: ActionTypes.SEARCH_INDEX_ADD_ANALYZER_ROW, |
| options: { |
| analyzer: analyzer |
| } |
| }); |
| }; |
| |
| const removeAnalyzerRow = (rowIndex) => (dispatch) => { |
| dispatch({ |
| type: ActionTypes.SEARCH_INDEX_REMOVE_ANALYZER_ROW, |
| options: { |
| rowIndex: rowIndex |
| } |
| }); |
| }; |
| |
| const setAnalyzerRowFieldName = (params) => (dispatch) => { |
| dispatch({ |
| type: ActionTypes.SEARCH_INDEX_SET_ANALYZER_ROW_FIELD_NAME, |
| options: { |
| rowIndex: params.rowIndex, |
| fieldName: params.fieldName |
| } |
| }); |
| }; |
| |
| const setAnalyzer = (params) => (dispatch) => { |
| dispatch({ |
| type: ActionTypes.SEARCH_INDEX_SET_ANALYZER_ROW, |
| options: { |
| rowIndex: params.rowIndex, |
| analyzer: params.analyzer |
| } |
| }); |
| }; |
| |
| const setDefaultMultipleAnalyzer = (analyzer) => (dispatch) => { |
| dispatch({ |
| type: ActionTypes.SEARCH_INDEX_SET_DEFAULT_MULTIPLE_ANALYZER, |
| options: { |
| analyzer: analyzer |
| } |
| }); |
| }; |
| |
| const setSingleAnalyzer = (analyzer) => (dispatch) => { |
| dispatch({ |
| type: ActionTypes.SEARCH_INDEX_SET_SINGLE_ANALYZER, |
| options: { |
| analyzer: analyzer |
| } |
| }); |
| }; |
| |
| const saveSearchIndex = (doc, info, navigateToUrl) => { |
| doc.setIndex(info.indexName, info.indexFunction, info.analyzerInfo); |
| |
| if (info.lastSavedDesignDocName === doc.id && info.lastSavedSearchIndexName !== info.indexName) { |
| var indexes = doc.get('indexes') || {}; |
| delete indexes[info.lastSavedSearchIndexName]; |
| doc.set({ indexes: indexes }); |
| } |
| |
| doc.save().then(() => { |
| info.designDocs.add(doc, { merge: true }); |
| |
| FauxtonAPI.addNotification({ |
| msg: 'The search index has been saved.', |
| type: 'success', |
| clear: true |
| }); |
| |
| // if the user just saved the view to a different design doc, remove the view from the old design doc and |
| // maybe even delete if it's empty |
| if (!info.isCreatingIndex && info.lastSavedDesignDocName !== doc.id) { |
| const oldDesignDoc = IndexEditorActions.helpers.findDesignDoc(info.designDocs, info.lastSavedDesignDocName); |
| IndexEditorActions.safeDeleteIndex(oldDesignDoc, info.designDocs, 'indexes', info.lastSavedSearchIndexName, { |
| onSuccess: () => { |
| SidebarActions.dispatchUpdateDesignDocs(info.designDocs); |
| } |
| }); |
| } |
| |
| SidebarActions.dispatchUpdateDesignDocs(info.designDocs); |
| FauxtonAPI.navigate(navigateToUrl, { trigger: true }); |
| }, (xhr) => { |
| const responseText = JSON.parse(xhr.responseText).reason; |
| FauxtonAPI.addNotification({ |
| msg: 'Save failed: ' + responseText, |
| type: 'error', |
| clear: true |
| }); |
| }); |
| }; |
| |
| |
| const deleteSearchIndex = (options) => { |
| const onSuccess = () => { |
| |
| // if the user was on the index that was just deleted, redirect them back to all docs |
| if (options.isOnIndex) { |
| const url = FauxtonAPI.urls('allDocs', 'app', options.database.safeID()); |
| FauxtonAPI.navigate(url); |
| } |
| SidebarActions.dispatchUpdateDesignDocs(options.designDocs); |
| |
| FauxtonAPI.addNotification({ |
| msg: 'The "' + options.indexName + '" search index has been deleted.', |
| type: 'info', |
| escape: false, |
| clear: true |
| }); |
| SidebarActions.dispatchHideDeleteIndexModal(); |
| }; |
| |
| IndexEditorActions.safeDeleteIndex(options.designDoc, options.designDocs, 'indexes', options.indexName, { onSuccess }); |
| }; |
| |
| const cloneSearchIndex = (params) => { |
| const targetDesignDoc = getDesignDoc(params.designDocs, params.targetDesignDocName, params.newDesignDocName, params.database); |
| let indexes = targetDesignDoc.get('indexes'); |
| if (indexes && _.has(indexes, params.newIndexName)) { |
| FauxtonAPI.addNotification({ |
| msg: 'That index name is already used in this design doc. Please enter a new name.', |
| type: 'error', |
| clear: true |
| }); |
| return; |
| } |
| if (!indexes) { |
| indexes = {}; |
| } |
| const sourceDesignDoc = IndexEditorActions.helpers.findDesignDoc(params.designDocs, '_design/' + params.sourceDesignDocName); |
| const sourceDesignDocJSON = sourceDesignDoc.toJSON(); |
| |
| // this sets whatever content is in the source index into the target design doc under the new index name |
| indexes[params.newIndexName] = sourceDesignDocJSON.indexes[params.sourceIndexName]; |
| targetDesignDoc.set({ indexes: indexes }); |
| |
| targetDesignDoc.save().then(function () { |
| params.onComplete(); |
| FauxtonAPI.addNotification({ |
| msg: 'The search index has been cloned.', |
| type: 'success', |
| clear: true |
| }); |
| |
| SidebarActions.dispatchUpdateDesignDocs(params.designDocs); |
| }, |
| function (xhr) { |
| params.onComplete(); |
| var responseText = JSON.parse(xhr.responseText).reason; |
| FauxtonAPI.addNotification({ |
| msg: 'Clone failed: ' + responseText, |
| type: 'error', |
| clear: true |
| }); |
| }); |
| }; |
| |
| const gotoEditSearchIndexPage = (databaseName, partitionKey, designDocName, indexName) => { |
| const encodedPartKey = partitionKey ? encodeURIComponent(partitionKey) : ''; |
| FauxtonAPI.navigate('#' + FauxtonAPI.urls('search', 'edit', encodeURIComponent(databaseName), |
| encodedPartKey, encodeURIComponent(designDocName), encodeURIComponent(indexName))); |
| }; |
| |
| const selectDesignDoc = (designDoc) => (dispatch) => { |
| dispatch({ |
| type: ActionTypes.SEARCH_INDEX_SELECT_DESIGN_DOC, |
| options: { |
| value: designDoc |
| } |
| }); |
| }; |
| |
| // const querySearch = (searchQuery, partitionKey) => { |
| const querySearch = (databaseName, partitionKey, ddocName, indexName, searchQuery) => { |
| const encodedDatabaseName = encodeURIComponent(databaseName); |
| const encodedPartitionKey = encodeURIComponent(partitionKey); |
| const encodedDdocName = encodeURIComponent(ddocName); |
| const encodedIndexName = encodeURIComponent(indexName); |
| const encodedSearchQuery = encodeURIComponent(searchQuery); |
| |
| const baseUrl = partitionKey ? |
| FauxtonAPI.urls('partitioned_search', 'app', encodedDatabaseName, encodedPartitionKey, encodedDdocName, encodedIndexName) : |
| FauxtonAPI.urls('search', 'app', encodedDatabaseName, encodedDdocName, encodedIndexName); |
| FauxtonAPI.navigate(`${baseUrl}${encodedIndexName}?${encodedSearchQuery}`, {trigger: true}); |
| }; |
| |
| const executSearchQuery = (database, partitionKey, ddoc, index, searchQuery, dispatch) => { |
| dispatch({ type: ActionTypes.SEARCH_INDEX_PREVIEW_REQUEST_MADE }); |
| |
| return API.fetchSearchResults(database, partitionKey, ddoc, index, searchQuery) |
| .then(rows => { |
| dispatch({ |
| type: ActionTypes.SEARCH_INDEX_PREVIEW_MODEL_UPDATED, |
| options: { |
| searchResults: rows |
| } |
| }); |
| }).catch(err => { |
| dispatch({ type: ActionTypes.SEARCH_INDEX_PREVIEW_REQUEST_ERROR }); |
| |
| if (err && err.message.includes('`partition` not supported')) { |
| dispatch(partitionParamNotSupported()); |
| } else if (err && err.message.includes('`partition` parameter is mandatory')) { |
| dispatch(partitionParamIsMandatory()); |
| } else { |
| FauxtonAPI.addNotification({ |
| msg: 'Search failed: ' + err.message, |
| type: 'error', |
| clear: true |
| }); |
| } |
| |
| if (err.message.includes('not found')) { |
| FauxtonAPI.navigate(FauxtonAPI.urls('allDocsSanitized', 'app', database), {trigger: true}); |
| } |
| }); |
| }; |
| |
| const setSearchQuery = (query) => (dispatch) => { |
| dispatch({ |
| type: ActionTypes.SEARCH_INDEX_SET_SEARCH_QUERY, |
| options: { |
| query: query |
| } |
| }); |
| }; |
| |
| const updateNewDesignDocName = (designDocName) => (dispatch) => { |
| dispatch({ |
| type: ActionTypes.SEARCH_INDEX_NEW_DESIGN_DOC_NAME_UPDATED, |
| options: { |
| value: designDocName |
| } |
| }); |
| }; |
| |
| const updateNewDesignDocPartitioned = (isPartitioned) => (dispatch) => { |
| dispatch({ |
| type: ActionTypes.SEARCH_INDEX_NEW_DESIGN_DOC_PARTITONED_UPDATED, |
| options: { |
| value: isPartitioned |
| } |
| }); |
| }; |
| |
| const partitionParamNotSupported = () => ({ |
| type: ActionTypes.SEARCH_INDEX_PARTITION_PARAM_NOT_SUPPORTED |
| }); |
| |
| const partitionParamIsMandatory = () => ({ |
| type: ActionTypes.SEARCH_INDEX_PARTITION_PARAM_MANDATORY |
| }); |
| |
| |
| // helpers |
| |
| function getDesignDoc (designDocs, targetDesignDocName, newDesignDocName, database) { |
| if (targetDesignDocName === 'new-doc') { |
| var doc = { |
| "_id": "_design/" + newDesignDocName, |
| "indexes": {}, |
| "language": "javascript" |
| }; |
| return new Search.Doc(doc, { database: database }); |
| } |
| |
| var foundDoc = designDocs.find(function (ddoc) { |
| return ddoc.id === targetDesignDocName; |
| }); |
| return (!foundDoc) ? null : foundDoc.dDocModel(); |
| } |
| |
| |
| export default { |
| dispatchInitNewSearchIndex, |
| dispatchInitSearchIndex, |
| dispatchEditSearchIndex, |
| selectTab, |
| setSearchIndexName, |
| setAnalyzerType, |
| addAnalyzerRow, |
| removeAnalyzerRow, |
| setAnalyzerRowFieldName, |
| setAnalyzer, |
| setDefaultMultipleAnalyzer, |
| selectDesignDoc, |
| saveSearchIndex, |
| cloneSearchIndex, |
| deleteSearchIndex, |
| gotoEditSearchIndexPage, |
| querySearch, |
| setSearchQuery, |
| setSingleAnalyzer, |
| updateNewDesignDocName, |
| updateNewDesignDocPartitioned, |
| partitionParamNotSupported, |
| partitionParamIsMandatory |
| }; |