stretchy-model 0.6.0 → 0.6.6

Sign up to get free protection for your applications and to get access to all the features.
Files changed (191) hide show
  1. checksums.yaml +4 -4
  2. data/.yardopts +2 -1
  3. data/README.md +28 -10
  4. data/Rakefile +56 -0
  5. data/containers/Dockerfile.opensearch +4 -3
  6. data/docker-compose.yml +32 -19
  7. data/docs/.nojekyll +0 -0
  8. data/docs/README.md +147 -0
  9. data/docs/_coverpage.md +14 -0
  10. data/docs/_sidebar.md +14 -0
  11. data/docs/examples/_sidebar.md +15 -0
  12. data/docs/examples/data_analysis.md +216 -0
  13. data/docs/examples/semantic_search_with_llm.md +83 -0
  14. data/docs/examples/simple-ingest-pipeline.md +326 -0
  15. data/docs/guides/_sidebar.md +14 -0
  16. data/docs/guides/aggregations.md +142 -0
  17. data/docs/guides/machine-learning.md +154 -0
  18. data/docs/guides/models.md +372 -0
  19. data/docs/guides/pipelines.md +151 -0
  20. data/docs/guides/querying.md +361 -0
  21. data/docs/guides/quick-start.md +72 -0
  22. data/docs/guides/scopes.md +125 -0
  23. data/docs/index.html +113 -0
  24. data/docs/stretchy.cover.png +0 -0
  25. data/docs/stretchy.logo.png +0 -0
  26. data/docs/styles.css +90 -0
  27. data/lib/elasticsearch/api/actions/machine_learning/models/delete_model.rb +33 -0
  28. data/lib/elasticsearch/api/actions/machine_learning/models/deploy.rb +31 -0
  29. data/lib/elasticsearch/api/actions/machine_learning/models/get_model.rb +43 -0
  30. data/lib/elasticsearch/api/actions/machine_learning/models/get_status.rb +31 -0
  31. data/lib/elasticsearch/api/actions/machine_learning/models/params_registry.rb +45 -0
  32. data/lib/elasticsearch/api/actions/machine_learning/models/register.rb +45 -0
  33. data/lib/elasticsearch/api/actions/machine_learning/models/undeploy.rb +32 -0
  34. data/lib/elasticsearch/api/actions/machine_learning/models/update_model.rb +39 -0
  35. data/lib/elasticsearch/api/namespace/machine_learning/model.rb +27 -0
  36. data/lib/opensearch/api/actions/machine_learning/models/delete_model.rb +33 -0
  37. data/lib/opensearch/api/actions/machine_learning/models/deploy.rb +31 -0
  38. data/lib/opensearch/api/actions/machine_learning/models/get_model.rb +44 -0
  39. data/lib/opensearch/api/actions/machine_learning/models/get_status.rb +31 -0
  40. data/lib/opensearch/api/actions/machine_learning/models/params_registry.rb +45 -0
  41. data/lib/opensearch/api/actions/machine_learning/models/register.rb +45 -0
  42. data/lib/opensearch/api/actions/machine_learning/models/undeploy.rb +31 -0
  43. data/lib/opensearch/api/actions/machine_learning/models/update_model.rb +39 -0
  44. data/lib/opensearch/api/namespace/machine_learning/model.rb +27 -0
  45. data/lib/stretchy/attributes/transformers/keyword_transformer.rb +41 -35
  46. data/lib/stretchy/attributes/type/array.rb +24 -1
  47. data/lib/stretchy/attributes/type/base.rb +6 -2
  48. data/lib/stretchy/attributes/type/binary.rb +24 -17
  49. data/lib/stretchy/attributes/type/boolean.rb +29 -22
  50. data/lib/stretchy/attributes/type/completion.rb +18 -10
  51. data/lib/stretchy/attributes/type/constant_keyword.rb +35 -26
  52. data/lib/stretchy/attributes/type/date_time.rb +81 -20
  53. data/lib/stretchy/attributes/type/dense_vector.rb +46 -49
  54. data/lib/stretchy/attributes/type/flattened.rb +28 -19
  55. data/lib/stretchy/attributes/type/geo_point.rb +21 -12
  56. data/lib/stretchy/attributes/type/geo_shape.rb +21 -12
  57. data/lib/stretchy/attributes/type/hash.rb +24 -10
  58. data/lib/stretchy/attributes/type/histogram.rb +25 -0
  59. data/lib/stretchy/attributes/type/ip.rb +26 -17
  60. data/lib/stretchy/attributes/type/join.rb +16 -7
  61. data/lib/stretchy/attributes/type/keyword.rb +21 -26
  62. data/lib/stretchy/attributes/type/knn_vector.rb +47 -0
  63. data/lib/stretchy/attributes/type/match_only_text.rb +22 -1
  64. data/lib/stretchy/attributes/type/nested.rb +16 -11
  65. data/lib/stretchy/attributes/type/numeric/base.rb +30 -22
  66. data/lib/stretchy/attributes/type/numeric/byte.rb +20 -0
  67. data/lib/stretchy/attributes/type/numeric/double.rb +20 -0
  68. data/lib/stretchy/attributes/type/numeric/float.rb +20 -0
  69. data/lib/stretchy/attributes/type/numeric/half_float.rb +20 -0
  70. data/lib/stretchy/attributes/type/numeric/integer.rb +21 -1
  71. data/lib/stretchy/attributes/type/numeric/long.rb +20 -0
  72. data/lib/stretchy/attributes/type/numeric/scaled_float.rb +16 -7
  73. data/lib/stretchy/attributes/type/numeric/short.rb +20 -0
  74. data/lib/stretchy/attributes/type/numeric/unsigned_long.rb +21 -1
  75. data/lib/stretchy/attributes/type/percolator.rb +16 -4
  76. data/lib/stretchy/attributes/type/point.rb +19 -9
  77. data/lib/stretchy/attributes/type/range/base.rb +24 -1
  78. data/lib/stretchy/attributes/type/range/date_range.rb +21 -5
  79. data/lib/stretchy/attributes/type/range/double_range.rb +20 -4
  80. data/lib/stretchy/attributes/type/range/float_range.rb +21 -5
  81. data/lib/stretchy/attributes/type/range/integer_range.rb +20 -4
  82. data/lib/stretchy/attributes/type/range/ip_range.rb +20 -4
  83. data/lib/stretchy/attributes/type/range/long_range.rb +20 -4
  84. data/lib/stretchy/attributes/type/rank_feature.rb +16 -6
  85. data/lib/stretchy/attributes/type/rank_features.rb +27 -10
  86. data/lib/stretchy/attributes/type/search_as_you_type.rb +28 -18
  87. data/lib/stretchy/attributes/type/shape.rb +19 -9
  88. data/lib/stretchy/attributes/type/sparse_vector.rb +25 -21
  89. data/lib/stretchy/attributes/type/string.rb +42 -1
  90. data/lib/stretchy/attributes/type/text.rb +53 -28
  91. data/lib/stretchy/attributes/type/token_count.rb +21 -11
  92. data/lib/stretchy/attributes/type/version.rb +16 -6
  93. data/lib/stretchy/attributes/type/wildcard.rb +36 -25
  94. data/lib/stretchy/attributes.rb +30 -0
  95. data/lib/stretchy/delegation/gateway_delegation.rb +86 -2
  96. data/lib/stretchy/index_setting.rb +94 -0
  97. data/lib/stretchy/indexing/bulk.rb +75 -3
  98. data/lib/stretchy/machine_learning/model.rb +192 -0
  99. data/lib/stretchy/model/callbacks.rb +1 -0
  100. data/lib/stretchy/model/common.rb +157 -0
  101. data/lib/stretchy/model/persistence.rb +144 -0
  102. data/lib/stretchy/model/refreshable.rb +26 -0
  103. data/lib/stretchy/open_search_compatibility.rb +4 -0
  104. data/lib/stretchy/pipeline.rb +124 -0
  105. data/lib/stretchy/pipelines/processor.rb +57 -0
  106. data/lib/stretchy/querying.rb +7 -7
  107. data/lib/stretchy/rails/instrumentation/publishers.rb +31 -0
  108. data/lib/{rails → stretchy/rails}/instrumentation/railtie.rb +11 -6
  109. data/lib/stretchy/record.rb +5 -4
  110. data/lib/stretchy/relation.rb +230 -28
  111. data/lib/stretchy/relations/aggregation_methods/aggregation.rb +59 -0
  112. data/lib/stretchy/relations/aggregation_methods/avg.rb +45 -0
  113. data/lib/stretchy/relations/aggregation_methods/bucket_script.rb +47 -0
  114. data/lib/stretchy/relations/aggregation_methods/bucket_selector.rb +47 -0
  115. data/lib/stretchy/relations/aggregation_methods/bucket_sort.rb +47 -0
  116. data/lib/stretchy/relations/aggregation_methods/cardinality.rb +47 -0
  117. data/lib/stretchy/relations/aggregation_methods/children.rb +47 -0
  118. data/lib/stretchy/relations/aggregation_methods/composite.rb +41 -0
  119. data/lib/stretchy/relations/aggregation_methods/date_histogram.rb +53 -0
  120. data/lib/stretchy/relations/aggregation_methods/date_range.rb +53 -0
  121. data/lib/stretchy/relations/aggregation_methods/extended_stats.rb +48 -0
  122. data/lib/stretchy/relations/aggregation_methods/filter.rb +47 -0
  123. data/lib/stretchy/relations/aggregation_methods/filters.rb +47 -0
  124. data/lib/stretchy/relations/aggregation_methods/geo_bounds.rb +40 -0
  125. data/lib/stretchy/relations/aggregation_methods/geo_centroid.rb +40 -0
  126. data/lib/stretchy/relations/aggregation_methods/global.rb +39 -0
  127. data/lib/stretchy/relations/aggregation_methods/histogram.rb +43 -0
  128. data/lib/stretchy/relations/aggregation_methods/ip_range.rb +41 -0
  129. data/lib/stretchy/relations/aggregation_methods/max.rb +40 -0
  130. data/lib/stretchy/relations/aggregation_methods/min.rb +41 -0
  131. data/lib/stretchy/relations/aggregation_methods/missing.rb +40 -0
  132. data/lib/stretchy/relations/aggregation_methods/nested.rb +40 -0
  133. data/lib/stretchy/relations/aggregation_methods/percentile_ranks.rb +45 -0
  134. data/lib/stretchy/relations/aggregation_methods/percentiles.rb +45 -0
  135. data/lib/stretchy/relations/aggregation_methods/range.rb +42 -0
  136. data/lib/stretchy/relations/aggregation_methods/reverse_nested.rb +40 -0
  137. data/lib/stretchy/relations/aggregation_methods/sampler.rb +40 -0
  138. data/lib/stretchy/relations/aggregation_methods/scripted_metric.rb +43 -0
  139. data/lib/stretchy/relations/aggregation_methods/significant_terms.rb +45 -0
  140. data/lib/stretchy/relations/aggregation_methods/stats.rb +42 -0
  141. data/lib/stretchy/relations/aggregation_methods/sum.rb +42 -0
  142. data/lib/stretchy/relations/aggregation_methods/terms.rb +46 -0
  143. data/lib/stretchy/relations/aggregation_methods/top_hits.rb +42 -0
  144. data/lib/stretchy/relations/aggregation_methods/top_metrics.rb +44 -0
  145. data/lib/stretchy/relations/aggregation_methods/value_count.rb +41 -0
  146. data/lib/stretchy/relations/aggregation_methods/weighted_avg.rb +42 -0
  147. data/lib/stretchy/relations/aggregation_methods.rb +20 -749
  148. data/lib/stretchy/relations/finder_methods.rb +2 -18
  149. data/lib/stretchy/relations/null_relation.rb +55 -0
  150. data/lib/stretchy/relations/query_builder.rb +139 -23
  151. data/lib/stretchy/relations/query_methods/bind.rb +19 -0
  152. data/lib/stretchy/relations/query_methods/extending.rb +29 -0
  153. data/lib/stretchy/relations/query_methods/fields.rb +70 -0
  154. data/lib/stretchy/relations/query_methods/filter_query.rb +53 -0
  155. data/lib/stretchy/relations/query_methods/has_field.rb +40 -0
  156. data/lib/stretchy/relations/query_methods/highlight.rb +75 -0
  157. data/lib/stretchy/relations/query_methods/hybrid.rb +60 -0
  158. data/lib/stretchy/relations/query_methods/ids.rb +40 -0
  159. data/lib/stretchy/relations/query_methods/match.rb +52 -0
  160. data/lib/stretchy/relations/query_methods/must_not.rb +54 -0
  161. data/lib/stretchy/relations/query_methods/neural.rb +58 -0
  162. data/lib/stretchy/relations/query_methods/neural_sparse.rb +43 -0
  163. data/lib/stretchy/relations/query_methods/none.rb +21 -0
  164. data/lib/stretchy/relations/query_methods/or_filter.rb +21 -0
  165. data/lib/stretchy/relations/query_methods/order.rb +63 -0
  166. data/lib/stretchy/relations/query_methods/query_string.rb +44 -0
  167. data/lib/stretchy/relations/query_methods/regexp.rb +61 -0
  168. data/lib/stretchy/relations/query_methods/should.rb +51 -0
  169. data/lib/stretchy/relations/query_methods/size.rb +44 -0
  170. data/lib/stretchy/relations/query_methods/skip_callbacks.rb +47 -0
  171. data/lib/stretchy/relations/query_methods/source.rb +59 -0
  172. data/lib/stretchy/relations/query_methods/where.rb +113 -0
  173. data/lib/stretchy/relations/query_methods.rb +51 -540
  174. data/lib/stretchy/relations/scoping/default.rb +136 -0
  175. data/lib/stretchy/relations/scoping/named.rb +70 -0
  176. data/lib/stretchy/relations/scoping/scope_registry.rb +36 -0
  177. data/lib/stretchy/relations/scoping.rb +30 -0
  178. data/lib/stretchy/relations/search_option_methods.rb +2 -0
  179. data/lib/stretchy/shared_scopes.rb +6 -1
  180. data/lib/stretchy/version.rb +1 -1
  181. data/lib/stretchy.rb +23 -11
  182. metadata +147 -18
  183. data/lib/rails/instrumentation/publishers.rb +0 -29
  184. data/lib/stretchy/common.rb +0 -33
  185. data/lib/stretchy/null_relation.rb +0 -53
  186. data/lib/stretchy/persistence.rb +0 -43
  187. data/lib/stretchy/refreshable.rb +0 -15
  188. data/lib/stretchy/scoping/default.rb +0 -134
  189. data/lib/stretchy/scoping/named.rb +0 -68
  190. data/lib/stretchy/scoping/scope_registry.rb +0 -34
  191. data/lib/stretchy/scoping.rb +0 -28
