blob: 01f5d27db9dc2a6a21cfa1f20c53b1c8fe141add [file] [log] [blame]
// 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
};