Class: ActsAsFerret::LocalIndex

Inherits:
AbstractIndex show all
Includes:
MoreLikeThis::IndexMethods
Defined in:
lib/local_index.rb

Instance Attribute Summary

Attributes inherited from AbstractIndex

#index_definition, #index_name, #logger, #registered_models_config

Instance Method Summary collapse

Methods included from MoreLikeThis::IndexMethods

#build_more_like_this_query

Methods inherited from AbstractIndex

#change_index_dir, #register_class, #shared?

Methods included from FerretFindMethods

#ar_find, #count_records, #find_id_model_arrays, #find_ids, #find_records, #lazy_find, #scope_query_to_models

Constructor Details

#initialize(index_name) ⇒ LocalIndex

Returns a new instance of LocalIndex.



5
6
7
8
# File 'lib/local_index.rb', line 5

def initialize(index_name)
  super
  ensure_index_exists
end

Instance Method Details

#add(record, analyzer = nil) ⇒ Object Also known as: <<

add record to index record may be the full AR object, a Ferret document instance or a Hash



103
104
105
106
107
108
109
# File 'lib/local_index.rb', line 103

def add(record, analyzer = nil)
  unless Hash === record || Ferret::Document === record
    analyzer = record.ferret_analyzer
    record = record.to_doc 
  end
  ferret_index.add_document(record, analyzer)
end

#bulk_index(class_name, ids, options) ⇒ Object



58
59
60
# File 'lib/local_index.rb', line 58

def bulk_index(class_name, ids, options)
  ferret_index.bulk_index(class_name.constantize, ids, options)
end

#closeObject

Closes the underlying index instance



36
37
38
39
40
41
42
# File 'lib/local_index.rb', line 36

def close
  @ferret_index.close if @ferret_index
rescue StandardError 
  # is raised when index already closed
ensure
  @ferret_index = nil
end

#determine_stored_fields(options = {}) ⇒ Object

retrieves stored fields from index definition in case the fields to retrieve haven’t been specified with the :lazy option



166
167
168
169
170
171
172
173
# File 'lib/local_index.rb', line 166

def determine_stored_fields(options = {})
  stored_fields = options[:lazy]
  if stored_fields && !(Array === stored_fields)
    stored_fields = index_definition[:ferret_fields].select { |field, config| config[:store] == :yes }.map(&:first)
  end
  logger.debug "stored_fields: #{stored_fields.inspect}"
  return stored_fields
end

#document_number(key) ⇒ Object

retrieves the ferret document number of the record with the given key.



140
141
142
143
144
145
146
# File 'lib/local_index.rb', line 140

def document_number(key)
  docnum = ferret_index.doc_number(key)
  # hits = ferret_index.search query_for_record(key)
  # return hits.hits.first.doc if hits.total_hits == 1
  raise "cannot determine document number for record #{key}" if docnum.nil?
  docnum
end

#ensure_index_existsObject

Checks for the presence of a segments file in the index directory Rebuilds the index if none exists.



27
28
29
30
31
32
33
# File 'lib/local_index.rb', line 27

def ensure_index_exists
  #logger.debug "LocalIndex: ensure_index_exists at #{index_definition[:index_dir]}"
  unless File.file? "#{index_definition[:index_dir]}/segments"
    ActsAsFerret::ensure_directory(index_definition[:index_dir])
    rebuild_index 
  end
end

#extract_stored_fields(doc, stored_fields) ⇒ Object

loads data for fields declared as :lazy from the Ferret document



176
177
178
179
180
181
182
183
184
185
186
187
188
189
# File 'lib/local_index.rb', line 176

def extract_stored_fields(doc, stored_fields) 
  data = {} 
  unless stored_fields.nil?
    logger.debug "extracting stored fields #{stored_fields.inspect} from document #{doc[:class_name]} / #{doc[:id]}"
    fields = index_definition[:ferret_fields] 
    stored_fields.each do |field|
      if field_cfg = fields[field]
        data[field_cfg[:via]] = doc[field]
      end
    end
    logger.debug "done: #{data.inspect}"
  end
  return data 
end

#ferret_indexObject

The ‘real’ Ferret Index instance



17
18
19
20
21
22
23
# File 'lib/local_index.rb', line 17