@@ -0,0 +1,39 @@
1
+ module OpenSearch
2
+ module API
3
+ module MachineLearning
4
+ module Models
5
+ module Actions
6
+ # Returns a model.
7
+ #
8
+ # @option arguments [String] :id The model id
9
+ # @option arguments [Hash] :body The request fields
10
+ #
11
+ # Example
12
+ # update_model(id: 109sdj0asl092, "rate_limiter": {
13
+ # "limit": "4",
14
+ # "unit": "MINUTES"
15
+ # }
16
+ # )
17
+ #
18
+ # PUT /_plugins/_ml/models/<model_id>
19
+ def update_model(arguments = {})
20
+ raise ArgumentError, "Required argument 'body' missing" unless arguments[:body]
21
+ raise ArgumentError, "Required argument 'id' missing" unless arguments[:id]
22
+
23
+ _id = arguments.delete(:id)
24
+
25
+ headers = arguments.delete(:headers) || {}
26
+
27
+ method = OpenSearch::API::HTTP_PUT
28
+ path = "_plugins/_ml/models/#{Utils.__listify(_id)}"
29
+ params = Utils.__validate_and_extract_params arguments, ParamsRegistry.get(__method__)
30
+
31
+ body = arguments[:body]
32
+ perform_request(method, path, params, body, headers).body
33
+ end
34
+
35
+ end
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,27 @@
1
+ Dir[File.expand_path('../../actions/**/*.rb', __dir__)].sort.each { |f| require f }
2
+
3
+ module OpenSearch
4
+ module API
5
+ module MachineLearning
6
+ module Models
7
+ module Actions; end
8
+
9
+ # Client for the "machine_learning/models" namespace (includes the {MachineLearning::Models::Actions} methods)
10
+ #
11
+ class MachineLearningClient
12
+ include MachineLearning::Models::Actions
13
+ include OpenSearch::API::Common::Client::Base
14
+ include OpenSearch::API::Common::Client
15
+ end
16
+
17
+ # Proxy method for {MachineLearningModel}, available in the receiving object
18
+ #
19
+ def machine_learning
20
+ @machine_learning ||= MachineLearningClient.new(self)
21
+ end
22
+
23
+ alias ml machine_learning
24
+ end
25
+ end
26
+ end
27
+ end
@@ -1,6 +1,20 @@
1
1
  module Stretchy
