最近开发一个功能,使用 Mysql 存储的数据,数据源是 Elactic Search,每天凌晨获取一下当前某个项目的性能指标的平均值。
由于提供了 URL
筛选的范围,所以对于查询条件出现了排列组合的方式,类似下面这样的。
const params = [{ path: 'a.com', filter: 0 }, { path: 'b.com', filter: 1 }]; // 查询条件
先是计算出全组合,这里有一个算法:
/*
全组合算法
输入 [a,b,c,d,e]
输出 [Array(5), Array(10), Array(10), Array(5), Array(1)]
展开后得
['A', 'B', 'C', 'D', 'E']
['AB', 'AC', 'BC', 'AD', 'BD', 'CD', 'AE', 'BE', 'CE', 'DE']
['ABC', 'ABD', 'ACD', 'BCD', 'ABE', 'ACE', 'BCE', 'ADE', 'BDE', 'CDE']
['ABCD', 'ABCE', 'ABDE', 'ACDE', 'BCDE']
['ABCDE']
*/
Helper.combination = (arr) => {
const list = [];
for (let i = 0; i < arr.length; i++) {
list.push([]);
}
const l = Math.pow(2, arr.length) - 1;
for (let i = 1; i <= l; i++) {
const t = [];
for (let s = i, k = 0; s > 0; s >>= 1, k++) {
if ((s & 1) === 1) {
t.push(arr[k]);
}
}
list[t.length - 1].push([t]); // join 的分隔符可以自行约定
}
return list.flat(2); // 拍平
};
因为我的查询条件是一个对象数组,最小的查询单元也是一个对象,如果要把这个对象当成一个查询维度存储到数据库中,首先想到的是 Hash
,我首先想到了用 MD5 hash 一下 JSON.strigify([{}])
后的内容,然而在查询的时候却无法查询到。
此时我发现请求的参数中数组的内元素顺序会发生改变,虽然内容不变,但是顺序变换之后,Hash 的结果也因此发生改变,所以需要先调整数组的位置,形成一个“稳定的”结构后再 Hash 存储。
所以就转变为了字符串的比较,通过调整顺序,最终形成的字符串一定是一样的。
/* 字符串排序 */
const sort = (str) => {
const strArr = str.split('');
const sorted = strArr.sort((a, b) => a.localeCompare(b)); // 转换成字符串排序
return sorted.join(); // 变成字符串用于 Hash
};
这里我通过 sort
方法来比较字符之前的顺序,从而达到调整位置的目的。其他同事有提到 charCodeAt
转变为数字,再排序形成字符串的形式,不过这种方式本质都是一样的,从乱序到有序的过程。
Finish!