工作纪实56-ES搜索串一致性

最近接收到了一个需求,一个用户有一组搜索串,比如字符串:abcdefg,如果新增的数据是abdcdfge,则视为是同一条数据,可以理解为即使顺序不同,但是实际上也是唯一串;
为什么是一个数据串我解释一下:大部分的公司es搜索功能(中台),都是会做二次开发的,到实际业务部门,需要根据他们二次开发的规则,提供一个查询数据串

思路

  • 对即将生成的唯一串数据,要先处理成有序字段【treeMap】
  • 生成的唯一串结果最好是定长(不定长的字符串->定长的字符串:hash算法)
  • 运算效率(暂时不考虑,一般都很快)

有点数字签名的意思,只不过这个地方不需要公司秘钥

代码

我选择的是sha-256哈希加密,安全性还可以,生成的效率够用

如果不考虑安全性的话,md5效率要更好一点;

public static String genUniqueKey(String uid, String queryCond) {
    String[] paramsArray = queryCond.split("&");
    Map<String, String> paramsMap = new TreeMap<>();
    for (String param : paramsArray) {
        String[] keyValue = param.split("=");
        paramsMap.put(keyValue[0], keyValue[1]);
    }
    // 将参数按照键名排序
    StringBuilder sortedParams = new StringBuilder(uid).append("&");
    for (Map.Entry<String, String> entry : paramsMap.entrySet()) {
        sortedParams.append(entry.getKey()).append("=").append(entry.getValue()).append("&");
    }
    // 使用SHA-256生成哈希值
    try {
        java.security.MessageDigest md = java.security.MessageDigest.getInstance("SHA-256");
        byte[] hash = md.digest(sortedParams.toString().getBytes("UTF-8"));
        StringBuilder hexString = new StringBuilder();
        for (byte b : hash) {
            String hex = Integer.toHexString(0xff & b);
            if (hex.length() == 1) hexString.append('0');
            hexString.append(hex);
        }
        return hexString.toString();
    } catch (Exception e) {
        e.printStackTrace();
    }
    return null;
}

思考

sha-256生成的串太长了,想短点?
这个算法输出的结果是64(32 * 2)个字符,一个字符2个字节(sha-256生成的结果是32位的)
策略1: 截断处理
直接截断或者自定义抽取不同位置的字符,形成新的串,数据量不大的情况下,理论上唯一性也可以保证

策略2:套娃算法

  1. 考虑过压缩算法(一般在网络传输的时候传输、接收用,不符合场景)
  2. 使用其他的定长算法二次加密【没找到】
  3. 自定义算法,一定范围内的定长【转载自:https://developer.aliyun.com/article/816907,见仁见智吧,如果每个字符串都不一样,长度直接翻倍了】
    第一种,只统计字符出现次数,比如aabcccccaaa,压缩成a5b1c5
public static String doDepressTwo(String str) {
    if ("".equals(str)) return str;
    char[] chars = str.toCharArray();
    HashMap<Character, Integer> map = new HashMap<>();
    for (char c : chars) {
        if (map.containsKey(c)) {
            //如果存在该键,则只需要value+1
            Integer integer = map.get(c);
            map.put(c, ++integer);
        } else {
            //如果不存在该键,就直接放入,value为1
            map.put(c, 1);
        }
    }
    StringBuilder sb = new StringBuilder();
    map.entrySet().forEach(entry -> {
        //将key和value拼接到sb
        sb.append(entry.getKey()).append(entry.getValue());
    });
    return sb.toString();
}

第二种,统计相邻字符串出现次数,比如aabcccccaaa,压缩成a2b1c5a3
思路:需要维护一个当前对比字符,一个字符出现次数,用第n个字符去和第n+1个字符对比,相等则出现次数+1,不相等则拼接在字符串后面

public static String doDepress(String str) {
    //字符串为空直接返回
    if ("".equals(str)) return str;
    StringBuilder buffer = new StringBuilder();
    //用第n个字符去和第n+1个字符对比
    char c = str.charAt(0);
    //记录字符出现次数,默认为1
    int repeat = 1;
    for (int i = 1; i < str.length(); i++) {
        if (c == str.charAt(i)) {
            //如果第n个字符和第n+1个字符相等,则出现次数+1
            repeat++;
        } else {
            //如果第n个字符和第n+1个字符不相等,则拼接在字符串后面
            buffer.append(c).append(repeat);
            //拼接完后重置当前字符串
            c = str.charAt(i);
            //重置字符出现次数
            repeat = 1;
        }
    }
    //最后一组在for循环里是没有拼接的
    return buffer.append(c).append(repeat).toString();
}
  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值