2
2
  module Attributes
3
3
  module Transformers
4
+ # Applies transformations to keyword fields in queries
5
+ #
6
+ # ### Examples
7
+ #
8
+ # ```ruby
9
+ # class Goat < StretchyModel
10
+ # attribute :name, :keyword
11
+ # attribute :age, :integer
12
+ # end
13
+ #
14
+ # Goat.where(name: 'billy').to_elastic
15
+ # # => {query: {term: {'name.keyword': 'billy'}}}
16
+ #
17
+ # ```
4
18
  class KeywordTransformer
5
19
 
6
20
  KEYWORD_AGGREGATION_KEYS = [:terms, :rare_terms, :significant_terms, :cardinality, :string_stats]
@@ -24,61 +38,53 @@ module Stretchy
24
38
  end
25
39
  end
26
40
 
27
- def keyword?(arg)
28
- attr = @attribute_types[arg.to_s]
29
- return false unless attr
30
- attr.is_a?(Stretchy::Attributes::Type::Keyword)
41
+ def keyword_available?(arg)
42
+ attrib = @attribute_types[arg.to_s.split(".").first]
43
+ return false unless attrib
44
+ attrib.respond_to?(:keyword_field?) && attrib.keyword_field?
45
+ end
46
+
47
+ def keyword_field_for(arg)
48
+ attrib = @attribute_types[arg.to_s.split(".").first]
49
+ keyword_field = attrib.respond_to?(:fields) ? attrib.fields.find { |k,d| d[:type].to_sym == :keyword }&.first : nil
50
+ keyword_field || Stretchy.configuration.default_keyword_field
31
51
  end
