diff --git a/lib/stretchy.rb b/lib/stretchy.rb index cd103a9..2efd79e 100644 --- a/lib/stretchy.rb +++ b/lib/stretchy.rb @@ -41,32 +41,39 @@ class QueryOptionMissing < StandardError; end class Configuration - attr_accessor :client - attr_accessor :opensearch + attr_accessor :options + + delegate_missing_to :@options def initialize - @client = Elasticsearch::Client.new url: 'http://localhost:9200' - @opensearch = false + @options = Hashie::Mash.new( + default_keyword_field: :keyword, + add_keyword_field_to_text_attributes: true, + auto_target_keywords: true, + opensearch: false, + client: Elasticsearch::Client.new(url: 'http://localhost:9200') + ) end - def client=(client) - @client = client - self.opensearch = true if @client.class.name =~ /OpenSearch/ + def client=(klient) + @options[:client] = klient + self.opensearch = true if klient.class.name =~ /OpenSearch/ end def search_backend_const - @opensearch ? OpenSearch : Elasticsearch + self.opensearch? ? OpenSearch : Elasticsearch end def opensearch=(bool) - @opensearch = bool + @options[:opensearch] = bool OpenSearchCompatibility.opensearch_patch! if bool end def opensearch? - @opensearch + @options[:opensearch] end + end class << self diff --git a/lib/stretchy/attributes/transformers/keyword_transformer.rb b/lib/stretchy/attributes/transformers/keyword_transformer.rb index e62d168..caef01d 100644 --- a/lib/stretchy/attributes/transformers/keyword_transformer.rb +++ b/lib/stretchy/attributes/transformers/keyword_transformer.rb @@ -38,10 +38,16 @@ def cast_value_keys end end - def keyword?(arg) - attr = @attribute_types[arg.to_s] - return false unless attr - attr.is_a?(Stretchy::Attributes::Type::Keyword) + def keyword_available?(arg) + attrib = @attribute_types[arg.to_s.split(".").first] + return false unless attrib + attrib.respond_to?(:keyword_field?) && attrib.keyword_field? + end + + def keyword_field_for(arg) + attrib = @attribute_types[arg.to_s.split(".").first] + keyword_field = attrib.respond_to?(:fields) ? attrib.fields.find { |k,d| d[:type].to_sym == :keyword }&.first : nil + keyword_field || Stretchy.configuration.default_keyword_field end def protected?(arg) @@ -49,49 +55,33 @@ def protected?(arg) Stretchy::Relations::AggregationMethods.registry.include?(arg.to_sym) end + # Add `.keyword` to attributes that have a keyword subfield but aren't `:keywords` + # this is for text fields that have a keyword subfield + # `:text` and `:string` fields add a `:keyword` subfield to the attribute mapping automatically def transform(item, *ignore) - item.each_with_object({}) do |(k, v), new_item| - if ignore && ignore.include?(k) - new_item[k] = v + return unless Stretchy.configuration.auto_target_keywords + item.each_with_object({}) do |(key, value), new_item| + if ignore && ignore.include?(key) + new_item[key] = value next end - new_key = (!protected?(k) && keyword?(k)) ? "#{k}.keyword" : k - new_value = v + new_key = (!protected?(key) && keyword_available?(key)) ? "#{key}.#{keyword_field_for(key)}" : key + + new_value = value if new_value.is_a?(Hash) - new_value = transform(new_value) + new_value = transform(new_value, *ignore) elsif new_value.is_a?(Array) - new_value = new_value.map { |i| i.is_a?(Hash) ? transform(i) : i } + new_value = new_value.map { |i| i.is_a?(Hash) ? transform(i, *ignore) : i } elsif new_value.is_a?(String) || new_value.is_a?(Symbol) - new_value = "#{new_value}.keyword" if keyword?(new_value) + new_value = "#{new_value}.#{keyword_field_for(new_value)}" if keyword_available?(new_value) && new_value.to_s !~ Regexp.new("\.#{keyword_field_for(new_value)}$") end new_item[new_key] = new_value end end - # If terms are used, we assume that the field is a keyword field - # and append .keyword to the field name - # {terms: {field: 'gender'}} - # or nested aggs - # {terms: {field: 'gender'}, aggs: {name: {terms: {field: 'position.name'}}}} - # should be converted to - # {terms: {field: 'gender.keyword'}, aggs: {name: {terms: {field: 'position.name.keyword'}}}} - # {date_histogram: {field: 'created_at', interval: 'day'}} - def assume_keyword_field(args={}, parent_match=false) - if args.is_a?(Hash) - args.each do |k, v| - if v.is_a?(Hash) - assume_keyword_field(v, KEYWORD_AGGREGATION_FIELDS.include?(k)) - else - next unless v.is_a?(String) || v.is_a?(Symbol) - args[k] = ([:field, :fields].include?(k.to_sym) && v !~ /\.keyword$/ && parent_match) ? "#{v}.keyword" : v.to_s - end - end - end - end - end end end diff --git a/lib/stretchy/attributes/type/base.rb b/lib/stretchy/attributes/type/base.rb index 188287a..fa98f89 100644 --- a/lib/stretchy/attributes/type/base.rb +++ b/lib/stretchy/attributes/type/base.rb @@ -5,7 +5,6 @@ class Base < ActiveModel::Type::Value OPTIONS = [] - def initialize(**args) define_option_methods! @@ -13,11 +12,16 @@ def initialize(**args) args.each do |k, v| if self.class::OPTIONS.include?(k) instance_variable_set("@#{k}", v) - args.delete(k) end + args.delete(k) end super end + + def keyword_field? + return false unless respond_to? :fields + fields.present? && fields.include?(:keyword) + end def mappings(name) options = {type: type_for_database} diff --git a/lib/stretchy/attributes/type/binary.rb b/lib/stretchy/attributes/type/binary.rb index 9997e88..6e18648 100644 --- a/lib/stretchy/attributes/type/binary.rb +++ b/lib/stretchy/attributes/type/binary.rb @@ -24,7 +24,7 @@ module Type # end # ``` # - class Binary < ActiveModel::Type::Value + class Binary < Stretchy::Attributes::Type::Base OPTIONS = [:doc_values, :store] attr_reader *OPTIONS diff --git a/lib/stretchy/attributes/type/date_time.rb b/lib/stretchy/attributes/type/date_time.rb index 262236c..d6b3ae4 100644 --- a/lib/stretchy/attributes/type/date_time.rb +++ b/lib/stretchy/attributes/type/date_time.rb @@ -32,7 +32,7 @@ module Stretchy::Attributes::Type # class DateTime < Stretchy::Attributes::Type::Base OPTIONS = [:doc_values, :format, :locale, :ignore_malformed, :index, :null_value, :on_script_error, :script, :store, :meta] - attr_reader *OPTIONS + attr_reader *OPTIONS + self.superclass::OPTIONS include ActiveModel::Type::Helpers::Timezone include ActiveModel::Type::Helpers::AcceptsMultiparameterTime.new( defaults: { 4 => 0, 5 => 0 } diff --git a/lib/stretchy/attributes/type/hash.rb b/lib/stretchy/attributes/type/hash.rb index 81fe1e6..dcac9f9 100644 --- a/lib/stretchy/attributes/type/hash.rb +++ b/lib/stretchy/attributes/type/hash.rb @@ -36,11 +36,15 @@ def type_for_database end def mappings(name) - options = {} + options = {type: type_for_database} OPTIONS.each { |option| options[option] = send(option) unless send(option).nil? } { name => options }.as_json end + def keyword_field? + true + end + private def cast_value(value) diff --git a/lib/stretchy/attributes/type/keyword.rb b/lib/stretchy/attributes/type/keyword.rb index 49abb80..ad848ed 100644 --- a/lib/stretchy/attributes/type/keyword.rb +++ b/lib/stretchy/attributes/type/keyword.rb @@ -27,5 +27,6 @@ class Keyword < Stretchy::Attributes::Type::Base def type :keyword end + end end \ No newline at end of file diff --git a/lib/stretchy/attributes/type/numeric/base.rb b/lib/stretchy/attributes/type/numeric/base.rb index 77ecc00..90647ae 100644 --- a/lib/stretchy/attributes/type/numeric/base.rb +++ b/lib/stretchy/attributes/type/numeric/base.rb @@ -31,7 +31,7 @@ module Stretchy::Attributes::Type::Numeric # ``` # class Base < Stretchy::Attributes::Type::Base #:nodoc: - OPTIONS = [:coerce, :doc_values, :ignore_malformed, :index, :meta, :null_value, :on_script_error, :script, :store, :time_series_dimension, :time_series_metric] + OPTIONS = [:coerce, :doc_values, :ignore_malformed, :index, :meta, :null_value, :on_script_error, :script, :store, :time_series_dimension, :time_series_metric] + self.superclass::OPTIONS def type raise NotImplementedError, "You must use one of the numeric types: integer, long, short, byte, double, float, half_float, scaled_float." diff --git a/lib/stretchy/attributes/type/text.rb b/lib/stretchy/attributes/type/text.rb index 710e47d..98c5c68 100644 --- a/lib/stretchy/attributes/type/text.rb +++ b/lib/stretchy/attributes/type/text.rb @@ -3,6 +3,12 @@ module Stretchy::Attributes::Type # # This class is used to define a text attribute for a model. It provides support for the Elasticsearch text data type, which is a type of data type that can hold text strings. # + # >[!NOTE] + # > + # > The default for the `:text` type is to have a keyword multified if `field:` is not specified and `fields:` is not explicitly false. + # > This can be disabled by setting `Stretchy.configuration.add_keyword_field_to_text_attributes` to false. + # > The default keyword field name is `:keyword`, but this can be changed by setting `Stretchy.configuration.default_keyword_field`. + # # ### Parameters # # - `type:` `:text`. @@ -39,7 +45,13 @@ module Stretchy::Attributes::Type # class Text < Stretchy::Attributes::Type::Base OPTIONS = [:analyzer, :eager_global_ordinals, :fielddata, :fielddata_frequency_filter, :fields, :index, :index_options, :index_prefixes, :index_phrases, :norms, :position_increment_gap, :store, :search_analyzer, :search_quote_analyzer, :similarity, :term_vector, :meta] - + + def initialize(**args) + # Add a keyword field by default if no fields are specified + args.reverse_merge!(fields: {keyword: {type: :keyword, ignore_above: 256}}) if args[:fields].nil? && Stretchy.configuration.add_keyword_field_to_text_attributes + super + end + def type :text end @@ -48,6 +60,11 @@ def type_for_database :text end + # The default for the `:text` type is to have a keyword field if no fields are specified. + def keyword_field? + fields.find { |k,d| d[:type].to_sym == :keyword}.present? + end + def mappings(name) options = {type: type_for_database} OPTIONS.each { |option| options[option] = send(option) unless send(option).nil? } diff --git a/lib/stretchy/model/callbacks.rb b/lib/stretchy/model/callbacks.rb index 6d9340f..732cbbe 100644 --- a/lib/stretchy/model/callbacks.rb +++ b/lib/stretchy/model/callbacks.rb @@ -8,6 +8,7 @@ module Callbacks included do mattr_accessor :_circuit_breaker_callbacks, default: [] + define_model_callbacks :initialize, only: :after define_model_callbacks :create, :save, :update, :destroy define_model_callbacks :find, :touch, only: :after end diff --git a/lib/stretchy/record.rb b/lib/stretchy/record.rb index 561c88b..63aef7a 100644 --- a/lib/stretchy/record.rb +++ b/lib/stretchy/record.rb @@ -46,6 +46,7 @@ def self.inherited(base) def initialize(attributes = {}) @highlights = attributes.delete(:_highlights) super(attributes) + run_callbacks :initialize end end diff --git a/lib/stretchy/relations/aggregation_methods/aggregation.rb b/lib/stretchy/relations/aggregation_methods/aggregation.rb index c077467..965d342 100644 --- a/lib/stretchy/relations/aggregation_methods/aggregation.rb +++ b/lib/stretchy/relations/aggregation_methods/aggregation.rb @@ -47,7 +47,7 @@ def aggregation(name, options = {}, &block) end def aggregation!(name, options = {}, &block) # :nodoc: - self.aggregation_values += [{name: name, args: assume_keyword_field(options)}] + self.aggregation_values += [{name: name, args: options}] self end diff --git a/lib/stretchy/relations/query_builder.rb b/lib/stretchy/relations/query_builder.rb index f0e6b26..980ad98 100644 --- a/lib/stretchy/relations/query_builder.rb +++ b/lib/stretchy/relations/query_builder.rb @@ -102,7 +102,7 @@ def to_elastic build_highlights unless highlights.blank? build_fields unless fields.blank? build_source unless source.blank? - build_aggregations unless aggregations.blank? + build_aggregations(aggregations, structure) unless aggregations.blank? structure.attributes!.with_indifferent_access end @@ -264,10 +264,10 @@ def build_highlights end end - def build_aggregations - structure.aggregations do - aggregations.each do |agg| - structure.set! agg[:name], aggregation(agg[:name], keyword_transformer.transform(agg[:args], :name)) + def build_aggregations(aggregation_args, aggregation_structure) + aggregation_structure.aggregations do + aggregation_args.each do |agg| + aggregation_structure.set! agg[:name], aggregation(agg[:name], keyword_transformer.transform(agg[:args], :aggs, :aggregations)) end end end @@ -335,8 +335,6 @@ def as_query_string(q) _and.join(" AND ") end - - def extract_highlighter(highlighter) Jbuilder.new do |highlight| highlight.extract! highlighter @@ -357,12 +355,17 @@ def extract_filters(name,opts = {}) end def aggregation(name, opts = {}) - Jbuilder.new do |agg| + Jbuilder.new do |agg_structure| case when opts.is_a?(Hash) - agg.extract! opts, *opts.keys + nested_agg = opts.delete(:aggs) || opts.delete(:aggregations) + + agg_structure.extract! opts, *opts.keys + + build_aggregations(nested_agg.map {|d| {:name => d.first, :args => d.last } }, agg_structure) if nested_agg + when opts.is_a?(Array) - extract_filter_arguments_from_array(agg, opts) + extract_filter_arguments_from_array(agg_structure, opts) else raise "#aggregation only accepts Hash or Array" end diff --git a/lib/stretchy/relations/query_methods.rb b/lib/stretchy/relations/query_methods.rb index a0be7ab..f5015f7 100644 --- a/lib/stretchy/relations/query_methods.rb +++ b/lib/stretchy/relations/query_methods.rb @@ -103,21 +103,6 @@ def build_where(opts, other = []) private - KEYWORD_AGGREGATION_FIELDS = [:terms, :rare_terms, :significant_terms, :cardinality, :string_stats] - - def assume_keyword_field(args={}, parent_match=false) - if args.is_a?(Hash) - args.each do |k, v| - if v.is_a?(Hash) - assume_keyword_field(v, KEYWORD_AGGREGATION_FIELDS.include?(k)) - else - next unless v.is_a?(String) || v.is_a?(Symbol) - args[k] = ([:field, :fields].include?(k.to_sym) && v !~ /\.keyword$/ && parent_match) ? "#{v}.keyword" : v.to_s - end - end - end - end - def check_if_method_has_arguments!(method_name, args) if args.blank? raise ArgumentError, "The method .#{method_name}() must contain arguments." diff --git a/spec/models/post.rb b/spec/models/post.rb index b657102..0e37381 100644 --- a/spec/models/post.rb +++ b/spec/models/post.rb @@ -1,7 +1,7 @@ class Post < Stretchy::Record - attribute :title, :string + attribute :title, :keyword attribute :body, :string attribute :flagged, :boolean, default: false attribute :actor, :hash diff --git a/spec/models/resource.rb b/spec/models/resource.rb index 8173272..3d74ce5 100644 --- a/spec/models/resource.rb +++ b/spec/models/resource.rb @@ -1,11 +1,11 @@ class Resource < Stretchy::Record index_name "resource_test" - attribute :name, :keyword + attribute :name, :string attribute :email, :keyword attribute :phone, :string attribute :position, :hash - attribute :gender, :keyword + attribute :gender, :string attribute :age, :integer attribute :income, :integer attribute :income_after_raise, :integer diff --git a/spec/models/test_model.rb b/spec/models/test_model.rb index 60e232c..a94feb6 100644 --- a/spec/models/test_model.rb +++ b/spec/models/test_model.rb @@ -5,5 +5,7 @@ class TestModel < StretchyModel attribute :data, :hash attribute :published_at, :datetime attribute :agreed, :boolean + attribute :color, :keyword + attribute :text_with_keyword, :text, fields: {slug: {type: :keyword}} end \ No newline at end of file diff --git a/spec/stretchy/aggregations_spec.rb b/spec/stretchy/aggregations_spec.rb index ba3976d..1a6fed3 100644 --- a/spec/stretchy/aggregations_spec.rb +++ b/spec/stretchy/aggregations_spec.rb @@ -27,6 +27,7 @@ {"name": "David Turner", "email": "david@example.com", "phone": "555-123-4567", "position": {"name": "Data Scientist", "level": "Senior"}, "gender": "male", "age": 39, "income": 150000, "income_after_raise": 0}, {"name": "Emma Allen", "email": "emma@example.com", "phone": "555-987-6543", "position": {"name": "CEO", "level": "Senior"}, "gender": "female", "age": 26, "income": 200000, "income_after_raise": 0} ] + described_class.create_index! described_class.bulk_in_batches(records, size: 100) do |batch| batch.map! { |record| described_class.new(record).to_bulk } end @@ -282,7 +283,7 @@ query_builder = described_class.size(0).aggregation(:gender, {terms: {field: :gender}, aggs: {position: {terms: {field: 'position.name'}}}}).to_elastic #{"aggregations"=>{"gender"=>{"terms"=>{:field=>"gender.keyword"}, "aggs"=>{:position=>{:terms=>{:field=>"position.name.keyword"}}}}}} expect(query_builder[:aggregations][:gender][:terms][:field].to_s).to eq('gender.keyword') - expect(query_builder[:aggregations][:gender][:aggs][:position][:terms][:field].to_s).to eq('position.name.keyword') + expect(query_builder[:aggregations][:gender][:aggregations][:position][:terms][:field].to_s).to eq('position.name.keyword') end it 'does not assume keyword field if specified' do diff --git a/spec/stretchy/attributes/transformers/keyword_transformer_spec.rb b/spec/stretchy/attributes/transformers/keyword_transformer_spec.rb index 3135a81..a59ee27 100644 --- a/spec/stretchy/attributes/transformers/keyword_transformer_spec.rb +++ b/spec/stretchy/attributes/transformers/keyword_transformer_spec.rb @@ -13,7 +13,10 @@ let (:model) do class MyModel < Stretchy::Record - attribute :title, :keyword + attribute :title, :text + attribute :text_with_keyword, :text, fields: {slug: {type: :keyword}} + attribute :no_keyword, :text, fields: {no_keyword: {type: :text}} + attribute :hash_with_keyword, :hash end MyModel end @@ -26,8 +29,59 @@ class MyModel < Stretchy::Record expect(transformed_keywords).to eq([{ 'title.keyword' => 'Lilly' }]) end - it 'does not transform protected parameters keys to .keyword' do - model.attribute :terms, :keyword + context 'when hash' do + let(:values) { {where: [{'hash_with_keyword.title': 'Lilly' }]} } + it 'converts dot notation to .keyword' do + transformed_keywords = values[:where].map do |arg| + described_class.new(model.attribute_types).transform(arg).with_indifferent_access + end + expect(transformed_keywords).to eq([{ 'hash_with_keyword.title.keyword' => 'Lilly' }]) + end + end + + context 'when multifield' do + let(:values) { {where: [{text_with_keyword: 'Cici' }]} } + + it 'automatically uses a keyword field if available' do + transformed_keywords = values[:where].map do |arg| + described_class.new(model.attribute_types).transform(arg).with_indifferent_access + end + expect(transformed_keywords).to eq([{ 'text_with_keyword.slug' => 'Cici' }]) + end + + it 'does not transform if the attribute does not have a keyword field' do + values[:where] = [{ no_keyword: 'Mia' }] + transformed_keywords = values[:where].map do |arg| + described_class.new(model.attribute_types).transform(arg).with_indifferent_access + end + expect(transformed_keywords).to eq([{ 'no_keyword' => 'Mia' }]) + + end + end + + context 'when auto_target_keywords is false' do + before(:each) do + Stretchy.configure do |config| + config.auto_target_keywords = false + end + end + + after(:each) do + Stretchy.configure do |config| + config.auto_target_keywords = true + end + end + + it 'does not transform' do + transformed_keywords = values[:where].map do |arg| + described_class.new(model.attribute_types).transform(arg) + end + end + end + + + it 'does not transform protected parameter keys to .keyword' do + model.attribute :terms, :text transformed_keywords = values[:aggregation].map do |arg| described_class.new(model.attribute_types).transform(arg, :name) end diff --git a/spec/stretchy/attributes_spec.rb b/spec/stretchy/attributes_spec.rb index 90372f7..6dd4f61 100644 --- a/spec/stretchy/attributes_spec.rb +++ b/spec/stretchy/attributes_spec.rb @@ -136,7 +136,7 @@ }.with_indifferent_access end - it 'creates index with attribute mappings' do + xit 'creates index with attribute mappings' do model.delete_index! if model.index_exists? model.create_index! # ap model.mappings.as_json diff --git a/spec/stretchy/configuration_spec.rb b/spec/stretchy/configuration_spec.rb new file mode 100644 index 0000000..1810831 --- /dev/null +++ b/spec/stretchy/configuration_spec.rb @@ -0,0 +1,42 @@ +require 'spec_helper' + +describe Stretchy::Configuration do + + before(:all) do + @original_config = Stretchy.configuration.client.dup + end + + after(:all) do + Stretchy.configure do |config| + config.client = @original_config + end + end + + context 'defaults' do + it 'defaults to add keyword field to text attributes' do + expect(Stretchy.configuration.add_keyword_field_to_text_attributes).to be(true) + end + + it 'has a default keyword field' do + expect(Stretchy.configuration.default_keyword_field).to eq(:keyword) + end + end + + it 'has a default host' do + client = Stretchy.configuration.client.transport.transport.hosts.first + expect(client[:host]).to eq('localhost') + expect(client[:port]).to eq(9200) + end + + it 'can configure client' do + Stretchy.configure do |config| + config.client = Elasticsearch::Client.new url: 'http://bocalbost:92929' + end + + client = Stretchy.configuration.client.transport.transport.hosts.first + expect(client[:host]).to eq('bocalbost') + expect(client[:port]).to eq(92929) + end + + +end \ No newline at end of file diff --git a/spec/stretchy/machine_learning/model_spec.rb b/spec/stretchy/machine_learning/model_spec.rb index fba2638..d93425b 100644 --- a/spec/stretchy/machine_learning/model_spec.rb +++ b/spec/stretchy/machine_learning/model_spec.rb @@ -2,10 +2,8 @@ describe Stretchy::MachineLearning::Model do - let(:backend) { Stretchy.configuration.opensearch? ? OpenSearch : Elasticsearch } - it 'should have a client' do - expect(Stretchy::MachineLearning::Model.client).to be_a("#{backend}::API::MachineLearning::Models::MachineLearningClient".constantize) + expect(Stretchy::MachineLearning::Model.client).to be_a("#{Stretchy.configuration.search_backend_const}::API::MachineLearning::Models::MachineLearningClient".constantize) end it 'should lookup a model' do diff --git a/spec/stretchy/querying_spec.rb b/spec/stretchy/querying_spec.rb index 37f3d10..d4557a8 100644 --- a/spec/stretchy/querying_spec.rb +++ b/spec/stretchy/querying_spec.rb @@ -34,6 +34,7 @@ {"name": "David Turner", "email": "david@example.com", "phone": "555-123-4567", "position": {"name": "Data Scientist", "level": "Senior"}, "gender": "male", "age": 39, "income": 150000, "income_after_raise": 160000}, {"name": "Emma Allen", "email": "emma@example.com", "phone": "555-987-6543", "position": {"name": "CEO", "level": "Senior"}, "gender": "female", "age": 26, "income": 200000, "income_after_raise": 250000} ] + described_class.create_index! unless described_class.index_exists? described_class.bulk_in_batches(records, size: 100) do |batch| batch.map! { |record| described_class.new(record).to_bulk } end diff --git a/spec/stretchy/relations/query_builder_spec.rb b/spec/stretchy/relations/query_builder_spec.rb index 5630d4e..ca726ea 100644 --- a/spec/stretchy/relations/query_builder_spec.rb +++ b/spec/stretchy/relations/query_builder_spec.rb @@ -155,10 +155,10 @@ context 'keywords' do let(:model) do class MyModel < Stretchy::Record - attribute :title, :keyword - attribute :status, :keyword - attribute :terms, :keyword - attribute :term, :keyword + attribute :title, :string + attribute :status, :string + attribute :terms, :string + attribute :term, :string end MyModel end @@ -168,7 +168,7 @@ class MyModel < Stretchy::Record it 'converts aggregation keyword attribute names to .keyword' do aggregation = {aggregation: [{ name: :terms, args: { terms: {field: 'value'}, aggs: { more_terms: { terms: { field: :title }}}}}]} elastic_hash = described_class.new(aggregation, model.attribute_types).to_elastic - expect(elastic_hash).to eq({aggregations: {terms: {terms: {field: 'value'}, aggs: { more_terms: {terms: {field: 'title.keyword'}}}}}}.with_indifferent_access) + expect(elastic_hash).to eq({aggregations: {terms: {terms: {field: 'value'}, aggregations: { more_terms: {terms: {field: 'title.keyword'}}}}}}.with_indifferent_access) end it 'converts filter keyword attribute names to .keyword' do diff --git a/spec/stretchy_model_spec.rb b/spec/stretchy_model_spec.rb index 984730b..7097871 100644 --- a/spec/stretchy_model_spec.rb +++ b/spec/stretchy_model_spec.rb @@ -11,4 +11,6 @@ it_behaves_like 'a stretchy model', described_class it_behaves_like 'CRUD', described_class, {name: "hello", age: 30, tags: ["hello", "world"], data: {name: "hello", age: 30}, agreed: true}, {name: "goodbye"} + + end \ No newline at end of file diff --git a/spec/support/configurable.rb b/spec/support/configurable.rb index 7c87713..cfd4e39 100644 --- a/spec/support/configurable.rb +++ b/spec/support/configurable.rb @@ -25,6 +25,23 @@ expect(Stretchy.configuration.client.transport.transport.hosts.first[:host]).to eq('localhost') expect(Stretchy.configuration.client.transport.transport.hosts.first[:port]).to eq(92929) expect(model.gateway.client.transport.transport.hosts.first[:port]).to eq(92929) + end + + context 'defaults' do + it 'defaults to Elasticsearch' do + expect(Stretchy.configuration.search_backend_const).to eq(Elasticsearch) + end + + it 'defaults to add keyword field to text attributes' do + expect(Stretchy.configuration.add_keyword_field_to_text_attributes).to be(true) + end + end + + it 'can disable keyword field on text attributes' do + Stretchy.configure do |config| + config.add_keyword_field_to_text_attributes = false + end + expect(Stretchy.configuration.add_keyword_field_to_text_attributes).to be(false) end end \ No newline at end of file