Redis實(shí)現(xiàn)AutoComplete

使用redis的有序集合實(shí)現(xiàn)前綴的autocomplete遮精。
1.首先把數(shù)據(jù)存入redis有續(xù)集,
原文件格式如下:


存入redis中格式泼疑,完整的名字有*結(jié)尾:

11609) "y"
11610) "ya"
11611) "yal"
11612) "yalo"
11613) "yalon"
11614) "yalond"
11615) "yalonda*"
11616) "yas"
11617) "yasm"
11618) "yasme"
11619) "yasmee"
11620) "yasmeen*"
11621) "yasmi"
11622) "yasmin*"
11623) "ye"
11624) "yel"
11625) "yele"
11626) "yelen"
11627) "yelena*"
11628) "yet"
11629) "yett"
11630) "yetta*"
11631) "yetti"
11632) "yettie*"
11633) "yetty*"

2.根據(jù)輸入的前綴赘淮,設(shè)置開始查找的起始位置

127.0.0.1:6379> zrank test y
(integer) 11608
127.0.0.1:6379> 

如y開頭,從11608開始查找昵仅。

3.從起始位置分批(循環(huán))向后查詢(zrange)缓熟,將符合前綴且*結(jié)尾的詞放入返回列表,直至當(dāng)列表size大于所需要的數(shù)量(maxNeeded)或不符合所輸入的前綴時(shí)摔笤,返回列表够滑。
用jedis實(shí)現(xiàn):

package com.zzhblh.sample;

import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;
import redis.clients.jedis.Pipeline;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Set;

public class testJedisAutocomplete {

    private final Jedis redis;
    private final Pipeline p;
    private final String redisKey;
    private long start,end;

    testJedisAutocomplete(final Jedis redis, final Pipeline p, final String redisKey, final URL dictionaryUrl, final boolean populate,
                 final boolean clear) throws IOException {
        this.redis = redis;
        this.p = p;
        this.redisKey = redisKey;

        if(clear) {
            // Truncate existing Dictionary
            clearDictionary();
        }
        if(populate) {
            start = System.currentTimeMillis();
            // Load the entries from dictionaryUrl
            loadDictionary(dictionaryUrl);
            end = System.currentTimeMillis();
            System.out.println("add with pipeline used [" + (end - start) / 1000 + "] seconds ..");
            p.close();
        }
    }

    public List<String> complete(final String prefix, final int count) {
        // 1. Find the position of the prefix in the sorted set in Redis
        if(null == prefix) {
            return Collections.emptyList();
        }
        int prefixLength = prefix.length();
        int start = redis.zrank(redisKey, prefix).intValue();
        if(start < 0 || prefixLength == 0) {
            return Collections.emptyList();
        }
        List<String> results = new ArrayList<String>();
        int found = 0, rangeLength = 50, maxNeeded = count;
        while(found < maxNeeded) {
            Set<String> rangeResults = redis.zrange(redisKey, start, start + rangeLength - 1);
            start += rangeLength;
            if(rangeResults.isEmpty()) {
                break;
            }
            for(final String entry : rangeResults) {
                int minLength = Math.min(entry.length(), prefixLength);
                if(!entry.substring(0, minLength).equalsIgnoreCase(prefix.substring(0, minLength))) {
                    return results;
                }
                if(entry.endsWith("*") && found < maxNeeded) {
                    results.add(entry.substring(0, entry.length() - 1));
                    found = results.size();
                }
            }
        }
        return results;
    }

    private void clearDictionary() {
        redis.del(redisKey);
    }


    private void loadDictionary(final URL dictionaryUrl) throws IOException {
        InputStreamReader inputStreamReader = new InputStreamReader(dictionaryUrl.openStream());
        BufferedReader reader = new BufferedReader(inputStreamReader);
        String word;
        while((word = reader.readLine()) != null) {
            word = word.trim();
            // Add the word if the word does not start with #
            if(!word.isEmpty() && !word.startsWith("#")) {
                addWord(word);
            }
        }
        reader.close();
        inputStreamReader.close();
    }

    private void addWord(final String word) {
        // Add all the possible prefixes of the given word and also the given
        // word with a * suffix.
        p.zadd(redisKey, 0, word + "*");
        for(int index = 1, total = word.length(); index < total; index++) {
            p.zadd(redisKey, 0, word.substring(0, index));
        }

    }

    public static class Builder {

        private final Jedis redis;
        private final Pipeline p;
        private final String redisKey;
        private final URL dictionaryUrl;
        private boolean clear = true;
        private boolean populate = true;

        public Builder(final Jedis redis, final  Pipeline p, final String redisKey, final URL dictionaryUrl) {
            this.redis = redis;
            this.p = p;
            this.redisKey = redisKey;
            this.dictionaryUrl = dictionaryUrl;
        }

        public Builder clear(final boolean clear) {
            this.clear = clear;
            this.populate = true;
            return this;
        }