32
52
 
33
53
  def protected?(arg)
34
54
  return false if arg.nil?
35
- Stretchy::Relations::AggregationMethods::AGGREGATION_METHODS.include?(arg.to_sym)
55
+ Stretchy::Relations::AggregationMethods.registry.include?(arg.to_sym)
36
56
  end
37
57
 
58
+ # Add `.keyword` to attributes that have a keyword subfield but aren't `:keywords`
59
+ # this is for text fields that have a keyword subfield
60
+ # `:text` and `:string` fields add a `:keyword` subfield to the attribute mapping automatically
38
61
  def transform(item, *ignore)
39
- item.each_with_object({}) do |(k, v), new_item|
40
- if ignore && ignore.include?(k)
41
- new_item[k] = v
62
+ return item unless Stretchy.configuration.auto_target_keywords
63
+ if item.is_a?(String)
64
+ return (!protected?(item) && keyword_available?(item)) ? "#{item}.#{keyword_field_for(item)}" : item
65
+ end
66
+ item.each_with_object({}) do |(key, value), new_item|
67
+ if ignore && ignore.include?(key)
68
+ new_item[key] = value
42
69
  next
43
70
  end
44
- new_key = (!protected?(k) && keyword?(k)) ? "#{k}.keyword" : k
45
71
 
46
- new_value = v
72
+ new_key = (!protected?(key) && keyword_available?(key)) ? "#{key}.#{keyword_field_for(key)}" : key
73
+
74
+ new_value = value
47
75
 
48
76
  if new_value.is_a?(Hash)
49
- new_value = transform(new_value)
77
+ new_value = transform(new_value, *ignore)
50
78
  elsif new_value.is_a?(Array)
51
- new_value = new_value.map { |i| i.is_a?(Hash) ? transform(i) : i }
79
+ new_value = new_value.map { |i| i.is_a?(Hash) ? transform(i, *ignore) : i }
52
80
  elsif new_value.is_a?(String) || new_value.is_a?(Symbol)
53
- new_value = "#{new_value}.keyword" if keyword?(new_value)
81
+ 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)}$")
54
82
  end
55
83
 
56
84
  new_item[new_key] = new_value
57
85
  end
58
86
  end
59
87
 
