2012年11月8日星期四

Redis+node.js使用实例——英文搜索引擎Reds

Redis不仅能像memcached一样用作缓存层,其还可以作以下用途:
  • 持久化:aof 或 rdb
  • 消息队列:使用Redis的list数据结构,也可以使用score set做带权重的消息队列
  • 日志收集器:多个端点将日志信息写入Redis,单独一个线程将所有日志写到磁盘
  • 记录社交关系:将每个人的好友存放在一个set中,求两个人的共同好友时,只需求出两个集合的交集
适合Redis的应用场景远不止上面列的这些,只有想不到,没有用不到。下面看一个使用Redis+node.js实现的英文搜索引擎——Reds,学习Redis的用法。

Reds用到node.js的Redis模块,以及处理英文自然语言的natural模块,其实现以下功能:
调用Reds的search.index接口将英文语句加到Redis服务器,如以下语句:
  • 'Tobi wants 4 dollars'
  • 'Loki is a ferret'
  • 'Tobi is also a ferret'
  • 'Jane is a bitchy ferret'
  • 'Tobi is employed by LearnBoost'
调用单词搜索接口search.query、search.type和search.end搜索符合条件的单词。针对以上语句,若查询同时包含 'jane' 和 'bitchy' 的语句,则Reds返回 'Jane is a bitchy ferret' 这条语句作为结果;若查询包含 'jane' 或 'dollars' 的语句,则Reds返回 'Jane is a bitchy ferret' 和 'Tobi wants 4 dollars' 作为结果。

具体接口调用如下:







reds.createSearch调用创建一个search对象,传入'misc'作为该search对象的标识,后续在该search下进行语句插入和单词查询,均需匹配该标识。

search.index处理语句,进行分词,最后存放到redis服务器score set结构中,第一个参数为要保存的语句,第二个参数为语句的id,第三个参数为插入语句时执行的函数,为可选参数。search.index函数原型如下:
















  • key为search对象的标识,即 'misc'
  • db为redis.createClient()调用所创建的redis客户端对象
  • 对于要插入的str语句,words调用确保str是[a-zA-Z0-9]范围内的有效字符,stripStopWords调用过滤掉str中的stop words(对于英文语句,a/the/to等词就属于stop words),之后再由stem调用得到语句中剩余词的词干(比如 cats/catty/catlike 的词干都是 cat),原语句 'Tobi wants 4 dollars' 经过该步处理后变成一个数组: {'toni',  'want',  'dollar'}
  • countWords计算以上数组元素个数
  • metaphoneMap函数也与自然语言处理相关,其底层调用natural模块的metaphone函数,生成单词对应的发音词(如对 'tobi',返回 'TB'),metaphoneMap最后返回包含如下内容的对象:







以上都是reds自然语言处理相关的代码,下面才真正用到Redis,Redis zadd命令的格式为:
zadd key score member [score] [member]
key为键值,score表示权重,member为内容,score和member可选。

再看上面的代码,对于语句中的每个单词,调用zadd添加两条记录,对于 'tobi',有:
zadd misc:word:TB 1 1
zadd misc:object:1 1 TB

整条语句保存后,有:







以上为分词插入Redis score sets过程,可以看到对于每个单词,插入了两条记录,一条以单词发音缩写为key,单词出现次数为score,句子id为内容;另一条以句子id为key,单词出现次数为score,单词发音缩写为内容。

下面来看单词查询过程,查询主要由end函数完成:




















以上查询代码,也是先求出所要查询单词的词干,然后由metaphoneKeys返回之前插入单词时的key格式,对于 'tobi',metaphoneKeys返回 'misc:word:TB'。

db.multi括起来的代码指示对Redis加权集合进行并集或交集操作,type由search.type接口指定,默认为 'and',即进行交集操作,对应的Redis命令为 zinterstore,zinterstore格式如下:

zinterstore destination numkeys key [key …]

可理解为创立一个名为 'destination' 的集合,key对应的member相同,则满足交集条件,这样的member属于 'destination' 中的一个元素。

对应于本文开头的查询示例,查询既有 'tobi' 单词,又包含 'dollar' 单词,有:







可以看到zrevrange返回id 1,对应于本文开头使用search.index插入的语句。

对于求并集,需由调用search..type('or'),对应的Redis命令为zunionstore

Have fun!

没有评论:

发表评论