        public Builder populate(final boolean populate) {
            this.populate = populate;
            return this;
        }

        public testJedisAutocomplete build() throws IOException {
            return new testJedisAutocomplete(redis, p, redisKey, dictionaryUrl, populate, clear);
        }

    }

    public static void main(final String[] args) throws Exception {
        JedisPoolConfig config = new JedisPoolConfig();
        JedisPool pool = new JedisPool(config, "107.170.248.158", 6379);
        Jedis redis = pool.getResource();
        redis.auth("123");
        Pipeline p = redis.pipelined();
        URL url = new URL("http://antirez.com/misc/female-names.txt");

        testJedisAutocomplete autoComplete = new testJedisAutocomplete.Builder(redis, p, "test", url).clear(true).populate(true).build();
        System.out.println(autoComplete.complete("y", 80));
    }

}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市吕世,隨后出現(xiàn)的幾起案子彰触,更是在濱河造成了極大的恐慌,老刑警劉巖寞冯,帶你破解...
    沈念sama閱讀 211,561評(píng)論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件渴析,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡吮龄,警方通過查閱死者的電腦和手機(jī)俭茧,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,218評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來漓帚,“玉大人母债,你說我怎么就攤上這事。” “怎么了毡们?”我有些...
    開封第一講書人閱讀 157,162評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵迅皇,是天一觀的道長。 經(jīng)常有香客問我衙熔,道長登颓,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,470評(píng)論 1 283
  • 正文 為了忘掉前任红氯,我火速辦了婚禮框咙,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘痢甘。我一直安慰自己喇嘱,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,550評(píng)論 6 385
  • 文/花漫 我一把揭開白布塞栅。 她就那樣靜靜地躺著者铜,像睡著了一般。 火紅的嫁衣襯著肌膚如雪放椰。 梳的紋絲不亂的頭發(fā)上作烟,一...
    開封第一講書人閱讀 49,806評(píng)論 1 290
  • 那天,我揣著相機(jī)與錄音砾医,去河邊找鬼俗壹。 笑死,一個(gè)胖子當(dāng)著我的面吹牛藻烤,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播头滔,決...
    沈念sama閱讀 38,951評(píng)論 3 407
  • 文/蒼蘭香墨 我猛地睜開眼怖亭,長吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了坤检?” 一聲冷哼從身側(cè)響起兴猩,我...
    開封第一講書人閱讀 37,712評(píng)論 0 266
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎早歇,沒想到半個(gè)月后倾芝,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,166評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡箭跳,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,510評(píng)論 2 327
  • 正文 我和宋清朗相戀三年晨另,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片谱姓。...
    茶點(diǎn)故事閱讀 38,643評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡借尿,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情路翻,我是刑警寧澤狈癞,帶...
    沈念sama閱讀 34,306評(píng)論 4 330
  • 正文 年R本政府宣布,位于F島的核電站茂契,受9級(jí)特大地震影響蝶桶,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜掉冶,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,930評(píng)論 3 313
  • 文/蒙蒙 一真竖、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧郭蕉,春花似錦疼邀、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,745評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至涨岁,卻和暖如春拐袜,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背梢薪。 一陣腳步聲響...
    開封第一講書人閱讀 31,983評(píng)論 1 266
  • 我被黑心中介騙來泰國打工蹬铺, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人秉撇。 一個(gè)月前我還...
    沈念sama閱讀 46,351評(píng)論 2 360
  • 正文 我出身青樓餐塘,卻偏偏與公主長得像宪迟,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,509評(píng)論 2 348

推薦閱讀更多精彩內(nèi)容

  • 本文將從Redis的基本特性入手狞换,通過講述Redis的數(shù)據(jù)結(jié)構(gòu)和主要命令對(duì)Redis的基本能力進(jìn)行直觀介紹蒜绽。之后概...
    kelgon閱讀 61,133評(píng)論 23 626
  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn)滋饲,斷路器厉碟,智...
    卡卡羅2017閱讀 134,633評(píng)論 18 139
  • 轉(zhuǎn)載:Redis 寶典 | 基礎(chǔ)箍鼓、高級(jí)特性與性能調(diào)優(yōu) 本文由 DevOpsDays 本文由簡(jiǎn)書作者kelgon供稿...
    meng_philip123閱讀 3,116評(píng)論 1 34
  • 生活遠(yuǎn)比自己想象的要精彩,未來也充滿了驚喜與意外呵曹,這都是我們無法琢磨透的…… 休學(xué)后的我在外地一家餐廳打工袄秩,那...
    旅玲閱讀 205評(píng)論 0 0
  • 時(shí)間過得很快,一轉(zhuǎn)眼的時(shí)間寒假結(jié)束了,報(bào)名的拓展寫作班也進(jìn)入了尾聲之剧。 能夠報(bào)這個(gè)班還蠻幸運(yùn)的郭卫,因?yàn)閺那暗奈揖椭佬?..
    shakalaka都被用了閱讀 194評(píng)論 0 0