60
- # If terms are used, we assume that the field is a keyword field
61
- # and append .keyword to the field name
62
- # {terms: {field: 'gender'}}
63
- # or nested aggs
64
- # {terms: {field: 'gender'}, aggs: {name: {terms: {field: 'position.name'}}}}
65
- # should be converted to
66
- # {terms: {field: 'gender.keyword'}, aggs: {name: {terms: {field: 'position.name.keyword'}}}}
67
- # {date_histogram: {field: 'created_at', interval: 'day'}}
68
- # TODO: There may be cases where we don't want to add .keyword to the field and there should be a way to override this
69
- def assume_keyword_field(args={}, parent_match=false)
70
- if args.is_a?(Hash)
71
- args.each do |k, v|
72
- if v.is_a?(Hash)
73
- assume_keyword_field(v, KEYWORD_AGGREGATION_FIELDS.include?(k))
74
- else
75
- next unless v.is_a?(String) || v.is_a?(Symbol)
76
- args[k] = ([:field, :fields].include?(k.to_sym) && v !~ /\.keyword$/ && parent_match) ? "#{v}.keyword" : v.to_s
77
- end
78
- end
79
- end
80
- end
81
-
82
88
  end
83
89
  end
84
90
  end
@@ -1,5 +1,28 @@
1
1
  module Stretchy::Attributes::Type
2
- class Array < Stretchy::Attributes::Type::Base # :nodoc:
2
+ # The Array attribute type
3
+ #
4
+ # This class is used to define an array attribute for a model. It provides support for the Elasticsearch array data type, which is a type of data type that can hold multiple values.
5
+ #
6
+ # ### Parameters
7
+ #
8
+ # - `type:` `:array`.
9
+ # - `options:` The Hash of options for the attribute.
10
+ # - `:data_type:` The Symbol representing the data type for the array. Defaults to `:text`.
11
+ # - `:fields:` The Boolean indicating if fields should be included in the mapping. Defaults to `true`.
12
+ #
13
+ # ---
14
+ #
15
+ # ### Examples
16
+ #
17
+ # #### Define an array attribute
18
+ #
19
+ # ```ruby
20
+ # class MyModel < StretchyModel
21
+ # attribute :tags, :array, data_type: :text
22
+ # end
23
+ # ```
24
+ #
25
+ class Array < Stretchy::Attributes::Type::Base
3
26
  OPTIONS = [:data_type, :fields]
4
27
  def type
5
28
  :array
@@ -5,7 +5,6 @@ module Stretchy
5
5
 
6
6
  OPTIONS = []
7
7
 
8
-
9
8
  def initialize(**args)
10
9
 
11
10
  define_option_methods!
@@ -13,11 +12,16 @@ module Stretchy
13
12
  args.each do |k, v|
14
13
  if self.class::OPTIONS.include?(k)
15
14
  instance_variable_set("@#{k}", v)
16
- args.delete(k)
17
15
  end
16
+ args.delete(k)
18
17
  end
19
18
  super
20
19
  end
20
+
21
+ def keyword_field?
22
+ return false unless respond_to? :fields
23
+ fields.present? && fields.include?(:keyword)
24
+ end
21
25
 
22
26
  def mappings(name)
23
27
  options = {type: type_for_database}
@@ -1,23 +1,30 @@
1
- # Public: Defines a binary attribute for the model.
2
- #
3
- # name - The Symbol name of the attribute.
4
- # opts - The Hash options used to refine the attribute (default: {}):
5
- # :doc_values - The Boolean indicating if the field should be stored on disk in a column-stride fashion.
6
- # This allows it to be used later for sorting, aggregations, or scripting. Defaults to false.
7
- # :store - The Boolean indicating if the field value should be stored and retrievable separately from the _source field. Defaults to false.
8
- #
9
- # Examples
10
- #
11
- # class MyModel
12
- # include StretchyModel
13
- # attribute :name, :binary, doc_values: true, store: true
14
- # end
15
- #
16
- # Returns nothing.
17
1
  module Stretchy
18
2
  module Attributes
19
3
  module Type
20
- class Binary < ActiveModel::Type::Value
4
+ # The Binary attribute type
5
+ #
6
+ # This class is used to define a binary attribute for a model. It provides support for the Elasticsearch binary data type, which is a type of data type that can hold binary data.
7
+ #
8
+ # ### Parameters
9
+ #
10
+ # - `type:` `:binary`.
11
+ # - `options:` The Hash of options for the attribute.
12
+ # - `:doc_values:` The Boolean indicating if the field should be stored on disk in a column-stride fashion. This allows it to be used later for sorting, aggregations, or scripting. Defaults to false.
13
+ # - `:store:` The Boolean indicating if the field value should be stored and retrievable separately from the _source field. Defaults to false.
14
+ #
15
+ # ---
16
+ #
17
+ # ### Examples
18
+ #
19
+ # #### Define a binary attribute
20
+ #
21
+ # ```ruby
22
+ # class MyModel < StretchyModel
23
+ # attribute :data, :binary, doc_values: true, store: true
24
+ # end
25
+ # ```
26
+ #
27
+ class Binary < Stretchy::Attributes::Type::Base
21
28
  OPTIONS = [:doc_values, :store]
22
29
  attr_reader *OPTIONS
23
30
 
