redis-緩存設計-搜索前綴匹配


說明

錄入:

是將錄入字符的String 的各個char 的ASCII碼轉為16進制 在通過-拼接起來,通過zadd新增 score設置為0 則會通過value 16進制進行排序

查找

將查找的字符轉換為16進制通過-拼接 

start計算:通過匹配字符16進制最后以為進1算出起始16進制 再+g 包括所有起始范圍

end計算:通過匹配字符16進制+g 包括所有范圍

然后zadd臨時加入到redis 默認通過value排序則將匹配字符大概包裹起來

然后通過2個臨時數據獲得rank  再根據起始和結zrank獲得數據 過濾掉多余的 再講16進制轉換為字符 返回

 

錄入數據

 //unicode編碼
    private String coding(String s) {
        char[] chars = s.toCharArray();
        StringBuffer buffer = new StringBuffer();
        for (char aChar : chars) {
            //通過字符對應的ASCII碼轉換為16進制再根據-拼接起來  -4f60-7231-5e7f-5dde-30
            String s1 = Integer.toString(aChar, 16);
            buffer.append("-" + s1);
        }

        String encoding = buffer.toString();
        return encoding;
    }

    //添加數據
    public long add(Jedis con, String member, String key) {
        //進行編碼 存儲的是 每個字符進行ASCII碼轉換的16進制
        String coding = coding(member);
        //如果score為0 則會根據value來進行排序
        Long zadd = con.zadd(key, 0, coding);
        return zadd;
    }

匹配數據

    /**
     * unicode解碼 字符通過16進制轉換為ASCII碼 即可得到對應字符
     *
     * @param s
     * @return
     */
    private String decoding(String s) {
        String[] split = s.split("-");
        StringBuffer buffer = new StringBuffer();

        for (String s1 : split) {
            if (!s1.trim().equals("")) {
                //將16進制 轉換成ASCII碼 ASCII碼對應的字符
                char i = (char) Integer.parseInt(s1, 16);
                buffer.append(i);
            }
        }
        return buffer.toString();
    }


    /**
     * 16進制
     * 二進制與16進制對應 0-0 1-1 2-2 3-3 4-4 5-5- 6-6 7-7 8-8 9-9 10-a  11-b 12-c 13-d 14-e 15-f 16-g
     */
    private static final String VALID_CHARACTERS = "0123456789abcdefg";
    
    private String[] findPrefixRange(String prefix) {
        int posn = VALID_CHARACTERS.indexOf(prefix.charAt(prefix.length() - 1));    //查找出前綴字符串最后一個字符在列表中的位置
        char suffix = VALID_CHARACTERS.charAt(posn > 0 ? posn - 1 : 0);                //找出前驅字符
        String start = null;
        String end = null;
        //針對於單純查0的 沒有前置了 所以不用拼g
        if (posn == 0) {
            start = prefix;
            end = prefix + "g";//end拼g 把范圍內的全查出來
        } else {
            start = prefix.substring(0, prefix.length() - 1) + suffix + 'g';        //生成前綴字符串的前驅字符串
            end = prefix + 'g';
            //    防止多個群成員可以同時操作有序集合,將相同的前驅字符串和后繼字符串插入有序集合//生成前綴字符串的后繼字符串
            String identifier = UUID.randomUUID().toString();
            start += identifier;
            end += identifier;
        }
        return new String[]{start, end};
    }


    //查找數據
    public List<String> find(Jedis con, String member, String key) {
        List<String> list = new ArrayList<>();
        member = coding(member);//把輸入的字符轉換成16進制字符串,因為redis里面存的是每個字符對應的16進制字符串
        String[] range = findPrefixRange(member);
        String start = range[0];
        String end = range[1];
        //往zset插入起始和結束的字符,將匹配的結果的數據包起來
        con.zadd(key, 0, start);
        con.zadd(key, 0, end);
        while (true) {
            con.watch(key);
            //根據插入包起來的數據rank拿出啟始和結束的字符
            int sindex = con.zrank(key, start).intValue();
            int eindex = con.zrank(key, end).intValue();            //找出兩個插入元素的位置
            int erange = Math.min(sindex + 9, eindex - 2);                //因為最多展示10個,所以計算出結束為止
            Transaction transaction = con.multi();
            transaction.zrem(key, start);
            transaction.zrem(key, end);
            transaction.zrange(key, sindex, erange);
            List<Object> results = transaction.exec();
            if (results != null) {
                Set<String> set = (Set<String>) results.get(results.size() - 1);
                list.addAll(set);
                break;
            }
        }
        ListIterator<String> iterator = list.listIterator();
        // 這里過濾多個成員添加前驅字符串和后繼字符串引起的不符合的數據
        while (iterator.hasNext()) {
            String string = iterator.next();
            if (string.indexOf("g") != -1) {
                iterator.remove();
            } else {
                iterator.set(decoding(string));//把16進制字符串轉換回來
            }
        }
        return list;
    }

測試

    public static void main(String[] args)
            throws Exception {
        Jedis conn = new Jedis("127.0.0.1", 6379);
        conn.flushDB();
        AutoComplete auto_complete = new AutoComplete();
        for (int i = 0; i < 1; i++) {
            auto_complete.add(conn, "你愛廣州" + i, "test1");
        }
        auto_complete.add(conn, "我愛廣州", "test1");
        auto_complete.add(conn, "我愛成都", "test1");
        auto_complete.add(conn, "你好啊", "test1");
        auto_complete.add(conn, "aaabbb", "test1");
        auto_complete.add(conn, "ac", "test1");
        auto_complete.add(conn, "01", "test1");
        auto_complete.add(conn, "02", "test1");
        auto_complete.add(conn, "11", "test1");
        List<String> numbers = auto_complete.find(conn, "我愛成", "test1");
        System.out.println(JSON.toJSONString(numbers));
    }

打印

["我愛成都"]


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM