| // 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 React from 'react'; |
| import PropTypes from 'prop-types'; |
| import { Popover, OverlayTrigger } from 'react-bootstrap'; |
| |
| const TOO_MANY_DOCS_SCANNED_WARNING = "The number of documents examined is high in proportion to the number of results returned. Consider adding an index to improve this."; |
| |
| export default class ExecutionStats extends React.Component { |
| constructor (props) { |
| super(props); |
| } |
| |
| humanizeDuration(milliseconds) { |
| if (milliseconds < 1000) { |
| return Math.round(milliseconds) + ' ms'; |
| } |
| let seconds = milliseconds / 1000; |
| if (seconds < 60) { |
| return Math.floor(seconds) + ' seconds'; |
| } |
| const minutes = Math.floor(seconds / 60); |
| seconds = seconds - (minutes * 60); |
| |
| return minutes + 'minute, ' + seconds + 'seconds'; |
| } |
| |
| getWarning(executionStats, warning) { |
| if (!executionStats) { return warning; } |
| |
| // warn if many documents scanned in relation to results returned |
| if (!warning && executionStats.results_returned) { |
| const docsExamined = executionStats.total_docs_examined || executionStats.total_quorum_docs_examined; |
| if (docsExamined / executionStats.results_returned > 10) { |
| return TOO_MANY_DOCS_SCANNED_WARNING; |
| } |
| } |
| |
| return warning; |
| } |
| |
| warningPopupComponent(warningText) { |
| if (warningText) { |
| return (<div className="warning"> |
| <i className="fonticon-attention-circled"></i> {warningText} |
| </div>); |
| } |
| } |
| |
| executionStatsLine(title, value, alwaysShow = false, units = "") { |
| const hasValue = value === 0 && !alwaysShow ? "false" : "true"; |
| return <div data-status={hasValue}>{title + ": "}<span className="value">{value.toLocaleString()} {units}</span></div>; |
| } |
| |
| executionStatsPopupComponent(executionStats) { |
| if (!executionStats) return null; |
| return ( |
| <div className="execution-stats-popup-component"> |
| {/* keys examined always 0 so hide it for now */} |
| {/* {this.executionStatsLine("keys examined", executionStats.total_keys_examined)} */} |
| {this.executionStatsLine("documents examined", executionStats.total_docs_examined)} |
| {this.executionStatsLine("documents examined (quorum)", executionStats.total_quorum_docs_examined)} |
| {this.executionStatsLine("results returned", executionStats.results_returned, true)} |
| {this.executionStatsLine("execution time", executionStats.execution_time_ms, false, "ms")} |
| </div> |
| ); |
| } |
| |
| popup(executionStats, warningText) { |
| return ( |
| <Popover id="popover-execution-stats" title="Execution Statistics"> |
| <div className="execution-stats-popup"> |
| {this.executionStatsPopupComponent(executionStats)} |
| {this.warningPopupComponent(warningText)} |
| </div> |
| </Popover> |
| ); |
| } |
| |
| render() { |
| const { |
| executionStats, |
| warning |
| } = this.props; |
| |
| const warningText = this.getWarning(executionStats, warning); |
| |
| let warningComponent = null; |
| if (warningText) { |
| warningComponent = <i className="fonticon-attention-circled"></i>; |
| } |
| |
| let executionStatsComponent = null; |
| if (executionStats) { |
| executionStatsComponent = ( |
| <span className="execution-stats-component">Executed in {this.humanizeDuration(executionStats.execution_time_ms)}</span> |
| ); |
| } else if (warningText) { |
| executionStatsComponent = ( |
| <span className="execution-stats-component">Warning</span> |
| ); |
| } |
| |
| const popup = this.popup(executionStats, warningText); |
| return ( |
| <OverlayTrigger trigger={['hover', 'focus', 'click']} placement="right" overlay={popup}> |
| <span className="execution-stats"> |
| {warningComponent} |
| {executionStatsComponent} |
| </span> |
| </OverlayTrigger> |
| ); |
| } |
| } |
| |
| ExecutionStats.propTypes = { |
| executionStats: PropTypes.object, |
| warning: PropTypes.string |
| }; |