Module: FuzzySearch::TrigramModelExtensions

Defined in:
lib/trigram_model_extensions.rb

Instance Method Summary collapse

Instance Method Details

#delete_trigrams(rec) ⇒ Object



86
87
88
# File 'lib/trigram_model_extensions.rb', line 86

def delete_trigrams(rec)
  delete_all(:rec_id => rec.id)
end

#params_for_search(search_term, opts = {}) ⇒ Object



18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
# File 'lib/trigram_model_extensions.rb', line 18

def params_for_search(search_term, opts = {})
  trigrams = FuzzySearch::split_trigrams(search_term)
  # No results for empty search string
  return {:conditions => "0 = 1"} unless trigrams and !trigrams.empty?

  subset = nil
  if opts[:subset]
    if (
    opts[:subset].size == 1 &&
    opts[:subset].keys.first == target_class.fuzzy_search_subset_property
    )
      subset = opts[:subset].values.first.to_i
      opts.delete(:subset)
    else
      # TODO Test me
      raise "Invalid subset argument #{opts[:subset]}"
    end
  end

  unless opts.empty?
    # TODO Test me
    raise "Invalid options: #{opts.keys.join(",")}"
  end

  # Retrieve the IDs of the matching items
  search_result = connection.select_rows(
    "SELECT rec_id, count(*) FROM #{i(table_name)} " +
    (connection.adapter_name.downcase == 'mysql' ?
      "IGNORE INDEX (index_#{table_name}_on_rec_id) " : ""
    ) +
    "WHERE token IN (#{trigrams.map{|t| v(t)}.join(',')}) " +
    (subset ? "AND subset = #{subset} " : "") +
    "GROUP by rec_id " +
    "ORDER BY count(*) DESC " +
    "LIMIT #{target_class.send(:fuzzy_search_limit)}"
  )
  return {:conditions => "0 = 1"} if search_result.empty?

  # Perform a join between the target table and a fake table of matching ids
  static_sql_union = search_result.map{|rec_id, count|
    "SELECT #{v(rec_id)} AS id, #{count} AS score"
  }.join(" UNION ");
  primary_key_expr = "#{i(target_class.table_name)}.#{i(target_class.primary_key)}"
  return {
    :joins => "INNER JOIN (#{static_sql_union}) AS fuzzy_search_results ON " +
              "fuzzy_search_results.id = #{primary_key_expr}",
    :order => "fuzzy_search_results.score DESC"
  }
end

#rebuild_indexObject



8
9
10
11
12
13
14
15
16
# File 'lib/trigram_model_extensions.rb', line 8

def rebuild_index
  reset_column_information
  delete_all
  target_class.find_each do |rec|
    # Maybe can make this more efficient by updating trigrams for
    # batches of records...
    update_trigrams(rec)
  end
end

#set_target_class(cls) ⇒ Object



3
4
5
6
# File 'lib/trigram_model_extensions.rb', line 3

def set_target_class(cls)
  write_inheritable_attribute :target_class, cls
  class_inheritable_reader :target_class
end

#update_trigrams(rec) ⇒ Object



68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
# File 'lib/trigram_model_extensions.rb', line 68

def update_trigrams(rec)
  delete_trigrams(rec)

  values = target_class.fuzzy_search_properties.map{|p| rec.send(p)}
  values = values.select{|p| p and p.respond_to?(:to_s)}
  trigrams = FuzzySearch::split_trigrams(values)

  subset_prop = target_class.fuzzy_search_subset_property
  subset = subset_prop ? rec.send(subset_prop) : 0

  # Ar-extensions import, much much faster than individual creates
  import(
    [:subset, :token, :rec_id],
    trigrams.map{|t| [subset, t, rec.id]},
    :validate => false
  )
end