@@ -1,28 +1,35 @@
1
- # Public: Defines a boolean attribute for the model.
2
- #
3
- # name - The Symbol name of the attribute.
4
- # opts - The Hash options used to refine the attribute (default: {}):
5
- # :doc_values - The Boolean indicating if the field should be stored on disk in a column-stride fashion.
6
- # This allows it to be used later for sorting, aggregations, or scripting. Defaults to true.
7
- # :index - The Boolean indicating if the field should be quickly searchable. Defaults to true.
8
- # :ignore_malformed - The Boolean indicating if exceptions thrown when trying to index the wrong data type into a field should be ignored. Defaults to false.
9
- # :null_value - The Boolean value to be substituted for any explicit null values. Defaults to null.
10
- # :on_script_error - The String defining what to do if the script defined by the :script parameter throws an error at indexing time. Can be 'fail' or 'continue'.
11
- # :script - The String script that will index values generated by this script, rather than reading the values directly from the source.
12
- # :store - The Boolean indicating if the field value should be stored and retrievable separately from the _source field. Defaults to false.
13
- # :meta - The Hash metadata about the field.
14
- #
15
- # Examples
16
- #
17
- # class MyModel
18
- # include StretchyModel
19
- # attribute :name, :boolean, doc_values: true, store: true
20
- # end
21
- #
22
- # Returns nothing.
23
1
  module Stretchy
24
2
  module Attributes
25
3
  module Type
4
+ # The Boolean attribute type
5
+ #
6
+ # This class is used to define a boolean attribute for a model. It provides support for the Elasticsearch boolean data type, which is a type of data type that can hold true or false values.
7
+ #
8
+ # ### Parameters
9
+ #
10
+ # - `type:` `:boolean`.
11
+ # - `options:` The Hash of options for the attribute.
12
+ # - `:doc_values:` The Boolean indicating if the field should be stored on disk in a column-stride fashion. This allows it to be used later for sorting, aggregations, or scripting. Defaults to true.
13
+ # - `:index:` The Boolean indicating if the field should be quickly searchable. Defaults to true.
14
+ # - `:ignore_malformed:` The Boolean indicating if exceptions thrown when trying to index the wrong data type into a field should be ignored. Defaults to false.
15
+ # - `:null_value:` The Boolean value to be substituted for any explicit null values. Defaults to null.
16
+ # - `:on_script_error:` The String defining what to do if the script defined by the :script parameter throws an error at indexing time. Can be 'fail' or 'continue'.
17
+ # - `:script:` The String script that will index values generated by this script, rather than reading the values directly from the source.
18
+ # - `:store:` The Boolean indicating if the field value should be stored and retrievable separately from the _source field. Defaults to false.
19
+ # - `:meta:` The Hash metadata about the field.
20
+ #
21
+ # ---
22
+ #
23
+ # ### Examples
24
+ #
25
+ # #### Define a boolean attribute
26
+ #
27
+ # ```ruby
28
+ # class MyModel < StretchyModel
29
+ # attribute :is_active, :boolean, doc_values: true, store: true
30
+ # end
31
+ # ```
32
+ #
26
33
  class Boolean < Stretchy::Attributes::Type::Base
27
34
 
28
35
  OPTIONS = [:doc_values, :index, :ignore_malformed, :null_value, :on_script_error, :script, :store, :meta]
@@ -1,20 +1,28 @@
1
1
  module Stretchy::Attributes::Type
2
- # Public: Defines a completion attribute for the model.
2
+ # Completion attribute type for Elasticsearch completion suggester.
3
3
  #
4
- # opts - The Hash options used to refine the attribute (default: {}):
5
- # :analyzer - The String index analyzer to use. Defaults to 'simple'.
6
- # :search_analyzer - The String search analyzer to use. Defaults to the value of :analyzer.
7
- # :preserve_separators - The Boolean indicating if separators should be preserved. Defaults to true.
8
- # :preserve_position_increments - The Boolean indicating if position increments should be enabled. Defaults to true.
9
- # :max_input_length - The Integer limit for the length of a single input. Defaults to 50.
4
+ # This class is used to define a completion attribute for a model. It provides support for the Elasticsearch completion suggester, which is a type of suggester that provides auto-complete functionality.
10
5
  #
11
- # Examples
6
+ # ### Parameters
12
7
  #
13
- # class MyModel < Stretchy::Record
8
+ # - `analyzer:` The String index analyzer to use. Defaults to 'simple'.
9
+ # - `search_analyzer:` The String search analyzer to use. Defaults to the value of `analyzer`.
10
+ # - `preserve_separators:` The Boolean indicating if separators should be preserved. Defaults to true.
11
+ # - `preserve_position_increments:` The Boolean indicating if position increments should be enabled. Defaults to true.
12
+ # - `max_input_length:` The Integer limit for the length of a single input. Defaults to 50.
13
+ #
14
+ # ---
15
+ #
16
+ # ### Examples
17
+ #
18
+ # #### Define a completion attribute
19
+ #
20
+ # ```ruby
21
+ # class MyModel < StretchyModel
14
22
  # attribute :name, :completion, analyzer: 'simple', max_input_length: 100
15
23
  # end
24
+ # ```
16
25
  #
17
- # Returns nothing.
18
26
  class Completion < Stretchy::Attributes::Type::Base
19
27
  OPTIONS = [:analyzer, :search_analyzer, :preserve_separators, :preserve_position_increments, :max_input_length]
20
28
  attr_reader *OPTIONS
