Skip to content
This repository has been archived by the owner on Oct 20, 2020. It is now read-only.

Commit

Permalink
support different column weights in full text index
Browse files Browse the repository at this point in the history
  • Loading branch information
mislav committed May 13, 2012
1 parent e5f9d73 commit a9eceb7
Showing 1 changed file with 57 additions and 23 deletions.
80 changes: 57 additions & 23 deletions searchable.rb
Expand Up @@ -19,19 +19,35 @@
# Post.search("hello")
# Post.search("hello", :index => :title_only)
module Searchable
def searchable(columns, opts = {})
opts[:index] ||= 'search'
__searches[opts[:index]] = columns
def searchable(columns, options = {})
index_name = options.fetch(:index, 'search')
__searches[index_name] = columns
end

def search(q, opts = {})
opts[:index] ||= 'search'
finder = all(opts.except(:index, :conditions).merge(:conditions => [
"#{opts[:index]}_vector @@ plainto_tsquery('english', ?)", q]))
finder &= all(opts[:conditions]) if opts[:conditions]
def search(query, options = {})
index_name = options.fetch(:index, 'search')
finder = all(options.except(:index, :conditions).merge(
:conditions => ["#{index_name}_vector @@ plainto_tsquery('english', ?)", query]
))
finder &= all(options[:conditions]) if options[:conditions]
finder
end

def search_raw query, options = {}
limit = options.fetch(:limit, 50)
page = (options[:page] || 1).to_i
offset = (page - 1)*limit
repository.adapter.select <<-SQL, query, limit, offset
SELECT
document_id, title, abstract, publish_date, obsoleted,
ts_rank_cd(search_vector, query) AS search_rank
FROM #{storage_name}, plainto_tsquery('english', ?) query
WHERE search_vector @@ query
ORDER BY search_rank DESC
LIMIT ? OFFSET ?
SQL
end

def auto_migrate_up!(repository_name)
super

Expand All @@ -49,34 +65,52 @@ def auto_migrate_up!(repository_name)
private

def create_alter_table_sql(repository_name, name)
<<-EOS
<<-SQL
ALTER TABLE #{storage_name(repository_name)}
ADD COLUMN #{name}_vector tsvector NOT NULL
EOS
SQL
end

def create_index_sql(repository_name, name)
<<-EOS
<<-SQL
CREATE INDEX #{storage_name(repository_name)}_#{name}_vector_idx
ON #{storage_name(repository_name)} USING gin(#{name}_vector)
EOS
SQL
end

def create_trigger_sql(repository_name, name, columns)
<<-EOS
CREATE TRIGGER #{storage_name(repository_name)}_#{name}_vector_refresh
BEFORE INSERT OR UPDATE ON #{storage_name(repository_name)}
FOR EACH ROW EXECUTE PROCEDURE
tsvector_update_trigger(#{name}_vector, 'pg_catalog.english',
#{column_sql(columns)});
EOS
table_name = storage_name(repository_name)
vector_column = "#{name}_vector"

case columns
when Array
column_sql = columns.map {|col| send(col).field }.join(', ')
trigger = "tsvector_update_trigger(#{vector_column}, 'pg_catalog.english', #{column_sql})"
create_function = ''
when Hash
trigger = "#{table_name}_tsvector_update()"
create_function = <<-SQL
CREATE FUNCTION #{trigger} RETURNS trigger AS $$
begin
new.#{vector_column} :=
#{columns.map {|col,w| "setweight(to_tsvector('pg_catalog.english', coalesce(new.#{col},'')), '#{w}')" }.join(" ||\n" + ' '*13)};
return new;
end
$$ LANGUAGE plpgsql;
SQL
else
raise ArgumentError, "unknown type: #{columns.class}"
end

<<-SQL
#{create_function}
CREATE TRIGGER #{table_name}_#{name}_vector_refresh
BEFORE INSERT OR UPDATE ON #{table_name}
FOR EACH ROW EXECUTE PROCEDURE #{trigger};
SQL
end

def __searches
@__searches ||= {}
end

def column_sql(columns)
columns.map {|column| send(column).field }.join(", ")
end
end

0 comments on commit a9eceb7

Please sign in to comment.