Class: ActsAsFerret::LocalIndex

Inherits:
AbstractIndex show all
Includes:
MoreLikeThis::IndexMethods
Defined in:
lib/acts_as_ferret/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/acts_as_ferret/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



100
101
102
103
104
105
106
# File 'lib/acts_as_ferret/local_index.rb', line 100

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/acts_as_ferret/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/acts_as_ferret/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



163
164
165
166
167
168
169
170
# File 'lib/acts_as_ferret/local_index.rb', line 163

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.



137
138
139
140
141
142
143
# File 'lib/acts_as_ferret/local_index.rb', line 137

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/acts_as_ferret/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



173
174
175
176
177
178
179
180
181
182
183
184
185
186
# File 'lib/acts_as_ferret/local_index.rb', line 173

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/acts_as_ferret/local_index.rb', line 17

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

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

highlight search terms for the record with the given id.



115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
# File 'lib/acts_as_ferret/local_index.rb', line 115

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
# File 'lib/acts_as_ferret/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
      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



147
148
149
150
151
152
153
154
155
156
157
158
# File 'lib/acts_as_ferret/local_index.rb', line 147

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?
  #   Ferret::Search::BooleanQuery.new.tap do |bq|
  #     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/acts_as_ferret/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



110
111
112
# File 'lib/acts_as_ferret/local_index.rb', line 110

def remove(key)
  ferret_index.delete key
end

#reopen!Object



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

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

#searcherObject



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

def searcher
  ferret_index
end

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

Total number of hits for the given query.



82
83
84
# File 'lib/acts_as_ferret/local_index.rb', line 82

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