Class: Searchkick::Index

Inherits:
Object
  • Object
show all
Includes:
IndexWithInstrumentation
Defined in:
lib/searchkick/index.rb

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(name, options = {}) ⇒ Index

Returns a new instance of Index.



7
8
9
10
11
# File 'lib/searchkick/index.rb', line 7

def initialize(name, options = {})
  @name = name
  @options = options
  @klass_document_type = {} # cache
end

Instance Attribute Details

#nameObject (readonly)

Returns the value of attribute name.



5
6
7
# File 'lib/searchkick/index.rb', line 5

def name
  @name
end

#optionsObject (readonly)

Returns the value of attribute options.



5
6
7
# File 'lib/searchkick/index.rb', line 5

def options
  @options
end

Instance Method Details

#alias_exists?Boolean

Returns:

  • (Boolean)


39
40
41
# File 'lib/searchkick/index.rb', line 39

def alias_exists?
  client.indices.exists_alias name: name
end

#all_indices(unaliased: false) ⇒ Object



104
105
106
107
108
109
110
111
112
113
114
115
116
117
# File 'lib/searchkick/index.rb', line 104

def all_indices(unaliased: false)
  indices =
    begin
      if client.indices.respond_to?(:get_alias)
        client.indices.get_alias
      else
        client.indices.get_aliases
      end
    rescue Elasticsearch::Transport::Transport::Errors::NotFound
      {}
    end
  indices = indices.select { |_k, v| v.empty? || v["aliases"].empty? } if unaliased
  indices.select { |k, _v| k =~ /\A#{Regexp.escape(name)}_\d{14,17}\z/ }.keys
end

#batches_leftObject



235
236
237
# File 'lib/searchkick/index.rb', line 235

def batches_left
  bulk_indexer.batches_left
end

#bulk_delete(records) ⇒ Object



143
144
145
# File 'lib/searchkick/index.rb', line 143

def bulk_delete(records)
  bulk_indexer.bulk_delete(records)
end

#bulk_index(records) ⇒ Object Also known as: import



147
148
149
# File 'lib/searchkick/index.rb', line 147

def bulk_index(records)
  bulk_indexer.bulk_index(records)
end

#bulk_update(records, method_name) ⇒ Object



152
153
154
# File 'lib/searchkick/index.rb', line 152

def bulk_update(records, method_name)
  bulk_indexer.bulk_update(records, method_name)
end

#clean_indicesObject

remove old indices that start w/ index_name



120
121
122
123
124
125
126
# File 'lib/searchkick/index.rb', line 120

def clean_indices
  indices = all_indices(unaliased: true)
  indices.each do |index|
    Searchkick::Index.new(index).delete
  end
  indices
end

#conversions_fieldsObject

should not be public



254
255
256
257
258
259
# File 'lib/searchkick/index.rb', line 254

def conversions_fields
  @conversions_fields ||= begin
    conversions = Array(options[:conversions])
    conversions.map(&:to_s) + conversions.map(&:to_sym)
  end
end

#create(body = {}) ⇒ Object



17
18
19
# File 'lib/searchkick/index.rb', line 17

def create(body = {})
  client.indices.create index: name, body: body
end

#create_index(index_options: nil) ⇒ Object



224
225
226
227
228
229
# File 'lib/searchkick/index.rb', line 224

def create_index(index_options: nil)
  index_options ||= self.index_options
  index = Searchkick::Index.new("#{name}_#{Time.now.strftime('%Y%m%d%H%M%S%L')}", @options)
  index.create(index_options)
  index
end

#deleteObject



21
22
23
24
25
26
27
28
29
# File 'lib/searchkick/index.rb', line 21

def delete
  if alias_exists?
    # can't call delete directly on aliases in ES 6
    indices = client.indices.get_alias(name: name).keys
    client.indices.delete index: indices
  else
    client.indices.delete index: name
  end
end

#document_type(record) ⇒ Object



160
161
162
# File 'lib/searchkick/index.rb', line 160

def document_type(record)
  RecordData.new(self, record).document_type
end

#exists?Boolean

Returns:

  • (Boolean)


31
32
33
# File 'lib/searchkick/index.rb', line 31

def exists?
  client.indices.exists index: name
end

#import_scope(relation, **options) ⇒ Object



231
232
233
# File 'lib/searchkick/index.rb', line 231

def import_scope(relation, **options)
  bulk_indexer.import_scope(relation, **options)
end

#index_optionsObject



13
14
15
# File 'lib/searchkick/index.rb', line 13

def index_options
  IndexOptions.new(self).index_options
end

#klass_document_type(klass, ignore_type = false) ⇒ Object

other



241
242
243
244
245
246
247
248
249
250
251
# File 'lib/searchkick/index.rb', line 241

def klass_document_type(klass, ignore_type = false)
  @klass_document_type[[klass, ignore_type]] ||= begin
    if !ignore_type && klass.searchkick_klass.searchkick_options[:_type]
      type = klass.searchkick_klass.searchkick_options[:_type]
      type = type.call if type.respond_to?(:call)
      type
    else
      klass.model_name.to_s.underscore
    end
  end
end

#locations_fieldsObject



265
266
267
268
269
270
# File 'lib/searchkick/index.rb', line 265

def locations_fields
  @locations_fields ||= begin
    locations = Array(options[:locations])
    locations.map(&:to_s) + locations.map(&:to_sym)
  end
end

#mappingObject



43
44
45
# File 'lib/searchkick/index.rb', line 43

def mapping
  client.indices.get_mapping index: name
end

#promote(new_name, update_refresh_interval: false) ⇒ Object Also known as: swap



76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
# File 'lib/searchkick/index.rb', line 76

def promote(new_name, update_refresh_interval: false)
  if update_refresh_interval
    new_index = Searchkick::Index.new(new_name, @options)
    settings = options[:settings] || {}
    refresh_interval = (settings[:index] && settings[:index][:refresh_interval]) || "1s"
    new_index.update_settings(index: {refresh_interval: refresh_interval})
  end

  old_indices =
    begin
      client.indices.get_alias(name: name).keys
    rescue Elasticsearch::Transport::Transport::Errors::NotFound
      {}
    end
  actions = old_indices.map { |old_name| {remove: {index: old_name, alias: name}} } + [{add: {index: new_name, alias: name}}]
  client.indices.update_aliases body: {actions: actions}
end

#refreshObject



35
36
37
# File 'lib/searchkick/index.rb', line 35

def refresh
  client.indices.refresh index: name
end

#refresh_intervalObject



51
52
53
# File 'lib/searchkick/index.rb', line 51

def refresh_interval
  index_settings["refresh_interval"]
end

#reindex(relation, method_name, scoped:, full: false, scope: nil, **options) ⇒ Object

reindex



198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
# File 'lib/searchkick/index.rb', line 198

def reindex(relation, method_name, scoped:, full: false, scope: nil, **options)
  refresh = options.fetch(:refresh, !scoped)
  options.delete(:refresh)

  if method_name
    # TODO throw ArgumentError
    Searchkick.warn("unsupported keywords: #{options.keys.map(&:inspect).join(", ")}") if options.any?

    # update
    import_scope(relation, method_name: method_name, scope: scope)
    self.refresh if refresh
    true
  elsif scoped && !full
    # TODO throw ArgumentError
    Searchkick.warn("unsupported keywords: #{options.keys.map(&:inspect).join(", ")}") if options.any?

    # reindex association
    import_scope(relation, scope: scope)
    self.refresh if refresh
    true
  else
    # full reindex
    reindex_scope(relation, scope: scope, **options)
  end
end

#reindex_queueObject

queue



192
193
194
# File 'lib/searchkick/index.rb', line 192

def reindex_queue
  Searchkick::ReindexQueue.new(name)
end

#reload_synonymsObject

Raises:



179
180
181
182
183
184
185
186
187
188
# File 'lib/searchkick/index.rb', line 179

def reload_synonyms
  require "elasticsearch/xpack"
  raise Error, "Requires Elasticsearch 7.3+" if Searchkick.server_below?("7.3.0")
  raise Error, "Requires elasticsearch-xpack 7.8+" unless client.xpack.respond_to?(:indices)
  begin
    client.xpack.indices.reload_search_analyzers(index: name)
  rescue Elasticsearch::Transport::Transport::Errors::MethodNotAllowed
    raise Error, "Requires non-OSS version of Elasticsearch"
  end
end

#remove(record) ⇒ Object



135
136
137
# File 'lib/searchkick/index.rb', line 135

def remove(record)
  bulk_indexer.bulk_delete([record])
end

#retrieve(record) ⇒ Object



95
96
97
98
99
100
101
102
# File 'lib/searchkick/index.rb', line 95

def retrieve(record)
  record_data = RecordData.new(self, record).record_data

  # remove underscore
  get_options = Hash[record_data.map { |k, v| [k.to_s.sub(/\A_/, "").to_sym, v] }]

  client.get(get_options)["_source"]
end

#search_id(record) ⇒ Object



156
157
158
# File 'lib/searchkick/index.rb', line 156

def search_id(record)
  RecordData.new(self, record).search_id
end

#settingsObject



47
48
49
# File 'lib/searchkick/index.rb', line 47

def settings
  client.indices.get_settings index: name
end

#similar_record(record, **options) ⇒ Object



164
165
166
167
168
169
170
171
172
173
174
175
176
177
# File 'lib/searchkick/index.rb', line 164

def similar_record(record, **options)
  like_text = retrieve(record).to_hash
    .keep_if { |k, _| !options[:fields] || options[:fields].map(&:to_s).include?(k) }
    .values.compact.join(" ")

  options[:where] ||= {}
  options[:where][:_id] ||= {}
  options[:where][:_id][:not] = Array(options[:where][:_id][:not]) + [record.id.to_s]
  options[:per_page] ||= 10
  options[:similar] = true

  # TODO use index class instead of record class
  Searchkick.search(like_text, model: record.class, **options)
end

#store(record) ⇒ Object

record based use helpers for notifications



131
132
133
# File 'lib/searchkick/index.rb', line 131

def store(record)
  bulk_indexer.bulk_index([record])
end

#suggest_fieldsObject



261
262
263
# File 'lib/searchkick/index.rb', line 261

def suggest_fields
  @suggest_fields ||= Array(options[:suggest]).map(&:to_s)
end

#tokens(text, options = {}) ⇒ Object



59
60
61
# File 'lib/searchkick/index.rb', line 59

def tokens(text, options = {})
  client.indices.analyze(body: {text: text}.merge(options), index: name)["tokens"].map { |t| t["token"] }
end

#total_docsObject



63
64
65
66
67
68
69
70
71
72
73
74
# File 'lib/searchkick/index.rb', line 63

def total_docs
  response =
    client.search(
      index: name,
      body: {
        query: {match_all: {}},
        size: 0
      }
    )

  Searchkick::Results.new(nil, response).total_count
end

#update_record(record, method_name) ⇒ Object



139
140
141
# File 'lib/searchkick/index.rb', line 139

def update_record(record, method_name)
  bulk_indexer.bulk_update([record], method_name)
end

#update_settings(settings) ⇒ Object



55
56
57
# File 'lib/searchkick/index.rb', line 55

def update_settings(settings)
  client.indices.put_settings index: name, body: settings
end

#uuidObject

private



273
274
275
# File 'lib/searchkick/index.rb', line 273

def uuid
  index_settings["uuid"]
end