lib/searchkick/index.rb in searchkick-1.2.1 vs lib/searchkick/index.rb in searchkick-1.3.0

- old
+ new

@@ -34,11 +34,11 @@ def swap(new_name) 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 @@ -144,11 +144,11 @@ def clean_indices all_indices = begin client.indices.get_aliases rescue Elasticsearch::Transport::Transport::Errors::NotFound - [] + {} end indices = all_indices.select { |k, v| (v.empty? || v["aliases"].empty?) && k =~ /\A#{Regexp.escape(name)}_\d{14,17}\z/ }.keys indices.each do |index| Searchkick::Index.new(index).delete end @@ -216,19 +216,35 @@ if options[:mappings] && !options[:merge_mappings] settings = options[:settings] || {} mappings = options[:mappings] else + below22 = Searchkick.server_below?("2.2.0") + below50 = Searchkick.server_below?("5.0.0-alpha1") + default_type = below50 ? "string" : "text" + default_analyzer = below50 ? :default_index : :default + keyword_mapping = + if below50 + { + type: default_type, + index: "not_analyzed" + } + else + { + type: "keyword" + } + end + settings = { analysis: { analyzer: { searchkick_keyword: { type: "custom", tokenizer: "keyword", filter: ["lowercase"] + (options[:stem_conversions] == false ? [] : ["searchkick_stemmer"]) }, - default_index: { + default_analyzer => { type: "custom", # character filters -> tokenizer -> token filters # https://www.elastic.co/guide/en/elasticsearch/guide/current/analysis-intro.html char_filter: ["ampersand"], tokenizer: "standard", @@ -378,12 +394,12 @@ # http://elasticsearch-users.115913.n3.nabble.com/synonym-multi-words-search-td4030811.html # I find the following approach effective if you are doing multi-word synonyms (synonym phrases): # - Only apply the synonym expansion at index time # - Don't have the synonym filter applied search # - Use directional synonyms where appropriate. You want to make sure that you're not injecting terms that are too general. - settings[:analysis][:analyzer][:default_index][:filter].insert(4, "searchkick_synonym") - settings[:analysis][:analyzer][:default_index][:filter] << "searchkick_synonym" + settings[:analysis][:analyzer][default_analyzer][:filter].insert(4, "searchkick_synonym") + settings[:analysis][:analyzer][default_analyzer][:filter] << "searchkick_synonym" %w(word_start word_middle word_end).each do |type| settings[:analysis][:analyzer]["searchkick_#{type}_index".to_sym][:filter].insert(2, "searchkick_synonym") end end @@ -393,12 +409,12 @@ type: "synonym", format: "wordnet", synonyms_path: Searchkick.wordnet_path } - settings[:analysis][:analyzer][:default_index][:filter].insert(4, "searchkick_wordnet") - settings[:analysis][:analyzer][:default_index][:filter] << "searchkick_wordnet" + settings[:analysis][:analyzer][default_analyzer][:filter].insert(4, "searchkick_wordnet") + settings[:analysis][:analyzer][default_analyzer][:filter] << "searchkick_wordnet" %w(word_start word_middle word_end).each do |type| settings[:analysis][:analyzer]["searchkick_#{type}_index".to_sym][:filter].insert(2, "searchkick_wordnet") end end @@ -414,11 +430,11 @@ # conversions if (conversions_field = options[:conversions]) mapping[conversions_field] = { type: "nested", properties: { - query: {type: "string", analyzer: "searchkick_keyword"}, + query: {type: default_type, analyzer: "searchkick_keyword"}, count: {type: "integer"} } } end @@ -428,47 +444,54 @@ ] word = options[:word] != false && (!options[:match] || options[:match] == :word) mapping_options.values.flatten.uniq.each do |field| - field_mapping = { - type: "multi_field", - fields: {} - } + fields = {} - unless mapping_options[:only_analyzed].include?(field) - field_mapping[:fields][field] = {type: "string", index: "not_analyzed"} + if mapping_options[:only_analyzed].include?(field) + fields[field] = {type: default_type, index: "no"} + else + fields[field] = keyword_mapping end if !options[:searchable] || mapping_options[:searchable].include?(field) if word - field_mapping[:fields]["analyzed"] = {type: "string", index: "analyzed"} + fields["analyzed"] = {type: default_type, index: "analyzed", analyzer: default_analyzer} if mapping_options[:highlight].include?(field) - field_mapping[:fields]["analyzed"][:term_vector] = "with_positions_offsets" + fields["analyzed"][:term_vector] = "with_positions_offsets" end end - mapping_options.except(:highlight, :searchable, :only_analyzed).each do |type, fields| - if options[:match] == type || fields.include?(field) - field_mapping[:fields][type] = {type: "string", index: "analyzed", analyzer: "searchkick_#{type}_index"} + mapping_options.except(:highlight, :searchable, :only_analyzed).each do |type, f| + if options[:match] == type || f.include?(field) + fields[type] = {type: default_type, index: "analyzed", analyzer: "searchkick_#{type}_index"} end end end - mapping[field] = field_mapping + mapping[field] = + if below50 + { + type: "multi_field", + fields: fields + } + elsif fields[field] + fields[field].merge(fields: fields.except(field)) + end end (options[:locations] || []).map(&:to_s).each do |field| mapping[field] = { type: "geo_point" } end (options[:unsearchable] || []).map(&:to_s).each do |field| mapping[field] = { - type: "string", + type: default_type, index: "no" } end routing = {} @@ -482,37 +505,47 @@ dynamic_fields = { # analyzed field must be the default field for include_in_all # http://www.elasticsearch.org/guide/reference/mapping/multi-field-type/ # however, we can include the not_analyzed field in _all # and the _all index analyzer will take care of it - "{name}" => {type: "string", index: "not_analyzed", include_in_all: !options[:searchable]} + "{name}" => keyword_mapping.merge(include_in_all: !options[:searchable]) } + dynamic_fields["{name}"][:ignore_above] = 256 unless below22 + unless options[:searchable] if options[:match] && options[:match] != :word - dynamic_fields[options[:match]] = {type: "string", index: "analyzed", analyzer: "searchkick_#{options[:match]}_index"} + dynamic_fields[options[:match]] = {type: default_type, index: "analyzed", analyzer: "searchkick_#{options[:match]}_index"} end if word - dynamic_fields["analyzed"] = {type: "string", index: "analyzed"} + dynamic_fields["analyzed"] = {type: default_type, index: "analyzed"} end end + # http://www.elasticsearch.org/guide/reference/mapping/multi-field-type/ + multi_field = + if below50 + { + type: "multi_field", + fields: dynamic_fields + } + else + dynamic_fields["{name}"].merge(fields: dynamic_fields.except("{name}")) + end + mappings = { _default_: { + _all: {type: default_type, index: "analyzed", analyzer: default_analyzer}, properties: mapping, _routing: routing, # https://gist.github.com/kimchy/2898285 dynamic_templates: [ { string_template: { match: "*", match_mapping_type: "string", - mapping: { - # http://www.elasticsearch.org/guide/reference/mapping/multi-field-type/ - type: "multi_field", - fields: dynamic_fields - } + mapping: multi_field } } ] } }.deep_merge(options[:mappings] || {})