garrensmith closed pull request #716: [WIP] Query explorer new
URL: https://github.com/apache/couchdb-fauxton/pull/716
 
 
   

This is a PR merged from a forked repository.
As GitHub hides the original diff on merge, it is displayed below for
the sake of provenance:

As this is a foreign pull request (from a fork), the diff is supplied
below (as it won't show otherwise due to GitHub magic):

diff --git a/app/addons/documents/mango/mango.actions.js 
b/app/addons/documents/mango/mango.actions.js
index 56c57262b..8bd6a34cc 100644
--- a/app/addons/documents/mango/mango.actions.js
+++ b/app/addons/documents/mango/mango.actions.js
@@ -20,7 +20,7 @@ define([
   '../index-results/actions',
 ],
 function (app, FauxtonAPI, Documents, ActionTypes, Stores, IndexResultsStores, 
IndexResultActions) {
-  var store = Stores.mangoStore;
+  const store = Stores.mangoStore;
 
   return {
 
@@ -31,6 +31,27 @@ function (app, FauxtonAPI, Documents, ActionTypes, Stores, 
IndexResultsStores, I
       });
     },
 
+    addSelector: (options) => {
+      FauxtonAPI.dispatch({
+        type: ActionTypes.MANGO_BUILDER_ADD_SELECTOR,
+        options: options
+      });
+    },
+
+    removeSelector: (options) => {
+      FauxtonAPI.dispatch({
+        type: ActionTypes.MANGO_BUILDER_REMOVE_SELECTOR,
+        options: options
+      });
+    },
+
+    setLogicalOperator: (options) => {
+      FauxtonAPI.dispatch({
+        type: ActionTypes.MANGO_BUILDER_SET_LOGICAL_OPERATOR,
+        options: options
+      });
+    },
+
     newQueryFindCode: function (options) {
       FauxtonAPI.dispatch({
         type: ActionTypes.MANGO_NEW_QUERY_FIND_CODE,
diff --git a/app/addons/documents/mango/mango.actiontypes.js 
b/app/addons/documents/mango/mango.actiontypes.js
index b6bdbcc45..dbe91d5bb 100644
--- a/app/addons/documents/mango/mango.actiontypes.js
+++ b/app/addons/documents/mango/mango.actiontypes.js
@@ -10,13 +10,16 @@
 // License for the specific language governing permissions and limitations 
under
 // the License.
 
-define([], function () {
+define([], () => {
   return {
     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_RESET: 'MANGO_RESET',
+    MANGO_BUILDER_ADD_SELECTOR: 'MANGO_BUILDER_ADD_SELECTOR',
+    MANGO_BUILDER_REMOVE_SELECTOR: 'MANGO_BUILDER_REMOVE_SELECTOR',
+    MANGO_BUILDER_SET_LOGICAL_OPERATOR: 'MANGO_BUILDER_SET_LOGICAL_OPERATOR'
   };
 });
diff --git a/app/addons/documents/mango/mango.components.react.jsx 
b/app/addons/documents/mango/mango.components.react.jsx
index e88a05bf6..68ac6243a 100644
--- a/app/addons/documents/mango/mango.components.react.jsx
+++ b/app/addons/documents/mango/mango.components.react.jsx
@@ -19,21 +19,325 @@ define([
   '../../components/react-components.react',
   '../index-results/actions',
   './mango.helper',
+  'react-bootstrap',
 
   '../../../../assets/js/plugins/prettify'
 ],
 
-function (app, FauxtonAPI, React, Stores, Actions,
-          ReactComponents, IndexResultActions, MangoHelper) {
+ (app, FauxtonAPI, React, Stores, Actions,
+  ReactComponents, IndexResultActions, MangoHelper, ReactBootstrap) => {
 
-  var mangoStore = Stores.mangoStore;
-  var getDocUrl = app.helpers.getDocUrl;
+  const mangoStore = Stores.mangoStore;
+  const getDocUrl = app.helpers.getDocUrl;
 
-  var PaddedBorderedBox = ReactComponents.PaddedBorderedBox;
-  var CodeEditorPanel = ReactComponents.CodeEditorPanel;
-  var ConfirmButton = ReactComponents.ConfirmButton;
+  const PaddedBorderedBox = ReactComponents.PaddedBorderedBox;
+  const CodeEditorPanel = ReactComponents.CodeEditorPanel;
+  const ConfirmButton = ReactComponents.ConfirmButton;
+  const StyledSelect = ReactComponents.StyledSelect;
+  const BadgeList = ReactComponents.BadgeList;
 
-  var MangoQueryEditorController = React.createClass({
+  const ButtonGroup = ReactBootstrap.ButtonGroup;
+  const Button = ReactBootstrap.Button;
+
+
+  const OperatorSelector = ({possibleOperators}) => {
+    const options = possibleOperators.map((el, i) => {
+      return <option key={i} value={el.operator}>{el.text}</option>;
+    });
+
+    return (
+      <optgroup label="Select a selector">
+        {options}
+      </optgroup>
+    );
+  };
+
+  OperatorSelector.propTypes = {
+    possibleOperators: React.PropTypes.array.isRequired,
+    selected: React.PropTypes.string
+  };
+
+  class MangoQueryBuilderController extends React.Component {
+
+    constructor (props) {
+      super(props);
+
+      this.state = this.getStoreState();
+      this.state.operator = '$eq';
+    }
+
+    getStoreState () {
+      return {
+        database: mangoStore.getDatabase(),
+        isLoading: mangoStore.getLoadingIndexes(),
+
+        possibleOperators: mangoStore.getPossibleOperators(),
+        query: mangoStore.getStringifiedQuery(),
+        queryParts: mangoStore.getSelectors(),
+        builtQuery: mangoStore.getBuiltQuery(),
+        logicOperator: mangoStore.getLogicOperator(),
+      };
+    }
+
+    componentDidMount () {
+      mangoStore.on('change', this.onChange, this);
+    }
+
+    componentWillUnmount () {
+      mangoStore.off('change', this.onChange);
+    }
+
+    onChange () {
+      this.setState(this.getStoreState());
+    }
+
+    getMangoQueryBuilder () {
+      return this.refs.mangoQueryBuilder;
+    }
+
+    runQuery (e) {
+      e.preventDefault();
+
+      IndexResultActions.runMangoFindQuery({
+        database: this.state.database,
+        queryCode: this.getMangoQueryBuilder().getEditorValue()
+      });
+    }
+
+    render () {
+      //if (this.state.isLoading) {
+      //  return (
+      //    <LoadingEditor />
+      //  );
+      //}
+
+      // XXX splat other state
+      const {database} = this.state;
+
+      return (
+        <MangoQueryBuilder
+          {...this.state}
+          ref="mangoQueryBuilder"
+          description="Start building an ad hoc query below."
+          dbName={database.id}
+          onSubmit={this.runQuery.bind(this)}
+          title={this.props.editorTitle}
+          docs={getDocUrl('MANGO_SEARCH')}
+          exampleCode=""
+          confirmbuttonText="Run Query" />
+      );
+    }
+  };
+
+  class MangoQueryBuilder extends React.Component {
+
+    constructor (props) {
+      super(props);
+
+      this.state = {
+        field: '',
+        fieldValue: '',
+        queryParts: [],
+        operator: '$eq',
+        logicOperator: '$and'
+      };
+    }
+
+    selectChange (e) {
+      this.setState({operator: e.target.value});
+    }
+
+    fieldChange (e) {
+      this.setState({field: e.target.value});
+    }
+
+    fieldValueChange (e) {
+      this.setState({fieldValue: e.target.value});
+    }
+
+    toggleLogicalOperator (op) {
+      this.setState({logicOperator: op});
+      Actions.setLogicalOperator({logicOperator: op});
+    }
+
+    maybeAddSelectorKeyUp (e) {
+      if (e.keyCode !== 13) {
+        return;
+      }
+
+      if (!this.state.field) {
+        return;
+      }
+
+      if (!this.state.fieldValue) {
+        return;
+      }
+
+      this.addSelector();
+    }
+
+    addSelector () {
+      const selector = {
+        field: this.state.field,
+        operator: this.state.operator,
+        fieldValue: this.state.fieldValue
+      };
+
+      Actions.addSelector(selector);
+
+      this.setState({
+        field: '',
+        operator: '$eq',
+        fieldValue: ''
+      });
+
+    }
+
+    getSelectOptions () {
+      return (
+        <OperatorSelector possibleOperators={this.props.possibleOperators} />
+      );
+    }
+
+    removeSelector (label, selector) {
+      Actions.removeSelector(selector);
+    }
+
+    componentWillUpdate (nextProps, nextState) {
+      if (!nextProps.query) {
+        return;
+      }
+      this.refs.field.getEditor().setValue(nextProps.query);
+    }
+
+    getDataFields () {
+      return (
+        <PaddedBorderedBox>
+          <table className="mango-qb-selector-combobox">
+            <tbody>
+              <tr>
+                <td>
+                  Field
+                </td>
+                <td>
+                  <input
+                    onChange={this.fieldChange.bind(this)}
+                    onKeyUp={this.maybeAddSelectorKeyUp.bind(this)}
+                    value={this.state.field}
+                    type="text"
+                    className="mango-select-value"
+                    ref="mango-select-field" />
+                </td>
+              </tr>
+              <tr>
+                <td>
+                  Function
+                </td>
+                <td>
+                <StyledSelect
+                  selectValue={this.state.operator}
+                  selectId="mango-select-function"
+                  className={""}
+                  selectChange={this.selectChange.bind(this)}
+                  selectContent={this.getSelectOptions()} />
+                </td>
+              </tr>
+              <tr>
+                <td>
+                  Value
+                </td>
+                <td>
+                  <input
+                    onChange={this.fieldValueChange.bind(this)}
+                    onKeyUp={this.maybeAddSelectorKeyUp.bind(this)}
+                    value={this.state.fieldValue}
+                    type="text"
+                    className="mango-select-value"
+                    ref="mango-select-value" />
+                </td>
+                <td>
+                  <ConfirmButton onClick={this.addSelector.bind(this)} 
text="Add" />
+                </td>
+              </tr>
+            </tbody>
+          </table>
+
+          <div style={{textAlign: 'center'}} 
className="mango-selector-display">
+            <div className="button-wrapper">
+              <ButtonGroup className="two-sides-toggle-button">
+                <Button
+                  style={{width: '58px'}}
+                  className={this.state.logicOperator === '$and' ? 'active' : 
''}
+                  onClick={this.toggleLogicalOperator.bind(this, '$and')}
+                >
+                  And
+                </Button>
+                <Button
+                  style={{width: '58px'}}
+                  className={this.state.logicOperator === '$and' ? '' : 
'active'}
+                  onClick={this.toggleLogicalOperator.bind(this, '$or')}
+                >
+                  Or
+                </Button>
+              </ButtonGroup>
+            </div>
+          </div>
+
+          <div style={{minHeight: '40px'}}>
+            <BadgeList
+              style={{height: '100px'}}
+              removeBadge={this.removeSelector.bind(this)}
+              elements={this.props.queryParts}
+              getLabel={this.getLabel.bind(this)} />
+          </div>
+        </PaddedBorderedBox>
+      );
+    }
+
+    getLabel (selector) {
+      return `${selector.field} : ${selector.fieldValue}`;
+    }
+
+    getPreviewField () {
+      return (
+        <PaddedBorderedBox>
+          <CodeEditorPanel
+            id="query-field"
+            allowZenMode={false}
+            ref="field"
+            title={""}
+            docLink={"this.props.docs"}
+            defaultCode={this.props.query} />
+        </PaddedBorderedBox>
+      );
+    }
+
+    render () {
+      return (
+        <div className="mango-editor-wrapper">
+          <PaddedBorderedBox>
+            <div
+              dangerouslySetInnerHTML={{__html: this.props.description}}
+              className="editor-description">
+            </div>
+          </PaddedBorderedBox>
+          {this.getDataFields()}
+          {this.getPreviewField()}
+        </div>
+      );
+    }
+  };
+
+  const LoadingEditor = () => {
+    return (
+      <div className="editor-wrapper">
+        <ReactComponents.LoadLines />
+      </div>
+    );
+  };
+
+
+  const MangoQueryEditorController = React.createClass({
     getInitialState: function () {
       return this.getStoreState();
     },
@@ -71,12 +375,9 @@ function (app, FauxtonAPI, React, Stores, Actions,
     },
 
     render: function () {
-      var loadLines;
       if (this.state.isLoading) {
         return (
-          <div className="mango-editor-wrapper">
-            <ReactComponents.LoadLines />
-          </div>
+          <LoadingEditor />
         );
       }
 
@@ -102,7 +403,7 @@ function (app, FauxtonAPI, React, Stores, Actions,
 
       if (this.getMangoEditor().hasErrors()) {
         FauxtonAPI.addNotification({
-          msg:  'Please fix the Javascript errors and try again.',
+          msg: 'Please fix the Javascript errors and try again.',
           type: 'error',
           clear: true
         });
@@ -112,7 +413,6 @@ function (app, FauxtonAPI, React, Stores, Actions,
       IndexResultActions.runMangoFindQuery({
         database: this.state.database,
         queryCode: this.getMangoEditor().getEditorValue(),
-
       });
     }
   });
@@ -269,7 +569,7 @@ function (app, FauxtonAPI, React, Stores, Actions,
 
       if (this.getMangoEditor().hasErrors()) {
         FauxtonAPI.addNotification({
-          msg:  'Please fix the Javascript errors and try again.',
+          msg: 'Please fix the Javascript errors and try again.',
           type: 'error',
           clear: true
         });
@@ -283,9 +583,10 @@ function (app, FauxtonAPI, React, Stores, Actions,
     }
   });
 
-  var Views = {
+  const Views = {
     MangoIndexEditorController: MangoIndexEditorController,
-    MangoQueryEditorController: MangoQueryEditorController
+    MangoQueryEditorController: MangoQueryEditorController,
+    MangoQueryBuilderController: MangoQueryBuilderController
   };
 
   return Views;
diff --git a/app/addons/documents/mango/mango.stores.js 
b/app/addons/documents/mango/mango.stores.js
index 42c4966e5..e1d94355f 100644
--- a/app/addons/documents/mango/mango.stores.js
+++ b/app/addons/documents/mango/mango.stores.js
@@ -17,21 +17,42 @@ define([
 
 function (FauxtonAPI, ActionTypes) {
 
+  const operators = [
+    {operator: '$eq', text: 'Equals / is'},
+    {operator: '$gt', text: 'Greater than'},
 
-  var defaultQueryIndexCode = {
+    {operator: '$lt', text: 'Less than'},
+    {operator: '$gte', text: 'Greater than or equal'},
+    {operator: '$lte', text: 'Less than or equal'},
+    {operator: '$ne', text: 'Not equal'},
+
+    {operator: '$exists', text: 'Exists'},
+    {operator: '$type', text: 'Type of'},
+
+    {operator: '$in', text: 'In Array'},
+    {operator: '$nin', text: 'Not in Array'},
+
+    {operator: '$mod', text: 'Devisor / Remainder'},
+    {operator: '$regex', text: 'Regular expression'},
+
+  ];
+
+  const emptyQuery = {selector: {}};
+
+  const defaultQueryIndexCode = {
     "index": {
       "fields": ["_id"]
     },
     "type" : "json"
   };
 
-  var defaultQueryFindCode = {
+  const defaultQueryFindCode = {
     "selector": {
       "_id": {"$gt": null}
     }
   };
 
-  var Stores = {};
+  const Stores = {};
 
   Stores.MangoStore = FauxtonAPI.Store.extend({
 
@@ -41,6 +62,152 @@ function (FauxtonAPI, ActionTypes) {
       this._queryFindCodeChanged = false;
       this._availableIndexes = [];
       this._getLoadingIndexes = true;
+
+      this._queryParts = [];
+      this._logicOperator = '$and';
+    },
+
+    getPossibleOperators: function () {
+      return operators;
+    },
+
+    getSelectors: function () {
+      return this._queryParts;
+    },
+
+    isSelectorValid: function (selector) {
+      if (!selector.fieldValue) {
+        return false;
+      }
+
+      if (!selector.field) {
+        return false;
+      }
+
+      return true;
+    },
+
+    addSelector: function (selector) {
+      if (!this.isSelectorValid(selector)) {
+        return;
+      }
+
+      this.removeSelector(selector);
+
+      this._queryParts = this._queryParts.concat([selector]);
+
+      return this._queryParts;
+    },
+
+    removeSelector: function (selector) {
+
+      this._queryParts = this._queryParts.reduce((acc, el) => {
+        if (el.field === selector.field
+          && el.operator === selector.operator
+          && el.fieldValue === selector.fieldValue) {
+
+          return acc;
+        }
+
+        acc.push(el);
+
+        return acc;
+      }, []);
+
+      return this._queryParts;
+    },
+
+    getEmptyQuery: function () {
+      return JSON.parse(JSON.stringify(emptyQuery));
+    },
+
+    getStringifiedQuery: function () {
+      return this.formatCode(this.getQuery());
+    },
+
+    getQuery: function () {
+      if (!this._queryParts.length) {
+        return this.getEmptyQuery();
+      }
+
+      return this.buildQuery(this._queryParts);
+    },
+
+    setLogicOperator: function (options) {
+      this._logicOperator = options.logicOperator;
+    },
+
+    getLogicOperator: function () {
+      return this._logicOperator;
+    },
+
+
+
+
+    buildQuery: function (parts) {
+      const wrapper = this.getEmptyQuery();
+
+/*
+    {
+  "selector": {
+    "year": {
+      "$eq": 2001
+    }
+  },
+  "sort": [
+    "year"
+  ],
+  "fields": [
+    "year"
+  ]
+}
+*/
+      let res;
+      if (parts.length <= 1) {
+        res = this.getQueryForSingleSelector(parts);
+      } else {
+        res = this.getQueryWithLogicalOperator(parts);
+      }
+
+      wrapper.selector = res;
+
+      return wrapper;
+    },
+
+    getQueryWithLogicalOperator: function (parts) {
+      // XXX $text handling
+      // test on JSON parsing
+      // 1. tests, $text handling
+      const res = parts.reduce((acc, el) => {
+
+        let tmp;
+        try {
+          tmp = {[el.field]: {[el.operator]: JSON.parse(el.fieldValue)}};
+        } catch (e) {
+          tmp = {[el.field]: {[el.operator]: el.fieldValue}};
+        }
+
+        acc.push(tmp);
+
+        return acc;
+      }, []);
+
+      return {[this._logicOperator]: res};
+    },
+
+    getQueryForSingleSelector: function (parts) {
+      // XXX $text handling
+      // 1. tests, $text handling
+      return parts.reduce((acc, el) => {
+
+        try {
+          acc[el.field] = {[el.operator]: JSON.parse(el.fieldValue)};
+        } catch (e) {
+          acc[el.field] = {[el.operator]: el.fieldValue};
+        }
+
+        return acc;
+      }, {});
     },
 
     getQueryIndexCode: function () {
@@ -51,6 +218,14 @@ function (FauxtonAPI, ActionTypes) {
       this._queryIndexCode = options.code;
     },
 
+    getBuiltQuery: function () {
+      return {
+        "selector": {
+          "_id": {"$gt": null}
+        }
+      };
+    },
+
     getQueryFindCode: function () {
       return this.formatCode(this._queryFindCode);
     },
@@ -72,15 +247,14 @@ function (FauxtonAPI, ActionTypes) {
     },
 
     newQueryFindCodeFromFields: function (options) {
-      var fields = options.fields,
-          queryCode = JSON.parse(JSON.stringify(this._queryFindCode)),
-          selectorContent;
+      const fields = options.fields;
+      const queryCode = JSON.parse(JSON.stringify(this._queryFindCode));
 
       if (!fields) {
         return;
       }
 
-      selectorContent = fields.reduce(function (acc, field) {
+      const selectorContent = fields.reduce(function (acc, field) {
         acc[field] = {"$gt": null};
         return acc;
       }, {});
@@ -150,6 +324,18 @@ function (FauxtonAPI, ActionTypes) {
         case ActionTypes.MANGO_RESET:
           this.setLoadingIndexes(action.options);
         break;
+
+        case ActionTypes.MANGO_BUILDER_ADD_SELECTOR:
+          this.addSelector(action.options);
+        break;
+
+        case ActionTypes.MANGO_BUILDER_REMOVE_SELECTOR:
+          this.removeSelector(action.options);
+        break;
+
+        case ActionTypes.MANGO_BUILDER_SET_LOGICAL_OPERATOR:
+          this.setLogicOperator(action.options);
+        break;
       }
 
       this.triggerChange();
diff --git a/app/addons/documents/mango/tests/mango.storesSpec.js 
b/app/addons/documents/mango/tests/mango.storesSpec.js
index 5a2e5993c..ed8d7695a 100644
--- a/app/addons/documents/mango/tests/mango.storesSpec.js
+++ b/app/addons/documents/mango/tests/mango.storesSpec.js
@@ -17,12 +17,79 @@ define([
   '../../resources',
   '../../../../../test/mocha/testUtils',
 
-], function (FauxtonAPI, Stores, ActionTypes, Resources, testUtils) {
-  var assert = testUtils.assert;
-  var dispatchToken;
-  var store;
+], (FauxtonAPI, Stores, ActionTypes, Resources, testUtils) => {
+  const assert = testUtils.assert;
 
-  describe('Mango Store', function () {
+  let dispatchToken;
+  let store;
+
+  describe('Mango Store', () => {
+
+    describe('queryBuilder', () => {
+      beforeEach(() => {
+        store = new Stores.MangoStore();
+        dispatchToken = FauxtonAPI.dispatcher.register(store.dispatch);
+      });
+
+      afterEach(() => {
+        FauxtonAPI.dispatcher.unregister(dispatchToken);
+      });
+
+      it('adds selectors', () => {
+        store.addSelector({field: 'name', operator: '$eq', fieldValue: 'John 
Rambo'});
+
+        assert.deepEqual([{field: 'name', operator: '$eq', fieldValue: 'John 
Rambo'}], store.getSelectors());
+      });
+
+      it('is not possible to add the same selector twice', () => {
+        store.addSelector({field: 'name', operator: '$eq', fieldValue: 'John 
Rambo'});
+        store.addSelector({field: 'name', operator: '$eq', fieldValue: 'John 
Rambo'});
+
+        assert.equal(1, store.getSelectors().length);
+      });
+
+      it('is not possible to add incomplete selectors: missing field', () => {
+        store.addSelector({operator: '$eq', fieldValue: 'John Rambo'});
+
+        assert.equal(0, store.getSelectors().length);
+      });
+
+      it('is not possible to add incomplete selectors: missing fieldValue', () 
=> {
+        store.addSelector({field: 'name', operator: '$eq'});
+
+        assert.equal(0, store.getSelectors().length);
+      });
+
+      it('removes selectors', () => {
+        store.addSelector({field: 'name', operator: '$eq', fieldValue: 'John 
Rambo'});
+        store.removeSelector({field: 'name', operator: '$eq', fieldValue: 
'John Rambo'});
+
+        assert.equal(0, store.getSelectors().length);
+      });
+
+      it('does not remove selectors that dont match', () => {
+        store.addSelector({field: 'name', operator: '$eq', fieldValue: 'John 
Rambo'});
+        store.removeSelector({field: 'name', operator: '$ne', fieldValue: 
'John Rambo'});
+
+        assert.equal(1, store.getSelectors().length);
+      });
+
+      it('returns an empty selectorset if no selectors were added', () => {
+        assert.deepEqual(store.getEmptyQuery(), store.getQuery());
+      });
+
+      describe('buildQuery', () => {
+        it('builds queries for $eq operators', () => {
+          const selectorList = [{field: 'name', operator: '$eq', fieldValue: 
'John Rambo'}];
+
+          const res = store.buildQuery(selectorList);
+
+          assert.deepEqual({selector: {name: 'John Rambo'}}, res);
+        });
+
+      });
+
+    });
 
     describe('getQueryCode', function () {
 
diff --git a/app/addons/documents/routes-mango.js 
b/app/addons/documents/routes-mango.js
index ee11d3441..fcbf9ac8a 100644
--- a/app/addons/documents/routes-mango.js
+++ b/app/addons/documents/routes-mango.js
@@ -41,7 +41,7 @@ function (app, FauxtonAPI, Helpers, BaseRoute, Databases,
   ReactHeader, ReactActions, ReactPagination,
   MangoComponents, MangoActions, MangoStores, IndexResultsComponents, 
SidebarActions) {
 
-  var MangoIndexEditorAndQueryEditor = BaseRoute.extend({
+  const MangoIndexEditorAndQueryEditor = BaseRoute.extend({
     layout: 'two_pane',
     routes: {
       'database/:database/_index': {
@@ -52,6 +52,10 @@ function (app, FauxtonAPI, Helpers, BaseRoute, Databases,
         route: 'findUsingIndex',
         roles: ['fx_loggedIn']
       },
+      'database/:database/_query_builder': {
+        route: 'buildQuery',
+        roles: ['fx_loggedIn']
+      },
     },
 
     initialize: function (route, masterLayout, options) {
@@ -69,6 +73,48 @@ function (app, FauxtonAPI, Helpers, BaseRoute, Databases,
       });
     },
 
+    buildQuery: function (database) {
+      const params = this.createParams();
+      const urlParams = params.urlParams;
+      const mangoResultCollection = new 
Resources.MangoDocumentCollection(null, {
+        database: this.database,
+        paging: {
+          pageSize: IndexResultStores.indexResultsStore.getPerPage()
+        }
+      });
+
+      const mangoIndexList = new Resources.MangoIndexCollection(null, {
+        database: this.database,
+        params: null,
+        paging: {
+          pageSize: IndexResultStores.indexResultsStore.getPerPage()
+        }
+      });
+
+      SidebarActions.selectNavItem('mango-query');
+
+      this.setComponent('#react-headerbar', 
ReactHeader.BulkDocumentHeaderController);
+      this.setComponent('#footer', ReactPagination.Footer);
+
+      IndexResultsActions.newMangoResultsList({
+        collection: mangoResultCollection,
+        textEmptyIndex: 'No Results',
+        bulkCollection: Documents.BulkDeleteDocCollection
+      });
+
+
+      this.setComponent('#dashboard-lower-content', 
IndexResultsComponents.List);
+
+      this.setComponent('#left-content', 
MangoComponents.MangoQueryBuilderController);
+
+      this.apiUrl = () => {
+        return [
+          mangoResultCollection.urlRef('query-apiurl', ''),
+          FauxtonAPI.constants.DOC_URLS.MANGO_SEARCH
+        ];
+      };
+    },
+
     findUsingIndex: function () {
       var params = this.createParams(),
           urlParams = params.urlParams,
@@ -100,7 +146,7 @@ function (app, FauxtonAPI, Helpers, BaseRoute, Databases,
         indexList: mangoIndexList
       });
 
-      var url = FauxtonAPI.urls('allDocs', 'app', this.database.safeID(), 
'?limit=' + FauxtonAPI.constants.DATABASES.DOCUMENT_LIMIT);
+      const url = FauxtonAPI.urls('allDocs', 'app', this.database.safeID(), 
'?limit=' + FauxtonAPI.constants.DATABASES.DOCUMENT_LIMIT);
       this.breadcrumbs = this.setView('#breadcrumbs', new 
Components.Breadcrumbs({
         toggleDisabled: true,
         crumbs: [


 

----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on GitHub and use the
URL above to go to the specific comment.
 
For queries about this service, please contact Infrastructure at:
[email protected]


With regards,
Apache Git Services

Reply via email to