Redis自动完成

时间:2009-12-24 11:35:25

标签: autocomplete redis

如何使用redis实现自动填充?

比方说,我有一个数组["alfred","joel","jeff","addick"]。当我输入a时,我会收到["alfred", "addick"]

我希望你明白这一点。如何有效地使用redis命令实现这一点(如果可能,但我认为是)。如果我可以通过telnet尝试一些简单的命令来模仿这种行为,那就太好了。

由于

P.S:给你们所有人的快乐x-mas:)

7 个答案:

答案 0 :(得分:19)

如果你正在处理一个大型数据集,我建议考虑将其作为一个trie来实现。我把一小部分Ruby扔在一起就可以做到这一点:

require 'rubygems'
require 'redis'

class RedisTrie
  TERMINAL = '+'

  def initialize(prefix)
    @prefix = prefix
    @r = Redis.new
  end

  def add_word(word)
    w = word.gsub(/[^a-zA-Z0-9_-]/, '')
    key = "#{@prefix}:"

    w.each_char do |c|
      @r.zset_add key, c.bytes.first, c
      key += c
    end

    @r.zset_add key, 0, TERMINAL
  end

  def add_words(*words)
    words.flatten.compact.each {|word| add_word word}
  end

  def suggest(text)
    @r.zset_range("#{@prefix}:#{text}", 0, -1).map do |c|
      (c == TERMINAL) ? text : suggest(text + c)
    end.flatten
  end
end

rt = RedisTrie.new('trie')

rt.add_words %w( apple automobile carwash oil-change cranky five ruthie axe auto )

p rt.suggest(ARGV.shift.to_s)

例如:

$ ruby RedisTrie.rb
["apple", "auto", "automobile", "axe", "carwash", "cranky", "five", "oil-change", "ruthie"]
$ ruby RedisTrie.rb a
["apple", "auto", "automobile", "axe"]
$ ruby RedisTrie.rb au
["auto", "automobile"]
$ ruby RedisTrie.rb aux
[]

Wikipedia's entry on Tries了解有关尝试的更多信息。

您肯定希望优化您的建议方法以不返回所有值,而只返回它找到的前X值。它会破坏迭代整个数据结构的目的。

答案 1 :(得分:9)

[是的,问题发布后2年,但仍然相关]

在Redis网站上,有一个完整的教程(在Ruby中):

  

Auto Complete with Redis

答案 2 :(得分:6)

在阅读Simon Willison令人印象深刻的Redis tutorial时,我也发现了这个片段。

Solution:

  

Hello Max,

     

KEYS不是最好的方式   你能做的就是用一个   排序集。你想要的是转向   前4或5个字符   字符串成一个整数(你可以   想象每个字符都是一个数字   例如,基数为256的数字,但是   有更好的代表性)和   将所有用户名添加到已排序的   集。

     

然后使用ZRANGEBYSCORE即可获得   给定之间的所有元素   范围。

     

这种方法的可扩展性更高   这是一个O(log(N))的事情。

     

我正在报道这些东西   慢慢发展的Redis书......

     

干杯,Salvatore

答案 3 :(得分:3)

这是PHP中用于使用redis按字母顺序自动完成的简单算法:

function getNextChar($char) {
    $char++;
    if(strlen($char) > 1) { $char--; }
    return $char;
}

function createDictionary($redis, $key, $wordList) {
    if(!$redis->exists($key)) {
        foreach($wordList as $word) {
            $redis->zadd($key, 0, $word);
        }
    }
}

function getLexicalAutocomplete($redis, $dictionaryKey, $input) {
    $inputNext = substr($input, 0, -1) . getNextChar(substr($input, -1)); //ab -> ac

    $redis->zadd($dictionaryKey, 0, $input);
    $redis->zadd($dictionaryKey, 0, $inputNext);

    $rangeStart = $redis->zrank($dictionaryKey, $input)+1;
    $rangeEnd = $redis->zrank($dictionaryKey, $inputNext)-1;

    $autocompleteResults = $redis->zrange($dictionaryKey, $rangeStart, $rangeEnd);

    $redis->zrem($dictionaryKey, $input);
    $redis->zrem($dictionaryKey, $inputNext);

    return $autocompleteResults;
}

$redis = new Redis();
$redis->connect('', 0); //Your redis server ip/port goes here

createDictionary($redis, "dict", array("alfred", "joel", "jeff", "addick"));
$result = getLexicalAutocomplete($redis, "dict", $argv[1]);

echo json_encode($result);

基于Salvatore的文章Auto Complete with Redis,除了我需要生成一个额外的自动完成字典,代价是一点点的性能损失(几个zadds和zrems额外),但在大多数情况下它应该表现良好的情况。该脚本假定为phpredis,但实际上它与predis相同。

输出示例:

> php redisauto.php a
["addick","alfred"]

> php redisauto.php ad
["addick"]

> php redisauto.php al
["alfred"]

> php redisauto.php j
["jeff","joel"]

> php redisauto.php je
["jeff"]

答案 4 :(得分:2)

以下是Python中原始antirez的Ruby实现的端口:

http://www.varunpant.com/posts/auto-complete-with-redis-python

答案 5 :(得分:2)

我刚刚通过一篇很棒的文章,提供了您正在谈论的确切问题,以及更多内容。 Check it out

答案 6 :(得分:0)

可能不相关,但是如果你在这里登陆,你可能也会对使用建议自动填充UI字段的简单,正确,快速和可扩展的方式感兴趣:

http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/search-suggesters-completion.html