Skip to content

Commit

Permalink
Merge pull request #78 from theablefew/feature/mappings
Browse files Browse the repository at this point in the history
Fix attribute mapping issues
  • Loading branch information
esmarkowski authored Mar 15, 2024
2 parents 9b05964 + 7a2c2a9 commit 8b26a96
Show file tree
Hide file tree
Showing 10 changed files with 146 additions and 39 deletions.
13 changes: 13 additions & 0 deletions lib/stretchy/attributes/type/array.rb
Original file line number Diff line number Diff line change
@@ -1,7 +1,20 @@
module Stretchy::Attributes::Type
class Array < Stretchy::Attributes::Type::Base # :nodoc:
OPTIONS = [:data_type, :fields]
def type
:array
end

def type_for_database
data_type || :text
end

def mappings(name)
options = {type: type_for_database}
self.class::OPTIONS.each { |option| options[option] = send(option) unless send(option).nil? }
options.delete(:fields) if fields == false
options[:fields] = {keyword: {type: :keyword, ignore_above: 256}} if type_for_database == :text && fields.nil?
{ name => options }.as_json
end
end
end
6 changes: 5 additions & 1 deletion lib/stretchy/attributes/type/base.rb
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,15 @@ def initialize(**args)
end

def mappings(name)
options = {type: type}
options = {type: type_for_database}
self.class::OPTIONS.each { |option| options[option] = send(option) unless send(option).nil? }
{ name => options }.as_json
end

def type_for_database
type
end

private

def define_option_methods!
Expand Down
4 changes: 4 additions & 0 deletions lib/stretchy/attributes/type/date_time.rb
Original file line number Diff line number Diff line change
Expand Up @@ -27,5 +27,9 @@ class DateTime < Stretchy::Attributes::Type::Base
def type
:datetime
end

def type_for_database
:date
end
end
end
10 changes: 10 additions & 0 deletions lib/stretchy/attributes/type/hash.rb
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,16 @@ def type
:hash
end

def type_for_database
:object
end

def mappings(name)
options = {}
OPTIONS.each { |option| options[option] = send(option) unless send(option).nil? }
{ name => options }.as_json
end

private

def cast_value(value)
Expand Down
2 changes: 1 addition & 1 deletion lib/stretchy/attributes/type/keyword.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ module Stretchy::Attributes::Type
# Public: Defines a keyword attribute for the model.
#
# opts - The Hash options used to refine the attribute (default: {}):
#
# :doc_values - The Boolean indicating if the field should be stored on disk in a column-stride fashion. Defaults to true.
# :eager_global_ordinals - The Boolean indicating if global ordinals should be loaded eagerly on refresh. Defaults to false.
# :fields - The Hash of multi-fields for the same string value to be indexed in multiple ways.
Expand All @@ -20,7 +21,6 @@ module Stretchy::Attributes::Type
# :time_series_dimension - The Boolean indicating if the field is a time series dimension. Defaults to false.
#
# Examples
#
# class MyModel
# include StretchyModel
# attribute :tag, :keyword, ignore_above: 256, time_series_dimension: true
Expand Down
2 changes: 1 addition & 1 deletion lib/stretchy/attributes/type/string.rb
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
module Stretchy::Attributes::Type
class String < Stretchy::Attributes::Type::Base # :nodoc:
class String < Stretchy::Attributes::Type::Text # :nodoc:
def type
:string
end
Expand Down
15 changes: 14 additions & 1 deletion lib/stretchy/attributes/type/text.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ module Stretchy::Attributes::Type
# :eager_global_ordinals - The Boolean indicating if global ordinals should be loaded eagerly on refresh. Defaults to false.
# :fielddata - The Boolean indicating if the field can use in-memory fielddata for sorting, aggregations, or scripting. Defaults to false.
# :fielddata_frequency_filter - The Hash of expert settings which allow to decide which values to load in memory when fielddata is enabled.
# :fields - The Hash of multi-fields allow the same string value to be indexed in multiple ways for different purposes.
# :fields - The Hash of multi-fields allow the same string value to be indexed in multiple ways for different purposes. By default, a 'keyword' field is added. Set to false to disable.
# :index - The Boolean indicating if the field should be searchable. Defaults to true.
# :index_options - The String indicating what information should be stored in the index, for search and highlighting purposes. Defaults to 'positions'.
# :index_prefixes - The Hash indicating if term prefixes of between 2 and 5 characters are indexed into a separate field.
Expand All @@ -20,6 +20,7 @@ module Stretchy::Attributes::Type
# :term_vector - The String indicating if term vectors should be stored for the field. Defaults to 'no'.
# :meta - The Hash of metadata about the field.
#
#
# Examples
#
# class MyModel
Expand All @@ -34,5 +35,17 @@ class Text < Stretchy::Attributes::Type::Base
def type
:text
end

