labo-components
Version:
199 lines (175 loc) • 7.67 kB
JSX
import React from 'react';
import PropTypes from "prop-types";
import IDUtil from "../../util/IDUtil";
import QueryInfoBlock from "./helpers/QueryInfoBlock";
import QueryComparisonLineChart from "./QueryComparisonLineChart";
import ComparisonHistogram from "./ComparisonHistogram";
import CollectionUtil from "../../util/CollectionUtil";
import SearchAPI from "../../api/SearchAPI";
import Query from "../../model/Query";
import ComponentUtil from "../../util/ComponentUtil";
import ElasticsearchDataUtil from "../../util/ElasticsearchDataUtil";
import Loading from "../shared/Loading";
export default class QueriesTimelinePlotter extends React.Component {
constructor(props) {
super(props);
this.state = {
chartType : 'histogram',
//all populated by processQueryInput()
queryStats: null,
lineChartData: null, //contains all the retrieved stats per queryId (for all queries)
barChartData: null,
namedQueries: null,
isLoading : false
}
this.COLORS = [
'darkturquoise', 'goldenrod', 'deeppink', 'cadetblue', 'crimson',
'lime', 'violet', 'seagreen', 'yellowgreen', 'dodgerblue', 'mediumpurple',
'blue', 'lightcoral', 'olivedrab', 'sienna', 'gold', 'darkslategrey'
];
}
componentDidMount = () => {
this.processQueryInput(this.props.namedQueries);
};
switchGraphType = () => this.setState(
{chartType: this.state.chartType === 'lineChart' ? 'histogram' : 'lineChart'}
);
/* -------- FUNCTIONS THAT GENERATE UI DATA BASED ON SELECTED QUERIES ------------- */
processQueryInput = namedQueries => {
if(namedQueries && namedQueries.length > 0) {
this.setState(
{isLoading : true},
() => {
this.__fetchData(namedQueries).then(
data => this.__generateChartData(data, namedQueries)
);
}
)
} else {
this.setState({queryStats: null, lineChartData: null, barChartData: null, namedQueries: null});
}
};
__fetchData = async (namedQueries) => await Promise.all(
namedQueries.filter(q => q.query).map(q => this.__getData(q))
);
__getData = namedQuery => new Promise((resolve, reject) => {
CollectionUtil.generateCollectionConfig(
this.props.clientId, this.props.user, namedQuery.query.collectionId, (collectionConfig) => {
const dateField = collectionConfig.getPreferredDateField() || null;
if (namedQuery.query.dateRange == null && dateField !== null) {
namedQuery.query.desiredFacets.push({
"field": dateField,
"id": dateField,
"title": collectionConfig.toPrettyFieldName(dateField),
"type": "date_histogram"
});
}
SearchAPI.search(
Query.construct(namedQuery.query, collectionConfig),
collectionConfig,
data => {
//just resolve everything, even if the data is null or has errors. Handle this later
resolve(data);
},
false
)
});
}).catch(err => console.log('No data returned from query', err));
__generateChartData = (resultsObjects, namedQueries) => {
const queryStats = {}; //keep status information for the QueryInfoBlock
resultsObjects.forEach((resultsObj, index) => {
queryStats[resultsObj.searchId] = {
hasDateInformation: this.__hasDateInformation(resultsObj),
error: !resultsObj || resultsObj.error ? true : false,
totalHits: resultsObj ? resultsObj.totalHits || 0 : 0,
collectionConfig: resultsObj && resultsObj.collectionConfig ? resultsObj.collectionConfig : null,
color: this.COLORS[index] || 'black',
queryIndex: index + 1,
searchId: resultsObj.searchId
};
// Filter weird dates from returned query.
ComponentUtil.filterWeirdDates(
resultsObj.aggregations,
resultsObj.query.dateRange,
resultsObj.collectionConfig
);
});
const barChartData = resultsObjects.filter(this.__hasDateInformation);
const lineChartData = {};
barChartData.forEach(resultsObj => {
if (resultsObj && resultsObj.query) {
const timelineData = ElasticsearchDataUtil.searchResultsToTimeLineData(
resultsObj.searchId,
resultsObj.query,
resultsObj.aggregations
);
if (timelineData) { //avoid feeding the graph with null data
lineChartData[resultsObj.searchId] = {
data: timelineData,
comparisonId: resultsObj.searchId,
query: resultsObj.query,
collectionConfig: resultsObj.collectionConfig
};
}
}
});
this.setState({
queryStats: queryStats,
lineChartData: barChartData,
barChartData: barChartData,
namedQueries: namedQueries,
isLoading : false
});
};
__hasDateInformation = item => {
if (item.query && item.aggregations) {
if (item.query.dateRange && item.query.dateRange.field) {
return item.aggregations[item.query.dateRange.field] != null && item.aggregations[item.query.dateRange.field].length > 0
}
}
return false;
};
/* ----------------------- RENDER FUNCTIONS -------------------------- */
render() {
const queryInfoBlock = this.state.queryStats ?
<QueryInfoBlock queries={this.props.namedQueries} queryStats={this.state.queryStats}/> :
null
;
let graphTypeBtn = null;
let chart = null;
const loadingMsg = this.state.isLoading ? <Loading message="Loading query graph..."/> : null;
if (this.state.lineChartData && Object.keys(this.state.lineChartData).length > 0) {
graphTypeBtn = (
<button
onClick={this.switchGraphType}
type="button"
className="switch-view-btn">
{this.state.chartType === 'lineChart' ? 'Histogram' : 'Line Chart'}
</button>
);
chart = this.state.chartType === 'lineChart' ?
<QueryComparisonLineChart data={this.state.lineChartData} queryStats={this.state.queryStats}/> :
<ComparisonHistogram data={this.state.barChartData} queryStats={this.state.queryStats}/>
;
}
return (
<div className={IDUtil.cssClassName('query-timeline-plotter')}>
{loadingMsg}
{graphTypeBtn}
{chart}
{queryInfoBlock}
</div>
)
};
}
QueriesTimelinePlotter.propTypes = {
clientId : PropTypes.string.isRequired, //should be part of a context object
user: PropTypes.shape({ //should be part of a context object
id: PropTypes.string.isRequired,
name: PropTypes.string,
attributes: PropTypes.shape({
allowPersonalCollections: PropTypes.bool
})
}).isRequired,
namedQueries: PropTypes.array.isRequired
};