@@ -1,33 +1,42 @@
1
1
  module Stretchy::Attributes::Type
2
- # Public: Defines a constant_keyword attribute for the model. This field type is a specialization of the keyword field, but it only accepts a single value.
3
- #
4
- # opts - The Hash options used to refine the attribute (default: {}):
5
- # :doc_values - The Boolean indicating if the field should be stored on disk in a column-stride fashion. Defaults to true.
6
- # :eager_global_ordinals - The Boolean indicating if global ordinals should be loaded eagerly on refresh. Defaults to false.
7
- # :fields - The Hash of multi-fields for the same string value to be indexed in multiple ways.
8
- # :ignore_above - The Integer limit for the length of the string. Strings longer than this limit will not be indexed. Defaults to 2147483647.
9
- # :index - The Boolean indicating if the field should be quickly searchable. Defaults to true.
10
- # :index_options - The String indicating what information should be stored in the index for scoring purposes. Defaults to 'docs'.
11
- # :meta - The Hash metadata about the field.
12
- # :norms - The Boolean indicating if field-length should be taken into account when scoring queries. Defaults to false.
13
- # :null_value - The String value to be substituted for any explicit null values. Defaults to null.
14
- # :on_script_error - The String defining what to do if the script defined by the :script parameter throws an error at indexing time. Can be 'fail' or 'continue'.
15
- # :script - The String script that will index values generated by this script, rather than reading the values directly from the source.
16
- # :store - The Boolean indicating if the field value should be stored and retrievable separately from the _source field. Defaults to false.
17
- # :similarity - The String scoring algorithm or similarity to be used. Defaults to 'BM25'.
18
- # :normalizer - The String pre-processor for the keyword prior to indexing. Defaults to null.
19
- # :split_queries_on_whitespace - The Boolean indicating if full text queries should split the input on whitespace. Defaults to false.
20
- # :time_series_dimension - The Boolean indicating if the field is a time series dimension. Defaults to false.
21
- # :value - The String value to associate with all documents in the index.
22
- #
23
- # Examples
24
- #
25
- # class MyModel
26
- # include StretchyModel
2
+ # The ConstantKeyword attribute type
3
+ #
4
+ # This class is used to define a constant_keyword attribute for a model. This field type is a specialization of the keyword field, but it only accepts a single value.
5
+ #
6
+ # ### Parameters
7
+ #
8
+ # - `type:` `:constant_keyword`.
9
+ # - `options:` The Hash of options for the attribute.
10
+ # - `:doc_values:` The Boolean indicating if the field should be stored on disk in a column-stride fashion. Defaults to true.
11
+ # - `:eager_global_ordinals:` The Boolean indicating if global ordinals should be loaded eagerly on refresh. Defaults to false.
12
+ # - `:fields:` The Hash of multi-fields for the same string value to be indexed in multiple ways.
13
+ # - `:ignore_above:` The Integer limit for the length of the string. Strings longer than this limit will not be indexed. Defaults to 2147483647.
14
+ # - `:index:` The Boolean indicating if the field should be quickly searchable. Defaults to true.
15
+ # - `:index_options:` The String indicating what information should be stored in the index for scoring purposes. Defaults to 'docs'.
16
+ # - `:meta:` The Hash metadata about the field.
17
+ # - `:norms:` The Boolean indicating if field-length should be taken into account when scoring queries. Defaults to false.
18
+ # - `:null_value:` The String value to be substituted for any explicit null values. Defaults to null.
19
+ # - `:on_script_error:` The String defining what to do if the script defined by the :script parameter throws an error at indexing time. Can be 'fail' or 'continue'.
20
+ # - `:script:` The String script that will index values generated by this script, rather than reading the values directly from the source.
21
+ # - `:store:` The Boolean indicating if the field value should be stored and retrievable separately from the _source field. Defaults to false.
22
+ # - `:similarity:` The String scoring algorithm or similarity to be used. Defaults to 'BM25'.
23
+ # - `:normalizer:` The String pre-processor for the keyword prior to indexing. Defaults to null.
24
+ # - `:split_queries_on_whitespace:` The Boolean indicating if full text queries should split the input on whitespace. Defaults to false.
25
+ # - `:time_series_dimension:` The Boolean indicating if the field is a time series dimension. Defaults to false.
26
+ # - `:value:` The String value to associate with all documents in the index.
27
+ #
28
+ # ---
29
+ #
30
+ # ### Examples
31
+ #
32
+ # #### Define a constant_keyword attribute
33
+ #
34
+ # ```ruby
35
+ # class MyModel < StretchyModel
27
36
  # attribute :status, :constant_keyword, value: 'active'
28
37
  # end
38
+ # ```
29
39
  #
30
- # Returns nothing.
31
40
  class ConstantKeyword < Stretchy::Attributes::Type::Keyword
32
41
  OPTIONS = OPTIONS + [:value]
33
42
  attr_reader *OPTIONS
@@ -1,35 +1,96 @@
1
1
  module Stretchy::Attributes::Type