def ferret_index
  ensure_index_exists
  returning @ferret_index ||= Ferret::Index::Index.new(index_definition[:ferret]) do
    @ferret_index.batch_size = index_definition[:reindex_batch_size]
    @ferret_index.logger = logger
  end
end

#highlight(key, query, options = {}) ⇒ Object

highlight search terms for the record with the given id.



118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
# File 'lib/local_index.rb', line 118

def highlight(key, query, options = {})
  logger.debug("highlight: #{key} query: #{query}")
  options.reverse_merge! :num_excerpts => 2, :pre_tag => '<em>', :post_tag => '</em>'
  highlights = []
  ferret_index.synchronize do
    doc_num = document_number(key)

    if options[:field]
      highlights << ferret_index.highlight(query, doc_num, options)
    else
      query = process_query(query) # process only once
      index_definition[:ferret_fields].each_pair do |field, config|
        next if config[:store] == :no || config[:highlight] == :no
        options[:field] = field
        highlights << ferret_index.highlight(query, doc_num, options)
      end
    end
  end
  return highlights.compact.flatten[0..options[:num_excerpts]-1]
end

#process_query(query, options = {}) ⇒ Object

Parses the given query string into a Ferret Query object.



63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
# File 'lib/local_index.rb', line 63

def process_query(query, options = {})
  return query unless String === query
  ferret_index.synchronize do
    if options[:analyzer]
      # use per-query analyzer if present
      qp = Ferret::QueryParser.new ferret_index.instance_variable_get('@options').merge(options)
      reader = ferret_index.reader
      qp.fields =
          reader.fields unless options[:all_fields] || options[:fields]
      qp.tokenized_fields =
          reader.tokenized_fields unless options[:tokenized_fields]
      return qp.parse query
    else
      # work around ferret bug in #process_query (doesn't ensure the
      # reader is open)
      ferret_index.send(:ensure_reader_open)
      return ferret_index.process_query(query)
    end
  end
end

#query_for_record(key) ⇒ Object

build a ferret query matching only the record with the given id the class name only needs to be given in case of a shared index configuration



150
151
152
153
154
155
156
157
158
159
160
161
# File 'lib/local_index.rb', line 150

def query_for_record(key)
  return Ferret::Search::TermQuery.new(:key, key.to_s)
  # if shared?
  #   raise InvalidArgumentError.new("shared index needs class_name argument") if class_name.nil?
  #   returning bq = Ferret::Search::BooleanQuery.new do
  #     bq.add_query(Ferret::Search::TermQuery.new(:id,         id.to_s),    :must)
  #     bq.add_query(Ferret::Search::TermQuery.new(:class_name, class_name), :must)
  #   end
  # else
  #   Ferret::Search::TermQuery.new(:id, id.to_s)
  # end
end

#rebuild_indexObject

rebuilds the index from all records of the model classes associated with this index



45
46
47
48
49
50
51
52
53
54
55
56
# File 'lib/local_index.rb', line 45

def rebuild_index
  models = index_definition[:registered_models]
  logger.debug "rebuild index with models: #{models.inspect}"
  close
  index = Ferret::Index::Index.new(index_definition[:ferret].dup.update(:auto_flush  => false, 
                                                                        :field_infos => ActsAsFerret::field_infos(index_definition),
                                                                        :create      => true))
  index.batch_size = index_definition[:reindex_batch_size]
  index.logger = logger
  index.index_models models
  reopen!
end

#remove(key) ⇒ Object

delete record from index



113
114
115
# File 'lib/local_index.rb', line 113

def remove(key)
  ferret_index.delete key
end

#reopen!Object



10
11
12
13
14
# File 'lib/local_index.rb', line 10

def reopen!
  logger.debug "reopening index at #{index_definition[:ferret][:path]}"
  close
  ferret_index
end

#searcherObject



89
90
91
# File 'lib/local_index.rb', line 89

def searcher
  ferret_index
end

#total_hits(query, options = {}) ⇒ Object

Total number of hits for the given query.



85
86
87
# File 'lib/local_index.rb', line 85

def total_hits(query, options = {})
  ferret_index.search(process_query(query, options), options).total_hits
end