Skip to content

Commit 177593b

Browse files
authored
Merge pull request #1986 from colinl/1977_categorical_xaxis_remove_old_data
Improve historical chart data management
2 parents 9f365cd + e0724f0 commit 177593b

3 files changed

Lines changed: 58 additions & 8 deletions

File tree

nodes/store/data.js

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,22 @@ const setters = {
102102
}
103103
data[node.id].push(config.RED.util.cloneMessage(msg))
104104
}
105+
},
106+
/**
107+
* Fast filtering of existing array data (skips cloning and save checks for fast data cleanup)
108+
* @param {*} base - the base node
109+
* @param {*} node - the owner node
110+
* @param {(msg) => Boolean} filterFunction
111+
*/
112+
filter (base, node, filterFunction) {
113+
const currentData = data[node.id]
114+
if (filterFunction && Array.isArray(currentData) && currentData.length) {
115+
const filteredMessages = currentData.filter(filterFunction)
116+
if (filteredMessages.length !== currentData.length) {
117+
// no need for save operation to process messages - just apply them
118+
data[node.id] = filteredMessages
119+
}
120+
}
105121
}
106122
}
107123

@@ -111,5 +127,6 @@ module.exports = {
111127
setConfig: setters.setConfig,
112128
save: setters.save,
113129
append: setters.append,
130+
filter: setters.filter,
114131
clear: setters.clear
115132
}

nodes/widgets/ui_chart.js

Lines changed: 26 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -61,19 +61,36 @@ module.exports = function (RED) {
6161
if (removeOlder > 0) {
6262
const removeOlderUnit = parseFloat(config.removeOlderUnit)
6363
const ago = (removeOlder * removeOlderUnit) * 1000 // milliseconds ago
64-
const cutoff = (new Date()).getTime() - ago
65-
const _msg = datastore.get(node.id).filter((msg) => {
64+
const cutOff = (new Date()).getTime() - ago
65+
const filterFn = (msg) => {
6666
let timestamp = msg._datapoint.x
6767
// is x already a millisecond timestamp?
6868
if (typeof (msg._datapoint.x) === 'string') {
6969
timestamp = (new Date(msg._datapoint.x)).getTime()
7070
}
71-
return timestamp > cutoff
72-
})
73-
datastore.save(base, node, _msg)
71+
return timestamp > cutOff
72+
}
73+
datastore.filter(base, node, filterFn)
7474
}
7575
}
7676

77+
/**
78+
* For categorical xaxis and types other than histogram then only keep the latest data point for
79+
* each category in each series
80+
*/
81+
function clearOldCategoricalPoints () {
82+
const points = datastore.get(node.id)
83+
const latestSet = {}
84+
for (const item of points) {
85+
const { category, x } = item._datapoint
86+
const key = JSON.stringify([category, x]) // a unique key for each category/series combination
87+
latestSet[key] = item
88+
}
89+
90+
const filtered = Object.values(latestSet)
91+
datastore.save(base, node, filtered)
92+
}
93+
7794
// ensure sane defaults
7895
if (!['msg', 'str', 'property', 'timestamp'].includes(config.xAxisPropertyType)) {
7996
config.xAxisPropertyType = 'timestamp' // default to 'timestamp'
@@ -269,6 +286,10 @@ module.exports = function (RED) {
269286
if (config.xAxisType === 'time' && config.removeOlder && config.removeOlderUnit) {
270287
// remove any points older than the specified time
271288
clearOldPoints()
289+
} else if (config.xAxisType === 'category' && config.chartType !== 'histogram') {
290+
// for categorical xaxis and types other than histogram then only keep the latest data point for
291+
// each category in each series
292+
clearOldCategoricalPoints()
272293
}
273294
}
274295
}

ui/src/widgets/ui-chart/UIChart.vue

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -550,7 +550,9 @@ export default {
550550
console.log('have no payload')
551551
}
552552
}
553-
if (this.chartType === 'line' || this.chartType === 'area' || this.chartType === 'scatter') {
553+
if (this.xAxisType === 'category' && this.props.chartType !== 'histogram') {
554+
this.clearOldCategoricalPoints(options)
555+
} else if (this.chartType === 'line' || this.chartType === 'area' || this.chartType === 'scatter') {
554556
this.limitDataSize(options)
555557
}
556558
this.updateChart(options)
@@ -770,7 +772,6 @@ export default {
770772
// remove older points
771773
points = parseInt(this.props.removeOlderPoints)
772774
}
773-
774775
// apply data limitations to the chart
775776
const series = options.series
776777
if ((cutoff || points) && series.length > 0) {
@@ -789,13 +790,24 @@ export default {
789790
}
790791
}
791792
}
792-
// apply data limtations to the vuex store
793+
// apply data limitations to the vuex store
793794
this.$store.commit('data/restrict', {
794795
widgetId: this.id,
795796
min: cutoff,
796797
points
797798
})
798799
},
800+
801+
/**
802+
* For categorical xaxis and types other than histogram then only keep the latest data point for
803+
* each category in each series
804+
* @param {Object} options - existing eChart options object
805+
*/
806+
clearOldCategoricalPoints (options) {
807+
// There is no need to remove old data from the chart itself as, for categorical xAxis,
808+
// the chart only retains one value for each category
809+
},
810+
799811
calculateBins () {
800812
if (this.props.chartType !== 'histogram') {
801813
return []

0 commit comments

Comments
 (0)