2
- # Public: Defines a datetime attribute for the model.
3
- #
4
- # opts - The Hash options used to refine the attribute (default: {}):
5
- # :doc_values - The Boolean indicating if the field should be stored on disk in a column-stride fashion.
6
- # This allows it to be used later for sorting, aggregations, or scripting. Defaults to true.
7
- # :format - The String date format(s) that can be parsed. Defaults to 'strict_date_optional_time||epoch_millis'.
8
- # :locale - The String locale to use when parsing dates. Defaults to the ROOT locale.
9
- # :ignore_malformed - The Boolean indicating if malformed numbers should be ignored. Defaults to false.
10
- # :index - The Boolean indicating if the field should be quickly searchable. Defaults to true.
11
- # :null_value - The Date value to be substituted for any explicit null values. Defaults to null.
12
- # :on_script_error - The String defining what to do if the script defined by the :script parameter throws an error at indexing time. Can be 'fail' or 'continue'.
13
- # :script - The String script that will index values generated by this script, rather than reading the values directly from the source.
14
- # :store - The Boolean indicating if the field value should be stored and retrievable separately from the _source field. Defaults to false.
15
- # :meta - The Hash metadata about the field.
16
- #
17
- # Examples
18
- #
19
- # class MyModel < Stretchy::Record
2
+ # The DateTime attribute type
3
+ #
4
+ # This class is used to define a datetime attribute for a model. It provides support for the Elasticsearch date data type, which is a type of data type that can hold dates.
5
+ #
6
+ # ### Parameters
7
+ #
8
+ # - `type:` `:datetime`.
9
+ # - `options:` The Hash of options for the attribute.
10
+ # - `:doc_values:` The Boolean indicating if the field should be stored on disk in a column-stride fashion. Defaults to true.
11
+ # - `:format:` The String date format(s) that can be parsed. Defaults to 'strict_date_optional_time||epoch_millis'.
12
+ # - `:locale:` The String locale to use when parsing dates. Defaults to the ROOT locale.
13
+ # - `:ignore_malformed:` The Boolean indicating if malformed numbers should be ignored. Defaults to false.
14
+ # - `:index:` The Boolean indicating if the field should be quickly searchable. Defaults to true.
15
+ # - `:null_value:` The Date value to be substituted for any explicit null values. Defaults to null.
16
+ # - `:on_script_error:` The String defining what to do if the script defined by the :script parameter throws an error at indexing time. Can be 'fail' or 'continue'.
17
+ # - `:script:` The String script that will index values generated by this script, rather than reading the values directly from the source.
18
+ # - `:store:` The Boolean indicating if the field value should be stored and retrievable separately from the _source field. Defaults to false.
19
+ # - `:meta:` The Hash metadata about the field.
20
+ #
21
+ # ---
22
+ #
23
+ # ### Examples
24
+ #
25
+ # #### Define a datetime attribute
26
+ #
27
+ # ```ruby
28
+ # class MyModel < StretchyModel
20
29
  # attribute :created_at, :datetime, format: 'strict_date_optional_time||epoch_millis', locale: 'en'
21
30
  # end
31
+ # ```
22
32
  #
23
- # Returns nothing.
24
33
  class DateTime < Stretchy::Attributes::Type::Base
25
34
  OPTIONS = [:doc_values, :format, :locale, :ignore_malformed, :index, :null_value, :on_script_error, :script, :store, :meta]
26
- attr_reader *OPTIONS
35
+ attr_reader *OPTIONS + self.superclass::OPTIONS
36
+ include ActiveModel::Type::Helpers::Timezone
37
+ include ActiveModel::Type::Helpers::AcceptsMultiparameterTime.new(
38
+ defaults: { 4 => 0, 5 => 0 }
39
+ )
40
+ include ActiveModel::Type::Helpers::TimeValue
41
+
42
+ def initialize(**args)
43
+ @model_format = args.delete(:model_format)
44
+ super
45
+ end
46
+
27
47
  def type
28
48
  :datetime
29
49
  end
30
50
 
51
+ # Returns the type `:date` for the database.
52
+ #
31
53
  def type_for_database
32
54
  :date
33
55
  end
56
+
57
+ private
58
+ def cast_value(value)
59
+ return apply_seconds_precision(value) unless value.is_a?(::String)
60
+ return if value.empty?
61
+
62
+ fast_string_to_time(value) || fallback_string_to_time(value) || custom_string_to_time(value)
63
+ end
64
+
65
+ # '0.123456' -> 123456
66
+ # '1.123456' -> 123456
67
+ def microseconds(time)
68
+ time[:sec_fraction] ? (time[:sec_fraction] * 1_000_000).to_i : 0
69
+ end
70
+
71
+ def custom_string_to_time(string)
72
+ ::Date.strptime(string, @model_format)
73
+ end
74
+
75
+ def fallback_string_to_time(string)
76
+ time_hash = begin
77
+ ::Date._parse(string)
78
+ rescue ArgumentError => e
79
+ end
80
+ return unless time_hash
81
+
82
+ time_hash[:sec_fraction] = microseconds(time_hash)
83
+
84
+ new_time(*time_hash.values_at(:year, :mon, :mday, :hour, :min, :sec, :sec_fraction, :offset))
85
+ end
86
+
87
+ def value_from_multiparameter_assignment(values_hash)
88
+ missing_parameters = [1, 2, 3].delete_if { |key| values_hash.key?(key) }
89
+ unless missing_parameters.empty?
90
+ raise ArgumentError, "Provided hash #{values_hash} doesn't contain necessary keys: #{missing_parameters}"
91
+ end
92
+ super
93
+ end
94
+
34
95
  end
35
96
  end