This is an automated email from the ASF dual-hosted git repository.
garren pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/couchdb-fauxton.git
The following commit(s) were added to refs/heads/master by this push:
new 5b56ac0 Simplify Mango/Query panel layout (#950)
5b56ac0 is described below
commit 5b56ac0b932a20f3ddf199b70b0d934b46ff0cf7
Author: Will Holley <[email protected]>
AuthorDate: Mon Aug 14 14:02:42 2017 +0100
Simplify Mango/Query panel layout (#950)
This adds some tweaks to the Mango/Query UI in an attempt to improve
usability:
* Remove redundant description panel
* Remove list of available indexes. Now that queries will run with no
indexes defined, this is less important and is available in the index
management page.
* Increase query editor size and prevent redundant internal panel scrolling
* Add explicit Mango Index React component
* Add option to show the _explain output for the current query
* Remove the feature which changed the default query based on the
available indexes. This seems redundant now that Mango can run with no indexes
defined.
---
app/addons/components/components/codeeditor.js | 4 +-
.../components/components/codeeditorpanel.js | 1 +
.../documents/__tests__/results-toolbar.test.js | 7 -
app/addons/documents/assets/less/view-editor.less | 5 +-
app/addons/documents/base.js | 8 +
app/addons/documents/components/results-toolbar.js | 21 ++-
app/addons/documents/header/header.js | 5 +-
app/addons/documents/index-results/actions.js | 11 +-
.../index-results/index-results.components.js | 4 +-
app/addons/documents/index-results/stores.js | 11 +-
.../mango/__tests__/mango.actions.test.js | 60 -------
app/addons/documents/mango/mango.actions.js | 91 ++++------
app/addons/documents/mango/mango.actiontypes.js | 3 +-
app/addons/documents/mango/mango.components.js | 184 +++++++++++----------
app/addons/documents/mango/mango.stores.js | 69 ++------
.../documents/mango/tests/mango.componentsSpec.js | 94 -----------
.../documents/mango/tests/mango.storesSpec.js | 59 -------
app/addons/documents/mangolayout.js | 81 ++++++---
app/addons/documents/pagination/pagination.js | 2 +-
app/addons/documents/resources.js | 6 +-
app/addons/documents/routes-mango.js | 15 +-
.../documents/tests/nightwatch/mangoIndex.js | 1 -
.../tests/nightwatch/updatesUrlsSameRouteobject.js | 1 -
assets/less/fauxton.less | 4 +
24 files changed, 260 insertions(+), 487 deletions(-)
diff --git a/app/addons/components/components/codeeditor.js
b/app/addons/components/components/codeeditor.js
index 893826b..cc605cf 100644
--- a/app/addons/components/components/codeeditor.js
+++ b/app/addons/components/components/codeeditor.js
@@ -41,6 +41,7 @@ export const CodeEditor = React.createClass({
// these two options create auto-resizeable code editors, with a maximum
number of lines
setHeightToLineCount: false,
maxLines: 10,
+ minLines: 10,
// optional editor key commands (e.g. specific save action)
editorCommands: [],
@@ -165,7 +166,8 @@ export const CodeEditor = React.createClass({
var numLines = this.editor.getSession().getDocument().getLength();
var maxLines = (numLines > this.props.maxLines) ? this.props.maxLines :
numLines;
this.editor.setOptions({
- maxLines: maxLines
+ maxLines: maxLines,
+ minLines: this.props.minLines
});
},
diff --git a/app/addons/components/components/codeeditorpanel.js
b/app/addons/components/components/codeeditorpanel.js
index dc7910d..4ee89f5 100644
--- a/app/addons/components/components/codeeditorpanel.js
+++ b/app/addons/components/components/codeeditorpanel.js
@@ -136,6 +136,7 @@ export const CodeEditorPanel = React.createClass({
showGutter={true}
ignorableErrors={this.ignorableErrors}
setHeightToLineCount={true}
+ maxLines={10000}
blur={this.props.blur}
/>
<Beautify code={this.state.code} beautifiedCode={this.beautify} />
diff --git a/app/addons/documents/__tests__/results-toolbar.test.js
b/app/addons/documents/__tests__/results-toolbar.test.js
index 2fdd46f..7ce5331 100644
--- a/app/addons/documents/__tests__/results-toolbar.test.js
+++ b/app/addons/documents/__tests__/results-toolbar.test.js
@@ -36,11 +36,4 @@ describe('Results Toolbar', () => {
expect(wrapper.find('.two-sides-toggle-button').length).toBe(1);
expect(wrapper.find('.document-result-screen__toolbar-create-btn').length).toBe(1);
});
-
- it('only renders create button when there are no results', () => {
- const wrapper = mount(<ResultsToolBar hasResults={false} {...restProps}/>);
- expect(wrapper.find('.bulk-action-component').length).toBe(0);
- expect(wrapper.find('.two-sides-toggle-button').length).toBe(0);
-
expect(wrapper.find('.document-result-screen__toolbar-create-btn').length).toBe(1);
- });
});
diff --git a/app/addons/documents/assets/less/view-editor.less
b/app/addons/documents/assets/less/view-editor.less
index abebb70..d319b5e 100644
--- a/app/addons/documents/assets/less/view-editor.less
+++ b/app/addons/documents/assets/less/view-editor.less
@@ -35,7 +35,7 @@
border-bottom: 1px solid #ccc;
}
.padded-box {
- margin: 25px 30px;
+ margin: 15px;
}
.db-title {
color: @brandHighlight;
@@ -53,10 +53,11 @@
padding: 5px;
}
form {
- padding-bottom: 50px;
+ padding-bottom: 15px;
}
a.edit-link {
float: right;
+ margin-right: 5px;
}
.help-link {
margin-left: 4px;
diff --git a/app/addons/documents/base.js b/app/addons/documents/base.js
index d44e7ae..98d2b48 100644
--- a/app/addons/documents/base.js
+++ b/app/addons/documents/base.js
@@ -222,6 +222,14 @@ FauxtonAPI.registerUrls('mango', {
}
return 'database/' + db + '/_find' + query;
+ },
+
+ 'explain-server': function (db) {
+ return app.host + '/' + db + '/_explain';
+ },
+
+ 'explain-apiurl': function (db) {
+ return window.location.origin + '/' + db + '/_explain';
}
});
diff --git a/app/addons/documents/components/results-toolbar.js
b/app/addons/documents/components/results-toolbar.js
index e5c5a2a..f7c95c0 100644
--- a/app/addons/documents/components/results-toolbar.js
+++ b/app/addons/documents/components/results-toolbar.js
@@ -13,6 +13,7 @@ import React from 'react';
import BulkDocumentHeaderController from "../header/header";
import Stores from "../sidebar/stores";
import Components from "../../components/react-components";
+import app from '../../../app';
const {BulkActionComponent} = Components;
const store = Stores.sidebarStore;
@@ -23,7 +24,7 @@ export class ResultsToolBar extends React.Component {
}
render () {
- const dbName = store.getDatabase().id;
+ const database = store.getDatabase();
const {
hasResults,
isListDeletable,
@@ -52,15 +53,23 @@ export class ResultsToolBar extends React.Component {
bulkHeader = <BulkDocumentHeaderController {...this.props} />;
}
- return (
- <div className="document-result-screen__toolbar">
- {bulkAction}
- {bulkHeader}
+ let createDocumentLink = null;
+ if (database) {
+ const safeDatabaseId = app.utils.safeURLName(database.id);
+ createDocumentLink = (
<div className="document-result-screen__toolbar-flex-container">
- <a href={`#/database/${dbName}/new`} className="btn save
document-result-screen__toolbar-create-btn btn-primary">
+ <a href={`#/database/${safeDatabaseId}/new`} className="btn save
document-result-screen__toolbar-create-btn btn-primary">
Create Document
</a>
</div>
+ );
+ }
+
+ return (
+ <div className="document-result-screen__toolbar">
+ {bulkAction}
+ {bulkHeader}
+ {createDocumentLink}
</div>
);
}
diff --git a/app/addons/documents/header/header.js
b/app/addons/documents/header/header.js
index 0464709..0217c65 100644
--- a/app/addons/documents/header/header.js
+++ b/app/addons/documents/header/header.js
@@ -30,7 +30,8 @@ export default class BulkDocumentHeaderController extends
React.Component {
return {
selectedLayout: indexResultsStore.getSelectedLayout(),
bulkDocCollection: indexResultsStore.getBulkDocCollection(),
- isMango: indexResultsStore.getIsMangoResults()
+ isMango: indexResultsStore.getIsMangoResults(),
+ isMangoIndexList: indexResultsStore.getIsMangoIndexResults()
};
}
@@ -68,6 +69,8 @@ export default class BulkDocumentHeaderController extends
React.Component {
>
Metadata
</Button>;
+ } else if (this.state.isMangoIndexList) {
+ return null;
}
// reduce doesn't allow for include_docs=true, so we'll prevent JSON and
table
diff --git a/app/addons/documents/index-results/actions.js
b/app/addons/documents/index-results/actions.js
index f51d88d..b893313 100644
--- a/app/addons/documents/index-results/actions.js
+++ b/app/addons/documents/index-results/actions.js
@@ -136,17 +136,10 @@ export default {
},
reloadResultsList: function () {
- if (indexResultsStore.getTypeOfIndex() === 'mango') {
- return this.newResultsList({
- collection: indexResultsStore.getCollection(),
- bulkCollection: indexResultsStore.getBulkDocCollection(),
- typeOfIndex: 'mango'
- });
- }
-
return this.newResultsList({
collection: indexResultsStore.getCollection(),
- bulkCollection: indexResultsStore.getBulkDocCollection()
+ bulkCollection: indexResultsStore.getBulkDocCollection(),
+ typeOfIndex: indexResultsStore.getTypeOfIndex()
});
},
diff --git a/app/addons/documents/index-results/index-results.components.js
b/app/addons/documents/index-results/index-results.components.js
index 26dd135..d262879 100644
--- a/app/addons/documents/index-results/index-results.components.js
+++ b/app/addons/documents/index-results/index-results.components.js
@@ -358,7 +358,6 @@ var ResultsScreen = React.createClass({
isListDeletable={this.props.isListDeletable}
data={this.props.results}
isLoading={this.props.isLoading}
-
removeItem={this.props.removeItem}
isChecked={this.props.allDocumentsSelected}
hasSelectedItem={this.props.hasSelectedItem}
@@ -402,8 +401,7 @@ var ResultsScreen = React.createClass({
componentDidUpdate: function () {
prettyPrint();
- },
-
+ }
});
diff --git a/app/addons/documents/index-results/stores.js
b/app/addons/documents/index-results/stores.js
index 821e70c..6d4932e 100644
--- a/app/addons/documents/index-results/stores.js
+++ b/app/addons/documents/index-results/stores.js
@@ -44,6 +44,7 @@ Stores.IndexResultsStore = FauxtonAPI.Store.extend({
this._tableViewSelectedFields = [];
this._isPrioritizedEnabled = false;
+ this._explain = false;
this._tableSchema = [];
this._selectedLayout = Constants.LAYOUT_ORIENTATION.METADATA;
@@ -200,6 +201,10 @@ Stores.IndexResultsStore = FauxtonAPI.Store.extend({
this._selectedLayout = Constants.LAYOUT_ORIENTATION.METADATA;
}
+ if (this.getIsMangoIndexResults()) {
+ this._selectedLayout = Constants.LAYOUT_ORIENTATION.JSON;
+ }
+
this._cachedSelected = [];
this._filteredCollection =
this._collection.filter(filterOutGeneratedMangoDocs);
@@ -748,7 +753,11 @@ Stores.IndexResultsStore = FauxtonAPI.Store.extend({
},
getIsMangoResults: function () {
- return this._typeOfIndex === 'mango';
+ return this._typeOfIndex === 'mango' || this._typeOfIndex ===
'mango-index';
+ },
+
+ getIsMangoIndexResults: function () {
+ return this._typeOfIndex === 'mango-index';
},
getIsPrioritizedEnabled: function () {
diff --git a/app/addons/documents/mango/__tests__/mango.actions.test.js
b/app/addons/documents/mango/__tests__/mango.actions.test.js
deleted file mode 100644
index 1d74f7c..0000000
--- a/app/addons/documents/mango/__tests__/mango.actions.test.js
+++ /dev/null
@@ -1,60 +0,0 @@
-// 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 utils from '../../../../../test/mocha/testUtils';
-import FauxtonAPI from "../../../../core/api";
-import Actions from '../mango.actions';
-import sinon from 'sinon';
-
-const {restore, assert} = utils;
-FauxtonAPI.router = new FauxtonAPI.Router([]);
-
-describe('Mango actions', () => {
-
- afterEach(() => {
- restore(FauxtonAPI.navigate);
- restore(FauxtonAPI.addNotification);
- });
-
- it("should show a notification and redirect if database doesn't exist",
(done) => {
- const navigateSpy = sinon.spy(FauxtonAPI, 'navigate');
- const notificationSpy = sinon.spy(FauxtonAPI, 'addNotification');
-
- const database = {
- safeID : () => 'safe-id-db'
- };
-
- const options = {
- database,
- indexList: {
- database,
- fetch: () => {
- return Promise.reject({
- responseJSON: {
- error: 'not_found'
- }
- });
- },
- }
- };
-
- Actions.getIndexList(options);
- process.nextTick(() => {
- assert.ok(notificationSpy.calledOnce);
- assert.ok(/not exist/.test(notificationSpy.args[0][0].msg));
- assert.ok(navigateSpy.calledOnce);
- assert.deepEqual(navigateSpy.args[0][0], '/');
- done();
- });
- });
-
-});
diff --git a/app/addons/documents/mango/mango.actions.js
b/app/addons/documents/mango/mango.actions.js
index fec655f..f1540e4 100644
--- a/app/addons/documents/mango/mango.actions.js
+++ b/app/addons/documents/mango/mango.actions.js
@@ -13,7 +13,6 @@
import FauxtonAPI from "../../../core/api";
import Documents from "../resources";
import ActionTypes from "./mango.actiontypes";
-import IndexResultsStores from "../index-results/stores";
import IndexResultActions from "../index-results/actions";
export default {
@@ -39,9 +38,9 @@ export default {
});
},
- saveQuery: function (options) {
- var queryCode = JSON.parse(options.queryCode),
- mangoIndex = new Documents.MangoIndex(queryCode, {database:
options.database});
+ saveIndex: function ({database, queryCode}) {
+ const query = JSON.parse(queryCode),
+ mangoIndex = new Documents.MangoIndex(query, {database: database});
FauxtonAPI.addNotification({
msg: 'Saving Index for Query...',
@@ -52,36 +51,18 @@ export default {
mangoIndex
.save()
.then(function () {
- var url = '#' + FauxtonAPI.urls('mango', 'query-app',
options.database.safeID());
+ var url = '#' + FauxtonAPI.urls('mango', 'query-app',
database.safeID());
- FauxtonAPI.dispatch({
- type: ActionTypes.MANGO_NEW_QUERY_FIND_CODE_FROM_FIELDS,
- options: {
- fields: queryCode.index.fields
- }
- });
+ // force mango index list to reload
+ IndexResultActions.reloadResultsList();
- var mangoIndexCollection = new Documents.MangoIndexCollection(null, {
- database: options.database,
- params: null,
- paging: {
- pageSize: IndexResultsStores.indexResultsStore.getPerPage()
- }
+ FauxtonAPI.addNotification({
+ msg: 'Index is ready for querying. <a href="' + url + '">Run a
Query.</a>',
+ type: 'success',
+ clear: true,
+ escape: false
});
-
- this.getIndexList({indexList: mangoIndexCollection}).then(function () {
-
- IndexResultActions.reloadResultsList();
-
- FauxtonAPI.addNotification({
- msg: 'Index is ready for querying. <a href="' + url + '">Run a
Query.</a>',
- type: 'success',
- clear: true,
- escape: false
- });
- }.bind(this));
-
- }.bind(this))
+ })
.fail(function (res) {
FauxtonAPI.addNotification({
msg: res.responseJSON.reason,
@@ -91,33 +72,29 @@ export default {
});
},
- mangoResetIndexList: function (options) {
- FauxtonAPI.dispatch({
- type: ActionTypes.MANGO_RESET,
- options: options
- });
- },
-
- getIndexList: function (options) {
- FauxtonAPI.dispatch({
- type: ActionTypes.MANGO_NEW_AVAILABLE_INDEXES,
- options: options
- });
-
- return options.indexList.fetch({reset: true}).then(() => {
- this.mangoResetIndexList({isLoading: false});
- }, (xhr) => {
- let errorMsg = 'Bad request!';
- if (xhr.responseJSON && xhr.responseJSON.error === 'not_found') {
- const databaseName = options.indexList.database.safeID();
- errorMsg = `The ${databaseName} database does not exist.`;
- FauxtonAPI.navigate('/', {trigger: true});
- }
+ runExplainQuery: function ({database, queryCode}) {
+ const url = FauxtonAPI.urls('mango', 'explain-server', database.safeID()),
+ query = JSON.parse(queryCode);
+
+ $.ajax({
+ type: 'POST',
+ url: url,
+ contentType: 'application/json; charset=utf-8',
+ dataType: 'json',
+ data: JSON.stringify(query)
+ }).then(function (explainPlan) {
+ FauxtonAPI.dispatch({
+ type: ActionTypes.MANGO_EXPLAIN_RESULTS,
+ options: {
+ explainPlan: explainPlan
+ }
+ });
+ }).fail(function () {
FauxtonAPI.addNotification({
- msg: errorMsg,
- type: "error",
- clear: true
- });
+ msg: 'There was an error fetching the query plan.',
+ type: 'error',
+ clear: true
+ });
});
}
};
diff --git a/app/addons/documents/mango/mango.actiontypes.js
b/app/addons/documents/mango/mango.actiontypes.js
index 2e3014d..86db14a 100644
--- a/app/addons/documents/mango/mango.actiontypes.js
+++ b/app/addons/documents/mango/mango.actiontypes.js
@@ -12,9 +12,8 @@
export default {
MANGO_SET_DB: 'MANGO_SET_DB',
- MANGO_NEW_QUERY_FIND_CODE_FROM_FIELDS:
'MANGO_NEW_QUERY_FIND_CODE_FROM_FIELDS',
MANGO_NEW_QUERY_FIND_CODE: 'MANGO_NEW_QUERY_FIND_CODE',
MANGO_NEW_QUERY_CREATE_INDEX_CODE: 'MANGO_NEW_QUERY_CREATE_INDEX_CODE',
MANGO_NEW_AVAILABLE_INDEXES: 'MANGO_NEW_AVAILABLE_INDEXES',
- MANGO_RESET: 'MANGO_RESET'
+ MANGO_EXPLAIN_RESULTS: 'MANGO_EXPLAIN_RESULTS'
};
diff --git a/app/addons/documents/mango/mango.components.js
b/app/addons/documents/mango/mango.components.js
index 2931783..285ef5a 100644
--- a/app/addons/documents/mango/mango.components.js
+++ b/app/addons/documents/mango/mango.components.js
@@ -12,12 +12,11 @@
import app from "../../../app";
import FauxtonAPI from "../../../core/api";
-import React from "react";
+import React, { Component } from 'react';
import Stores from "./mango.stores";
import Actions from "./mango.actions";
import ReactComponents from "../../components/react-components";
import IndexResultActions from "../index-results/actions";
-import MangoHelper from "./mango.helper";
import "../../../../assets/js/plugins/prettify";
var mangoStore = Stores.mangoStore;
@@ -35,11 +34,7 @@ var MangoQueryEditorController = React.createClass({
getStoreState: function () {
return {
queryCode: mangoStore.getQueryFindCode(),
- database: mangoStore.getDatabase(),
- changedQuery: mangoStore.getQueryFindCodeChanged(),
- availableIndexes: mangoStore.getAvailableQueryIndexes(),
- additionalIndexes: mangoStore.getAvailableAdditionalIndexes(),
- isLoading: mangoStore.getLoadingIndexes()
+ database: mangoStore.getDatabase()
};
},
@@ -80,25 +75,43 @@ var MangoQueryEditorController = React.createClass({
dbName={this.state.database.id}
onSubmit={this.runQuery}
title={this.props.editorTitle}
- additionalIndexesText={this.props.additionalIndexesText}
docs={getDocUrl('MANGO_SEARCH')}
exampleCode={this.state.queryCode}
- changedQuery={this.state.changedQuery}
- availableIndexes={this.state.availableIndexes}
- additionalIndexes={this.state.additionalIndexes}
- confirmbuttonText="Run Query" />
+ onExplainQuery={this.runExplain}
+ changedQuery={this.state.changedQuery} />
);
},
- runQuery: function (event) {
- event.preventDefault();
-
+ notifyOnQueryError: function() {
if (this.getMangoEditor().hasErrors()) {
FauxtonAPI.addNotification({
msg: 'Please fix the Javascript errors and try again.',
type: 'error',
clear: true
});
+
+ return true;
+ }
+ return false;
+ },
+
+ runExplain: function(event) {
+ event.preventDefault();
+
+ if (this.notifyOnQueryError()) {
+ return;
+ }
+
+ Actions.runExplainQuery({
+ database: this.state.database,
+ queryCode: this.getMangoEditor().getEditorValue()
+ });
+ },
+
+ runQuery: function (event) {
+ event.preventDefault();
+
+ if (this.notifyOnQueryError()) {
return;
}
@@ -110,31 +123,9 @@ var MangoQueryEditorController = React.createClass({
});
var MangoEditor = React.createClass({
- getDefaultProps: function () {
- return {
- changedQuery: null,
- availableIndexes: null,
- additionalIndexes: null
- };
- },
-
render: function () {
- var url = '#/' + FauxtonAPI.urls('allDocs', 'app',
FauxtonAPI.url.encode(this.props.dbName), '');
-
return (
<div className="mango-editor-wrapper">
- <PaddedBorderedBox>
- <div
- dangerouslySetInnerHTML={{__html: this.props.description}}
- className="editor-description">
- </div>
- </PaddedBorderedBox>
- <PaddedBorderedBox>
- <strong>Database</strong>
- <div className="db-title">
- <a href={url}>{this.props.dbName}</a>
- </div>
- </PaddedBorderedBox>
<form className="form-horizontal" onSubmit={this.props.onSubmit}>
<PaddedBorderedBox>
<CodeEditorPanel
@@ -143,12 +134,12 @@ var MangoEditor = React.createClass({
title={this.props.title}
docLink={this.props.docs}
defaultCode={this.props.exampleCode} />
- {this.getChangedQueryText()}
</PaddedBorderedBox>
- {this.getIndexBox()}
<div className="padded-box">
- <div className="control-group">
- <ConfirmButton text={this.props.confirmbuttonText}
id="create-index-btn" showIcon={false} />
+ <div className="controls-group">
+ <button type="submit" id="create-index-btn" className="btn
btn-primary btn-space">Run Query</button>
+ <button type="button" id="explain-btn" className="btn
btn-secondary btn-space" onClick={this.props.onExplainQuery}>Explain</button>
+ <a className="edit-link" href={'#' + FauxtonAPI.urls('mango',
'index-app', encodeURIComponent(this.props.dbName))}>manage indexes</a>
</div>
</div>
</form>
@@ -156,51 +147,43 @@ var MangoEditor = React.createClass({
);
},
- getChangedQueryText: function () {
- if (!this.props.changedQuery) {
- return null;
- }
+ getEditorValue: function () {
+ return this.refs.field.getValue();
+ },
- return (
- <div className="info-changed-query">
- <strong>Info:</strong>
- <div>We changed the default query based on the last Index you
created.</div>
- </div>
- );
+ getEditor: function () {
+ return this.refs.field.getEditor();
},
- getIndexBox: function () {
- if (!this.props.availableIndexes) {
- return null;
- }
+ hasErrors: function () {
+ return this.getEditor().hasErrors();
+ }
+});
+var MangoIndexEditor = React.createClass({
+ render: function () {
return (
- <PaddedBorderedBox>
- <strong>Queryable indexes:</strong>
- <a className="edit-link" href={'#' + FauxtonAPI.urls('mango',
'index-app', encodeURIComponent(this.props.dbName))}>edit</a>
- <pre
- className="mango-available-indexes">
- {this.getIndexes('index', this.props.availableIndexes)}
- {this.getIndexes('additonal', this.props.additionalIndexes)}
- </pre>
- </PaddedBorderedBox>
+ <div className="mango-editor-wrapper">
+ <form className="form-horizontal" onSubmit={this.props.onSubmit}>
+ <PaddedBorderedBox>
+ <CodeEditorPanel
+ id="query-field"
+ ref="field"
+ title={this.props.title}
+ docLink={this.props.docs}
+ defaultCode={this.props.exampleCode} />
+ </PaddedBorderedBox>
+ <div className="padded-box">
+ <div className="control-group">
+ <ConfirmButton text="Create index" id="create-index-btn"
showIcon={false} />
+ <a className="edit-link" href={'#' + FauxtonAPI.urls('mango',
'query-app', encodeURIComponent(this.props.dbName))}>edit query</a>
+ </div>
+ </div>
+ </form>
+ </div>
);
},
- getIndexes: function (prefix, indexes) {
- if (!indexes) {
- return;
- }
-
- return indexes.map(function (index, i) {
- var name = MangoHelper.getIndexName(index);
-
- return (
- <div key={prefix + i}>{name}</div>
- );
- });
- },
-
getEditorValue: function () {
return this.refs.field.getValue();
},
@@ -239,24 +222,23 @@ var MangoIndexEditorController = React.createClass({
},
getMangoEditor: function () {
- return this.refs.mangoEditor;
+ return this.refs.mangoIndexEditor;
},
render: function () {
return (
- <MangoEditor
- ref="mangoEditor"
+ <MangoIndexEditor
+ ref="mangoIndexEditor"
description={this.props.description}
dbName={this.state.database.id}
- onSubmit={this.saveQuery}
+ onSubmit={this.saveIndex}
title="Index"
docs={getDocUrl('MANGO_INDEX')}
- exampleCode={this.state.queryIndexCode}
- confirmbuttonText="Create Index" />
+ exampleCode={this.state.queryIndexCode} />
);
},
- saveQuery: function (event) {
+ saveIndex: function (event) {
event.preventDefault();
if (this.getMangoEditor().hasErrors()) {
@@ -268,16 +250,38 @@ var MangoIndexEditorController = React.createClass({
return;
}
- Actions.saveQuery({
+ Actions.saveIndex({
database: this.state.database,
queryCode: this.getMangoEditor().getEditorValue()
});
}
});
-var Views = {
- MangoIndexEditorController: MangoIndexEditorController,
- MangoQueryEditorController: MangoQueryEditorController
+class ExplainPage extends Component {
+ componentDidMount () {
+ prettyPrint();
+ };
+
+ componentDidUpdate () {
+ prettyPrint();
+ };
+
+ render () {
+
+ return (
+ <div>
+ <pre className="prettyprint">{JSON.stringify(this.props.explainPlan,
null, ' ')}</pre>
+ </div>
+ );
+ };
+}
+
+ExplainPage.propTypes = {
+ explainPlan: React.PropTypes.object.isRequired
};
-export default Views;
+export default {
+ MangoIndexEditorController: MangoIndexEditorController,
+ MangoQueryEditorController: MangoQueryEditorController,
+ ExplainPage: ExplainPage
+};
diff --git a/app/addons/documents/mango/mango.stores.js
b/app/addons/documents/mango/mango.stores.js
index 7836798..7f19db8 100644
--- a/app/addons/documents/mango/mango.stores.js
+++ b/app/addons/documents/mango/mango.stores.js
@@ -12,7 +12,7 @@
import FauxtonAPI from "../../../core/api";
import ActionTypes from "./mango.actiontypes";
-
+import IndexActionTypes from "../index-results/actiontypes";
var defaultQueryIndexCode = {
"index": {
@@ -35,8 +35,6 @@ Stores.MangoStore = FauxtonAPI.Store.extend({
this._queryFindCode = defaultQueryFindCode;
this._queryIndexCode = defaultQueryIndexCode;
this._queryFindCodeChanged = false;
- this._availableIndexes = [];
- this._getLoadingIndexes = true;
},
getQueryIndexCode: function () {
@@ -55,37 +53,10 @@ Stores.MangoStore = FauxtonAPI.Store.extend({
this._queryFindCode = options.code;
},
- getLoadingIndexes: function () {
- return this._getLoadingIndexes;
- },
-
- setLoadingIndexes: function (options) {
- this._getLoadingIndexes = options.isLoading;
- },
-
formatCode: function (code) {
return JSON.stringify(code, null, ' ');
},
- newQueryFindCodeFromFields: function (options) {
- var fields = options.fields,
- queryCode = JSON.parse(JSON.stringify(this._queryFindCode)),
- selectorContent;
-
- if (!fields) {
- return;
- }
-
- selectorContent = fields.reduce(function (acc, field) {
- acc[field] = {"$gt": null};
- return acc;
- }, {});
-
- queryCode.selector = selectorContent;
- this._queryFindCode = queryCode;
- this._queryFindCodeChanged = true;
- },
-
getQueryFindCodeChanged: function () {
return this._queryFindCodeChanged;
},
@@ -98,26 +69,12 @@ Stores.MangoStore = FauxtonAPI.Store.extend({
return this._database;
},
- setAvailableIndexes: function (options) {
- this._availableIndexes = options.indexList;
- },
-
- getAvailableQueryIndexes: function () {
- return this._availableIndexes.filter(function (el) {
- return ['json', 'special'].indexOf(el.get('type')) !== -1;
- });
+ setExplainPlan: function (options) {
+ this._explainPlan = options && options.explainPlan;
},
- getAvailableAdditionalIndexes: function () {
- var indexes = FauxtonAPI.getExtensions('mango:additionalIndexes')[0];
-
- if (!indexes) {
- return;
- }
-
- return this._availableIndexes.filter(function (el) {
- return el.get('type').indexOf(indexes.type) !== -1;
- });
+ getExplainPlan: function() {
+ return this._explainPlan;
},
dispatch: function (action) {
@@ -131,21 +88,17 @@ Stores.MangoStore = FauxtonAPI.Store.extend({
this.setQueryIndexCode(action.options);
break;
- case ActionTypes.MANGO_NEW_QUERY_FIND_CODE_FROM_FIELDS:
- this.newQueryFindCodeFromFields(action.options);
- break;
-
case ActionTypes.MANGO_NEW_QUERY_FIND_CODE:
this.setQueryFindCode(action.options);
break;
- case ActionTypes.MANGO_NEW_AVAILABLE_INDEXES:
- this.setAvailableIndexes(action.options);
- break;
+ case ActionTypes.MANGO_EXPLAIN_RESULTS:
+ this.setExplainPlan(action.options);
+ break;
- case ActionTypes.MANGO_RESET:
- this.setLoadingIndexes(action.options);
- break;
+ case IndexActionTypes.INDEX_RESULTS_CLEAR_RESULTS:
+ this.setExplainPlan(false);
+ break;
}
this.triggerChange();
diff --git a/app/addons/documents/mango/tests/mango.componentsSpec.js
b/app/addons/documents/mango/tests/mango.componentsSpec.js
index f7274a0..4219f6f 100644
--- a/app/addons/documents/mango/tests/mango.componentsSpec.js
+++ b/app/addons/documents/mango/tests/mango.componentsSpec.js
@@ -48,26 +48,6 @@ describe('Mango IndexEditor', function () {
var payload = JSON.parse(editor.getMangoEditor().getEditorValue());
assert.equal(payload.index.fields[0], '_id');
});
-
- it('renders the current database', function () {
- editor = TestUtils.renderIntoDocument(
- <Views.MangoIndexEditorController description="foo" />,
- container
- );
- var $el = $(ReactDOM.findDOMNode(editor));
-
- assert.equal($el.find('.db-title').text(), 'testdb');
- });
-
- it('renders a description', function () {
- editor = TestUtils.renderIntoDocument(
- <Views.MangoIndexEditorController description="CouchDB Query is great!"
/>,
- container
- );
- var $el = $(ReactDOM.findDOMNode(editor));
-
- assert.equal($el.find('.editor-description').text(), 'CouchDB Query is
great!');
- });
});
describe('Mango QueryEditor', function () {
@@ -82,8 +62,6 @@ describe('Mango QueryEditor', function () {
database: database
});
- MangoActions.mangoResetIndexList({isLoading: false});
-
mangoCollection = new Resources.MangoIndexCollection([{
ddoc: '_design/e4d338e5d6f047749f5399ab998b4fa04ba0c816',
def: {
@@ -122,24 +100,6 @@ describe('Mango QueryEditor', function () {
ReactDOM.unmountComponentAtNode(container);
});
- it('lists our available indexes', function () {
- editor = TestUtils.renderIntoDocument(
- <Views.MangoQueryEditorController description="foo" />,
- container
- );
- var $el = $(ReactDOM.findDOMNode(editor));
- assert.equal($el.find('.mango-available-indexes').length, 1);
-
- assert.include(
- $el.find('.mango-available-indexes').text(),
- 'json: _id, foo, ente'
- );
- assert.include(
- $el.find('.mango-available-indexes').text(),
- 'json: _id'
- );
- });
-
it('has a default query', function () {
editor = ReactDOM.render(
<Views.MangoQueryEditorController description="foo" />,
@@ -148,58 +108,4 @@ describe('Mango QueryEditor', function () {
var json = JSON.parse(editor.getMangoEditor().getEditorValue());
assert.equal(Object.keys(json.selector)[0], '_id');
});
-
- it('can render a query based on the last defined index', function () {
- FauxtonAPI.dispatch({
- type: ActionTypes.MANGO_NEW_QUERY_FIND_CODE_FROM_FIELDS,
- options: {
- fields: ['zetti', 'mussmaennchen']
- }
- });
-
- editor = TestUtils.renderIntoDocument(
- <Views.MangoQueryEditorController description="foo" />,
- container
- );
-
- var json = JSON.parse(editor.getMangoEditor().getEditorValue());
- assert.equal(Object.keys(json.selector)[0], 'zetti');
- assert.equal(Object.keys(json.selector)[1], 'mussmaennchen');
- });
-
- it('informs the user that it uses a query based on the last defined index',
function () {
- FauxtonAPI.dispatch({
- type: ActionTypes.MANGO_NEW_QUERY_FIND_CODE_FROM_FIELDS,
- options: {
- fields: ['zetti', 'mussmaennchen']
- }
- });
-
- editor = TestUtils.renderIntoDocument(
- <Views.MangoQueryEditorController description="foo" />,
- container
- );
- var $el = $(ReactDOM.findDOMNode(editor));
- assert.equal($el.find('.info-changed-query').length, 1);
- });
-
- it('renders the current database', function () {
- editor = TestUtils.renderIntoDocument(
- <Views.MangoQueryEditorController description="foo" />,
- container
- );
- var $el = $(ReactDOM.findDOMNode(editor));
-
- assert.equal($el.find('.db-title').text(), 'testdb');
- });
-
- it('renders a description', function () {
- editor = TestUtils.renderIntoDocument(
- <Views.MangoQueryEditorController description="CouchDB Query is great!"
/>,
- container
- );
- var $el = $(ReactDOM.findDOMNode(editor));
-
- assert.equal($el.find('.editor-description').text(), 'CouchDB Query is
great!');
- });
});
diff --git a/app/addons/documents/mango/tests/mango.storesSpec.js
b/app/addons/documents/mango/tests/mango.storesSpec.js
index 941794c..d1078f0 100644
--- a/app/addons/documents/mango/tests/mango.storesSpec.js
+++ b/app/addons/documents/mango/tests/mango.storesSpec.js
@@ -12,7 +12,6 @@
import FauxtonAPI from "../../../../core/api";
import Stores from "../mango.stores";
-import Resources from "../../resources";
import testUtils from "../../../../../test/mocha/testUtils";
var assert = testUtils.assert;
var dispatchToken;
@@ -34,63 +33,5 @@ describe('Mango Store', function () {
it('returns a default query', function () {
assert.ok(store.getQueryFindCode());
});
-
- it('can set new selectors', function () {
- store.newQueryFindCodeFromFields({fields: ['foo', 'bar']});
- var res = store.getQueryFindCode();
- assert.equal(res, JSON.stringify({
- "selector": {
- "foo": {"$gt": null},
- "bar": {"$gt": null}
- }
- }, null, ' '));
- });
-
- it('indicates that we set another query for the user', function () {
- assert.notOk(store.getQueryFindCodeChanged());
- store.newQueryFindCodeFromFields({fields: ['mussman', 'zetti']});
- assert.ok(store.getQueryFindCodeChanged());
- });
-
- it('alters the default query', function () {
- assert.notOk(store.getQueryFindCodeChanged());
- store.newQueryFindCodeFromFields({fields: ['mussman', 'zetti']});
- assert.deepEqual(store.getQueryFindCode(), JSON.stringify({
- "selector": {
- "mussman": {"$gt": null},
- "zetti": {"$gt": null}
- }
- }, null, ' '));
- });
-
- it('filters querytypes that are not needed', function () {
-
- var collection = new Resources.MangoIndexCollection([
- new Resources.MangoIndex({
- ddoc: null,
- name: 'emma',
- type: 'special',
- def: {fields: [{_id: 'asc'}]}
- }, {}),
- new Resources.MangoIndex({
- ddoc: null,
- name: 'biene',
- type: 'json',
- def: {fields: [{_id: 'desc'}]}
- }, {}),
- new Resources.MangoIndex({
- ddoc: null,
- name: 'alf',
- type: 'nickname',
- def: {fields: [{_id: 'asc'}]}
- }, {})
- ], {
- database: {id: 'databaseId', safeID: function () { return this.id; }},
- params: {limit: 20}
- });
- store._availableIndexes = collection;
- assert.equal(store.getAvailableQueryIndexes().length, 2);
- });
-
});
});
diff --git a/app/addons/documents/mangolayout.js
b/app/addons/documents/mangolayout.js
index 105fd26..460e069 100644
--- a/app/addons/documents/mangolayout.js
+++ b/app/addons/documents/mangolayout.js
@@ -10,7 +10,7 @@
// License for the specific language governing permissions and limitations
under
// the License.
-import React from 'react';
+import React, { Component } from 'react';
import app from "../../app";
import ReactPagination from "./pagination/pagination";
import {Breadcrumbs} from '../components/header-breadcrumbs';
@@ -18,7 +18,10 @@ import {NotificationCenterButton} from
'../fauxton/notifications/notifications';
import {ApiBarWrapper} from '../components/layouts';
import MangoComponents from "./mango/mango.components";
import IndexResultsComponents from "./index-results/index-results.components";
+import Stores from "./mango/mango.stores";
+import FauxtonAPI from "../../core/api";
+const mangoStore = Stores.mangoStore;
export const RightHeader = ({docURL, endpoint}) => {
return (
@@ -59,17 +62,23 @@ MangoHeader.defaultProps = {
crumbs: []
};
-const MangoContent = ({edit, designDocs}) => {
- const leftContent = edit ?
- <MangoComponents.MangoIndexEditorController
+export const MangoContent = ({edit, designDocs, explainPlan}) => {
+ const leftContent = edit ? <MangoComponents.MangoIndexEditorController
description={app.i18n.en_US['mango-descripton-index-editor']}
- /> :
- <MangoComponents.MangoQueryEditorController
+ /> : <MangoComponents.MangoQueryEditorController
description={app.i18n.en_US['mango-descripton']}
editorTitle={app.i18n.en_US['mango-title-editor']}
additionalIndexesText={app.i18n.en_US['mango-additional-indexes-heading']}
/>;
+ let resultsPage = <IndexResultsComponents.List designDocs={designDocs} />;
+ let mangoFooter = <MangoFooter />;
+
+ if (explainPlan) {
+ resultsPage = <MangoComponents.ExplainPage explainPlan={explainPlan} />;
+ mangoFooter = null;
+ }
+
return (
<div id="two-pane-content" className="flex-layout flex-row flex-body">
<div id="left-content" className="flex-body">
@@ -77,24 +86,58 @@ const MangoContent = ({edit, designDocs}) => {
</div>
<div id="right-content" className="flex-body flex-layout flex-col">
<div id="dashboard-lower-content" className="flex-body">
- <IndexResultsComponents.List designDocs={designDocs} />
+ {resultsPage}
</div>
- <MangoFooter />
+ {mangoFooter}
</div>
</div>
);
};
+export class MangoLayout extends Component {
+ constructor (props) {
+ super(props);
+ this.state = this.getStoreState();
+ };
-export const MangoLayout = ({edit, docURL, endpoint, crumbs, designDocs}) => {
- return (
- <div id="dashboard" className="two-pane flex-layout flex-col">
- <MangoHeader
- docURL={docURL}
- endpoint={endpoint}
- crumbs={crumbs}
- />
- <MangoContent edit={edit} designDocs={designDocs}/>
- </div>
- );
+ getStoreState () {
+ return {
+ explainPlan: mangoStore.getExplainPlan()
+ };
+ };
+
+ componentDidMount () {
+ mangoStore.on('change', this.onChange, this);
+ };
+
+ componentWillUnmount () {
+ mangoStore.off('change', this.onChange, this);
+ };
+
+ onChange () {
+ this.setState(this.getStoreState());
+ };
+
+ render () {
+ const {database, edit, docURL, crumbs, designDocs} = this.props;
+ let endpoint = this.props.endpoint;
+
+ if (this.state.explainPlan) {
+ endpoint = FauxtonAPI.urls('mango', 'explain-apiurl', database);
+ }
+
+ return (
+ <div id="dashboard" className="two-pane flex-layout flex-col">
+ <MangoHeader
+ docURL={docURL}
+ endpoint={endpoint}
+ crumbs={crumbs}
+ />
+ <MangoContent
+ edit={edit}
+ designDocs={designDocs}
+ explainPlan={this.state.explainPlan} />
+ </div>
+ );
+ }
};
diff --git a/app/addons/documents/pagination/pagination.js
b/app/addons/documents/pagination/pagination.js
index 47ce4b4..8b1298e 100644
--- a/app/addons/documents/pagination/pagination.js
+++ b/app/addons/documents/pagination/pagination.js
@@ -40,7 +40,7 @@ var IndexPaginationController = React.createClass({
// Since we're migrating away from a paginated result list, don't forget
// to delete the cached offset used for an improved UX when switching
// between layouts.
- Actions.deleteCachedOffset();
+ setTimeout(() => Actions.deleteCachedOffset());
},
onChange: function () {
diff --git a/app/addons/documents/resources.js
b/app/addons/documents/resources.js
index ca7d43e..6323711 100644
--- a/app/addons/documents/resources.js
+++ b/app/addons/documents/resources.js
@@ -288,9 +288,9 @@ Documents.MangoDocumentCollection =
PagingCollection.extend({
},
parse: function (resp) {
- var rows = resp.docs;
+ var rows = [];
- this.paging.hasNext = this.paging.hasPrevious = false;
+ rows = resp.docs;
this._warning = resp.warning;
@@ -300,6 +300,8 @@ Documents.MangoDocumentCollection =
PagingCollection.extend({
update_seq: resp.update_seq
};
+ this.paging.hasNext = this.paging.hasPrevious = false;
+
if (this.paging.params.skip > 0) {
this.paging.hasPrevious = true;
}
diff --git a/app/addons/documents/routes-mango.js
b/app/addons/documents/routes-mango.js
index e26711d..16995bf 100644
--- a/app/addons/documents/routes-mango.js
+++ b/app/addons/documents/routes-mango.js
@@ -62,14 +62,6 @@ const MangoIndexEditorAndQueryEditor =
FauxtonAPI.RouteObject.extend({
}
});
- const mangoIndexList = new Resources.MangoIndexCollection(null, {
- database: this.database,
- params: null,
- paging: {
- pageSize: pageSize
- }
- });
-
SidebarActions.selectNavItem('mango-query');
IndexResultsActions.newMangoResultsList({
@@ -79,10 +71,6 @@ const MangoIndexEditorAndQueryEditor =
FauxtonAPI.RouteObject.extend({
bulkCollection: new Resources.BulkDeleteDocCollection([], { databaseId:
this.database.safeID() }),
});
- MangoActions.getIndexList({
- indexList: mangoIndexList
- });
-
const url = FauxtonAPI.urls(
'allDocs', 'app', this.database.safeID(), '?limit=' +
FauxtonAPI.constants.DATABASES.DOCUMENT_LIMIT
);
@@ -93,6 +81,7 @@ const MangoIndexEditorAndQueryEditor =
FauxtonAPI.RouteObject.extend({
];
return <MangoLayout
+ database={database}
crumbs={crumbs}
docURL={FauxtonAPI.constants.DOC_URLS.MANGO_SEARCH}
endpoint={mangoResultCollection.urlRef('query-apiurl', '')}
@@ -125,7 +114,7 @@ const MangoIndexEditorAndQueryEditor =
FauxtonAPI.RouteObject.extend({
IndexResultsActions.newResultsList({
collection: mangoIndexCollection,
bulkCollection: new Resources.MangoBulkDeleteDocCollection([], {
databaseId: this.database.safeID() }),
- typeOfIndex: 'mango'
+ typeOfIndex: 'mango-index'
});
const url = FauxtonAPI.urls(
diff --git a/app/addons/documents/tests/nightwatch/mangoIndex.js
b/app/addons/documents/tests/nightwatch/mangoIndex.js
index b7fa3b1..424466b 100644
--- a/app/addons/documents/tests/nightwatch/mangoIndex.js
+++ b/app/addons/documents/tests/nightwatch/mangoIndex.js
@@ -25,7 +25,6 @@ module.exports = {
.populateDatabase(newDatabaseName)
.loginToGUI()
.url(baseUrl + '/#/database/' + newDatabaseName + '/_index')
- .clickWhenVisible('.fonticon-json')
.waitForElementPresent('.prettyprint', waitTime, false)
.waitForElementNotPresent('.loading-lines', waitTime, false)
.execute('\
diff --git a/app/addons/fauxton/tests/nightwatch/updatesUrlsSameRouteobject.js
b/app/addons/fauxton/tests/nightwatch/updatesUrlsSameRouteobject.js
index 96a347e..39c3ca0 100644
--- a/app/addons/fauxton/tests/nightwatch/updatesUrlsSameRouteobject.js
+++ b/app/addons/fauxton/tests/nightwatch/updatesUrlsSameRouteobject.js
@@ -24,7 +24,6 @@ module.exports = {
.waitForElementVisible('.faux__jsonlink-link', waitTime, false)
.assert.attributeContains('.faux__jsonlink-link', 'href',
newDatabaseName + '/_find')
.clickWhenVisible('.edit-link')
- .clickWhenVisible('.fonticon-json')
.waitForElementVisible('.prettyprint', waitTime, false)
.waitForElementVisible('.faux__jsonlink-link', waitTime, false)
.assert.attributeContains('.faux__jsonlink-link', 'href',
newDatabaseName + '/_index')
diff --git a/assets/less/fauxton.less b/assets/less/fauxton.less
index 31145f0..d2c3e9b 100644
--- a/assets/less/fauxton.less
+++ b/assets/less/fauxton.less
@@ -681,3 +681,7 @@ body .control-toggle-include-docs span {
vertical-align: middle;
}
}
+
+.btn-space {
+ margin-right: 5px;
+}
--
To stop receiving notification emails like this one, please contact
['"[email protected]" <[email protected]>'].