UNPKG

labo-components

Version:
199 lines (175 loc) 7.67 kB
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 };