Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP

Usage in Chinese

ben7th edited this page · 13 revisions
Clone this wiki locally

redis-search 是一款基于 Redis 的高效搜索组件

特点

  • 实时更新搜索索引
  • 高效
  • 分词搜索和逐字匹配搜索
  • 别名搜索
  • 支持 ActiveRecord 和 Mongoid
  • 暂时只能用一个字段做为排序条件
  • 中文同音词搜索
  • 中文拼音搜索支持
  • 中文拼音首字母搜索
  • 可以用一些简单的附加条件组合搜索

环境需求

  • Ruby, Rails
  • ORM 支持 ActiveRecord 和 Mongoid
  • Redis 2.2+

安装

修改 Gemfile

gem 'redis','>= 2.1.1'
gem 'chinese_pinyin', '0.4.1'
gem 'rmmseg-cpp-huacnlee', '0.2.9'
gem 'redis-namespace','~> 1.1.0'
gem 'redis-search', '0.6.3'
bundle install

配置 redis-search

新建一个文件 config/initializes/redis_search.rb

require "redis"
require "redis-namespace"
require "redis-search"
redis = Redis.new(:host => "127.0.0.1",:port => "6379")
redis.select(3)
# don't forget change the namespace
redis = Redis::Namespace.new("your_app_name:redis_search", :redis => redis)
Redis::Search.configure do |config|
  config.redis = redis
  config.complete_max_length = 100
  config.pinyin_match = true
end

配置详解:

  • config.complete_max_length - 逐字匹配场景最长的字符,此处是个阈值,越短越好,但一定要更具实际数据的长度来设置,比如,用于用户昵称搜索,最长的昵称才20个字符,那就没必要设置超过20,但是如果还需要搜索地名,而最长的地名有50,那这里就需要设置至少50
  • config.pinyin_match 是否开启拼音搜索

使用

Model 配置

给需要搜索的 Model 加入 redis-search 引用,例如:

app/models/post.rb

class Post
  include Mongoid::Document
  include Redis::Search

  field :title
  field :body
  field :hits

  belongs_to :user
  belongs_to :category

  redis_search_index(:title_field => :title,
                     :score_field => :hits,
                     :condition_fields => [:user_id, :category_id],
                     :ext_fields => [:category_name])

  def category_name
    self.category.name
  end
end

app/models/user.rb

class User
  include Mongoid::Document
  include Redis::Search

  field :name
  # 别名
  field :alias_names, :type => Array
  field :tagline
  field :email
  field :followers_count

  redis_search_index(:title_field => :name,
                     :alias_field => :alias_names,
                     :prefix_index_enable => true,
                     :score_field => :followers_count,
                     :ext_fields => [:email,:tagline])
end

Controller 里面

app/controllers/search_controller.rb

class SearchController < ApplicationController
  # GET /searchs?q=title
  def index
    Redis::Search.query("Post", params[:q], :conditions => {:user_id => 12})
  end

  # GET /search_users?q=j
  def search_users
    Redis::Search.complete("Post", params[:q], :conditions => {:user_id => 12, :category_id => 4})
  end
end

redis_search_index 参数说明

  • title_field 用于搜索的字段,只能一个
  • alias_field 别名字段,你可以指定一个 Array 或 String 的字段,里面的内容也会作为搜索比配项,如果是 String 类型,将会自动以英文逗号分割为多个别名
  • score_field 用于排序的字段,也是只能一个
  • condition_fields 附加条件的字段,比如,可以把 sex 放进去,搜索的时候就可以选择只搜索男或女的
  • ext_fields 是将一些需要放入搜索结果的字段存如索引,可以有很多个,但适当,已节省内存(因为 redis-search 搜索是不通过数据库的)

搜索调用参数说明

Redis::Search.query 分词搜索

这个方法将会调用分词组件将第二个参数分词然后模糊搜索,排序将会自动按照 redis_search_index 的 score_field 字段降序排列

  • 参数1 需要搜索的 Model 名称,字符串,注意大小写
  • 参数2 需要搜索的字符
  • conditions Hash 类型 附加条件,里面可以有多个,多个 conditions 条件将会以 and 的方式组合,另外参数必须提前在 redis_search_index 的 condition_fields 里面定义过的才可以

用法:

Redis::Search.query("Post", params[:q], :conditions => {:sex => 1, :state => 3})

Redis::Search.complete 逐字匹配搜索

此搜索于 Redis::Search.query 的唯一区别是,它将不会分词,而是根据从左到右的顺序完全匹配搜索(拼音可以搜索中文的) 使用场景可以用在,匹配用户名,地名,email,分类,Tag等

用法:

Redis::Search.complete("Post", params[:q], :conditions => {:sex => 0})

重建索引

redis-search 装好后会自动索引新数据,但是如果是在已有数据的项目上面安装或者 Redis 数据丢失,那就需要用重建索引的功能

用法:

$ rake redis_search:index

此处 ActiveRecord 可能会有问题,因为 redis_search:index 无法知道你有哪些 Model 定义了 redis_search_index,那就需要做点小改动,在 rake 任务之前调用一下那些有定义 redis_search_index 的 Model,比如:

User, Post 两个 Model 有定义 redis_search_index

config/initializes/redis_search.rb 的最后加入

User.new
Post.new

这样 rake redis_search:index 就可以运行了(此处求改进)。

try add following lines to redis_search.rb

ActiveRecord::Base.connection.tables.map do |model|
  _name = model.capitalize.singularize.camelize
  if  Kernel.class_defined?(_name)
    _class=eval _name
    _class.new if _class.respond_to?(:redis_search_index)
  end
end

and define Kernel method

module Kernel
  def class_defined?(class_name)
    begin
      return self.const_get(class_name).is_a?(Class)
    rescue NameError
      return false
    end
  end
end
Something went wrong with that request. Please try again.