需求描述
公司需要搭建一个paas平台兼容所有的硬件,硬件把自己采集到的数据发送到paas平台,paas平台把数据处理之发送到各个服务。很多时候硬件发送的数据量特别大,很多不是我们需要的,这个时候,我们把全部的数据进行转发的话,就会浪费大量的带宽,所以,我们需要选择需要的数据进行发送,数据过滤算法便是由此而来。
实现思路
技术: 原生js + elementplus的 Tree树形控件(主要用来显示)
- 按照一定的规则把硬件发送的对象拆分成一个一个的属性
- 对每一个属性进行标记【类型,父元素,所在的节点id等】
- 标记完成之后,把生成标记对象树传入tree树形控件以生成可视化页面
- 把标记树用json的形式存入数据库,或者redis缓存
- 把每个过滤规则和硬件绑定或者和硬件所在的组绑定
- 当硬件发送过数据来的时候,自动根据该硬件的过滤规则(即:标记树),把需要的数据组合成一个新的对象并发送给对应的服务。
标记树的结构,是根据elementPlus的tree组件要求而搭建的,根据tree的要求传入一个对象,便可以生成可视化的选择树,标记树的结构就是根据这个创建的,也可以用其他形式,不过思想都是一样的
实现代码
- 把硬件数据拆分属性,并生成 标记树
/**
* 数据过滤算法----合成属性 树(后期会根据属性树入库、生成对象)
* @param {*} jsonData
* @param {*} respTreeData
* @param {*} level
* @returns
*/
export const makePropertityTree = (jsonData, respTreeData, level) => {
if (!respTreeData || !jsonData) {
console.error("参数传递出现错误!!")
return;
}
if (judgeType(jsonData) === "[object Object]") {
// 对象类型
Reflect.ownKeys(jsonData).forEach((val, idx, arr) => {
let tmpId = (level ? level : "") + "" + idx + val
let tmpObj = {
id: tmpId,
label: val,
type: judgeType(jsonData[val]),
path: [],
children: []
}
if (level) {
tmpObj.path = [...respTreeData.path, tmpId]
respTreeData.children.push(tmpObj)
} else {
tmpObj.path.push(tmpId)
respTreeData.push(tmpObj)
}
if (judgeType(jsonData[val]) == "[object Object]") {
makePropertityTree(jsonData[val], tmpObj, level ? level + 1 : 1)
}
})
} else {
// 如果参数类型不是 Object 直接原样传递,不做处理
// 非对象类型
let tmpObj = {
id: "all",
label: "all",
type: judgeType(jsonData),
path: [],
children: []
}
respTreeData.push(tmpObj)
}
}
- 根据 标记树 过滤硬件的数据包,并生成需要的对象
/**
* 根据属性树treeRef生成过滤对象
* @param {*} treeRef elemengplus 选择树 组件
* @param {*} requestData 需要过滤的数据
*
* example:
*
* // 节点选择树
* const treeRef = ref(null);
* const getCheckedNodes = () => {
* let filterData = filterPropertityObjByTreeRef(treeRef, testJson);
* console.log(filterData)
* }
*/
export const filterPropertityObjByTreeRef = (treeRef, requestData) => {
let nodeList = treeRef.value.getCheckedNodes(false, false)
let modelRuleObj = {};
nodeList.forEach((val, idx) => {
if(val.id == "all") {
// 如果选择了全部属性,或者不过滤,直接返回对象所有属性。
modelRuleObj = requestData;
return;
}
let tmpPropertityObj = {};
let tmpMiddleSaveObj = {};
let targetPropertityObj = {};
for (let i = 0; i < val.path.length; i++) {
// 根据node key 循环 拿到 该属性的所有父节点
let tmpNode = treeRef.value.getNode(val.path[i]).data
let pathLen = val.path.length;
if (i == 0) {
if (!tmpPropertityObj[tmpNode.label] && i != (pathLen - 1)) {
if (tmpNode.type == "[object Object]") {
tmpPropertityObj[tmpNode.label] = {};
tmpMiddleSaveObj = tmpPropertityObj[tmpNode.label];
}
}
} else {
if (i != (pathLen - 1)) {
if (!tmpMiddleSaveObj[tmpNode.label]) {
tmpMiddleSaveObj[tmpNode.label] = {};
}
tmpMiddleSaveObj = tmpMiddleSaveObj[tmpNode.label];
}
}
if (i == 0) {
targetPropertityObj[tmpNode.label] = requestData[tmpNode.label]
}
targetPropertityObj = targetPropertityObj[tmpNode.label]
if (i == (val.path.length - 1)) {
if (val.path.length == 1) {
tmpPropertityObj[val.label] = targetPropertityObj
} else {
if (judgeType(targetPropertityObj) != "[object Object]") {
// 最后一个属性直接赋值
tmpMiddleSaveObj[val.label] = targetPropertityObj
}
}
}
}
modelRuleObj = mergeObjDeep(modelRuleObj, tmpPropertityObj)
});
// 过滤后的对象
return modelRuleObj;
}
- 类型判断函数 (用到的工具方法)
/**
* [object String] 字符串
* [object Number] 数字类型
* [object Array] 数组
* [object Date] 日期
* [object Function] 函数
* [object Object] 对象
* @param {*} data
* @returns
*/
export const judgeType = (data) => {
return Object.prototype.toString.call(data);
}
- 深度融合对象函数 (用到的工具方法)
/**
* 深度融合两个对象
* ... 合并对象只是简单地,浅层次的合并
* @param {*} targetObj
* @param {*} sourceObj
* @returns
*/
export const mergeObjDeep = (targetObj, sourceObj) => {
Reflect.ownKeys(sourceObj).forEach((val, idx, arr) => {
if (targetObj[val]) {
if (judgeType(sourceObj[val]) == "[object Object]") {
mergeObjDeep(targetObj[val], sourceObj[val]);
} else {
targetObj[val] = sourceObj[val]
}
} else {
targetObj[val] = sourceObj[val]
}
})
return targetObj;
}
使用案例
- 使用案例
import { makePropertityTree, filterPropertityObjByTreeRef } from "@/utils/propertityFilter.js"
let treeData = reactive([]);
// 生成标记树
makePropertityTree(testJson, treeData);
console.log("treeData", treeData);
console.log("testJson", testJson)
// 节点选择树
const treeRef = ref(null);
const getCheckedNodes = () => {
// 根据标记树 生成过滤后的对象
let filterData = filterPropertityObjByTreeRef(treeRef, testJson);
console.log(filterData)
}
- 效果展示
- 测试json数据
{
"name": "张三",
"age": 18,
"hobby": {
"exercise": {
"ball": [
"basketball",
"pingpang"
],
"running": [
"long-distance race",
"short"
]
},
"reading": {
"moyan": {
"bookName": "平凡的世界",
"price": 200,
"priz e": true,
"bookDetail": {
"totalNumber": "20万字",
"year": "2004年出版",
"author": "莫言"
}
}
}
},
"family": [
"father",
"mother",
"wife",
"son",
"me"
],
"school": {
"bigSchool": "烟台大学",
"highSchool": "广饶一中",
"baseSchool": "李鹊镇初级中学",
"smallSchool": "东柳小学,艾家小学"
}
}