def type_for_database
:text
end

def mappings(name)
options = {type: type_for_database}
OPTIONS.each { |option| options[option] = send(option) unless send(option).nil? }
options.delete(:fields) if fields == false
options[:fields] = {keyword: {type: :keyword, ignore_above: 256}} if fields.nil?
{ name => options }.as_json
end
end
end
4 changes: 2 additions & 2 deletions lib/stretchy/delegation/gateway_delegation.rb
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,8 @@ def reload_gateway_configuration!
def gateway(&block)
reload_gateway_configuration! if @gateway && @gateway.client != Stretchy.configuration.client

@gateway ||= Stretchy::Repository.create(client: Stretchy.configuration.client, index_name: index_name, klass: base_class)
block.arity < 1 ? @gateway.instance_eval(&block) : block.call(@gateway) if block_given?
@gateway ||= Stretchy::Repository.create(client: Stretchy.configuration.client, index_name: index_name, klass: base_class, mapping: base_class.attribute_mappings.merge(dynamic: true))
# block.arity < 1 ? @gateway.instance_eval(&block) : block.call(@gateway) if block_given?
@gateway
end

Expand Down
2 changes: 1 addition & 1 deletion spec/stretchy/associations/belongs_to_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ class Book < Stretchy::Record

attribute :title, :keyword
attribute :description, :string
attribute :published_at, :date
attribute :published_at, :datetime
attribute :publisher_id, :keyword

belongs_to :publisher
Expand Down
127 changes: 95 additions & 32 deletions spec/stretchy/attributes_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -20,30 +20,16 @@

context 'in model' do
let(:model) do
class AttributeModel < Stretchy::Record
Object.send(:remove_const, :AttributeModel) if Object.const_defined?(:AttributeModel)
stub_const("AttributeModel", Class.new(Stretchy::Record) do
attribute :name, :text
attribute :age, :integer
attribute :tags, :array
attribute :data, :hash, properties: {weights: {type: :array}, biases: {type: :integer}}
attribute :data, :hash, properties: {weights: {type: :integer}, biases: {type: :integer}}
attribute :flagged, :boolean, default: false
end
AttributeModel
end

it 'array type is registered' do
expect(model.attribute_types['tags'].type).to eq(:array)
end

it 'hash type is registered' do
expect(model.attribute_types['data'].type).to eq(:hash)
end

it 'keyword type is registered' do
expect(model.attribute_types['name'].type).to eq(:text)
end

it 'text type is registered' do
expect(model.attribute_types['age'].type).to eq(:integer)
attribute :body, :text
attribute :only_text, :text, fields: false
end)
end

context 'hash' do
Expand All @@ -55,19 +41,96 @@ class AttributeModel < Stretchy::Record

context 'mappings' do

it 'returns mappings' do
expect(model.attribute_mappings).to eq({
properties: {
name: {type: :text},
age: {type: :integer},
tags: {type: :array},
data: {type: :hash, properties: {weights: {type: :array}, biases: {type: :integer}}},
flagged: {type: :boolean},
id: { type: :keyword },
created_at: { type: :datetime },
updated_at: { type: :datetime }

context 'auto keyword fields for text fields' do
it 'adds keyword to text fields' do
expect(model.attribute_mappings['properties']['body']).to eq({type: :text, fields: {keyword: {type: :keyword, ignore_above: 256}}}.as_json)
end

it 'does not add keyword to text fields when fields is false' do
expect(model.attribute_mappings['properties']['only_text']).to eq({type: :text}.as_json)
end

it 'does not add keyword to text fields when fields is present' do
model.attribute :location, :text, fields: {raw: {type: :keyword}}
expect(model.attribute_mappings['properties']['location']).to eq({type: :text, fields: {raw: {type: :keyword}}}.as_json)
end

let(:mapping) do
{
"attribute_models": {
"mappings": {
"dynamic": true,
"properties": {
"age": {
"type": "integer"
},
"body": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
},
"created_at": {
"type": "date"
},
"data": {
"properties": {
"biases": {
"type": "integer"
},
"weights": {
"type": "integer"
}
}
},
"flagged": {
"type": "boolean"
},
"id": {
"type": "keyword"
},
"name": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
},
"only_text": {
"type": "text"
},
"tags": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
},
"updated_at": {
"type": "date"
}
}
}
}
}.as_json)
}.with_indifferent_access
end

it 'creates index with attribute mappings' do
model.delete_index! if model.index_exists?
model.create_index!
# ap model.mappings.as_json
# ap mapping[:attribute_models][:mappings][:properties].as_json
expect(model.mappings.with_indifferent_access[:properties].as_json).to eq(mapping[:attribute_models][:mappings][:properties])
end

end


Expand Down

0 comments on commit 8b26a96

Please sign in to comment.