stretchy-model 0.4.0 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.rspec +1 -1
- data/README.md +19 -84
- data/lib/rails/instrumentation/railtie.rb +2 -0
- data/lib/stretchy/associations.rb +155 -15
- data/lib/stretchy/attributes/type/array.rb +20 -0
- data/lib/stretchy/attributes/type/base.rb +42 -0
- data/lib/stretchy/attributes/type/binary.rb +45 -0
- data/lib/stretchy/attributes/type/boolean.rb +48 -0
- data/lib/stretchy/attributes/type/completion.rb +25 -0
- data/lib/stretchy/attributes/type/constant_keyword.rb +38 -0
- data/lib/stretchy/attributes/type/date_time.rb +35 -0
- data/lib/stretchy/attributes/type/dense_vector.rb +59 -0
- data/lib/stretchy/attributes/type/flattened.rb +31 -0
- data/lib/stretchy/attributes/type/geo_point.rb +27 -0
- data/lib/stretchy/attributes/type/geo_shape.rb +27 -0
- data/lib/stretchy/attributes/type/hash.rb +40 -0
- data/lib/stretchy/attributes/type/histogram.rb +7 -0
- data/lib/stretchy/attributes/type/ip.rb +29 -0
- data/lib/stretchy/attributes/type/join.rb +22 -0
- data/lib/stretchy/attributes/type/keyword.rb +36 -10
- data/lib/stretchy/attributes/type/match_only_text.rb +8 -0
- data/lib/stretchy/attributes/type/nested.rb +25 -0
- data/lib/stretchy/attributes/type/numeric/base.rb +32 -0
- data/lib/stretchy/attributes/type/numeric/byte.rb +7 -0
- data/lib/stretchy/attributes/type/numeric/double.rb +7 -0
- data/lib/stretchy/attributes/type/numeric/float.rb +7 -0
- data/lib/stretchy/attributes/type/numeric/half_float.rb +7 -0
- data/lib/stretchy/attributes/type/numeric/integer.rb +7 -0
- data/lib/stretchy/attributes/type/numeric/long.rb +7 -0
- data/lib/stretchy/attributes/type/numeric/scaled_float.rb +23 -0
- data/lib/stretchy/attributes/type/numeric/short.rb +7 -0
- data/lib/stretchy/attributes/type/numeric/unsigned_long.rb +7 -0
- data/lib/stretchy/attributes/type/percolator.rb +23 -0
- data/lib/stretchy/attributes/type/point.rb +24 -0
- data/lib/stretchy/attributes/type/range/base.rb +9 -0
- data/lib/stretchy/attributes/type/range/date_range.rb +17 -0
- data/lib/stretchy/attributes/type/range/double_range.rb +17 -0
- data/lib/stretchy/attributes/type/range/float_range.rb +16 -0
- data/lib/stretchy/attributes/type/range/integer_range.rb +16 -0
- data/lib/stretchy/attributes/type/range/ip_range.rb +16 -0
- data/lib/stretchy/attributes/type/range/long_range.rb +16 -0
- data/lib/stretchy/attributes/type/rank_feature.rb +21 -0
- data/lib/stretchy/attributes/type/rank_features.rb +24 -0
- data/lib/stretchy/attributes/type/search_as_you_type.rb +30 -0
- data/lib/stretchy/attributes/type/shape.rb +24 -0
- data/lib/stretchy/attributes/type/sparse_vector.rb +37 -0
- data/lib/stretchy/attributes/type/string.rb +7 -0
- data/lib/stretchy/attributes/type/text.rb +51 -0
- data/lib/stretchy/attributes/type/token_count.rb +26 -0
- data/lib/stretchy/attributes/type/version.rb +21 -0
- data/lib/stretchy/attributes/type/wildcard.rb +35 -0
- data/lib/stretchy/attributes.rb +68 -2
- data/lib/stretchy/common.rb +5 -5
- data/lib/stretchy/delegation/gateway_delegation.rb +9 -5
- data/lib/stretchy/model/serialization.rb +1 -0
- data/lib/stretchy/querying.rb +4 -4
- data/lib/stretchy/record.rb +9 -10
- data/lib/stretchy/relation.rb +11 -17
- data/lib/stretchy/relations/finder_methods.rb +19 -2
- data/lib/stretchy/relations/merger.rb +5 -1
- data/lib/stretchy/relations/query_builder.rb +32 -3
- data/lib/stretchy/relations/query_methods.rb +66 -2
- data/lib/stretchy/scoping/named.rb +1 -1
- data/lib/stretchy/shared_scopes.rb +1 -1
- data/lib/stretchy/version.rb +1 -1
- data/lib/stretchy.rb +5 -3
- data/lib/stretchy_model.rb +9 -0
- metadata +49 -4
- data/lib/active_model/type/array.rb +0 -13
- data/lib/active_model/type/hash.rb +0 -15
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f23580fe0c8761ced02da75cbc44f16ab7893a559d30d9a5e0de23aa96ed790a
|
4
|
+
data.tar.gz: 821381d3e9e92822d71aac637d98f8b04a76736a80e421ca7d2c4fd05fd108d8
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 99dc36ee6cce021e869a8e1f3e2f005f546b1c50bf973434c624574c1dd601757baf4a1d39562f7ddd05bf4a1e640512111b821d8a70b8d5416bf73701b38f03
|
7
|
+
data.tar.gz: 4a58307bd7ae5a5781e7462df9c95438f66eecab5d0ae43238878d228e2ca6939a8a7643b70ac3c2586dcae537935206bfb15339415f6b3c3a70ef2518e401b4
|
data/.rspec
CHANGED
@@ -1 +1 @@
|
|
1
|
-
--require spec_helper
|
1
|
+
--require spec_helper
|
data/README.md
CHANGED
@@ -1,6 +1,5 @@
|
|
1
1
|
stretchy-model
|
2
2
|
===
|
3
|
-
|
4
3
|
<p>
|
5
4
|
<a href="https://stretchy.io/" target="_blank"><img src="./stretchy.logo.png" alt="Gum Image" width="450" /></a>
|
6
5
|
<br><br>
|
@@ -9,104 +8,42 @@ stretchy-model
|
|
9
8
|
|
10
9
|
</p>
|
11
10
|
|
12
|
-
Stretchy provides Elasticsearch models in a Rails environment with an integrated ActiveRecord-like interface and features.
|
13
11
|
|
14
12
|
## Features
|
15
13
|
Stretchy simplifies the process of querying, aggregating, and managing Elasticsearch-backed models, allowing Rails developers to work with search indices as comfortably as they would with traditional Rails models.
|
16
14
|
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
attribute :flagged, :boolean, default: false
|
25
|
-
attribute :author, :hash
|
26
|
-
attribute :tags, :array, default: []
|
27
|
-
|
28
|
-
end
|
29
|
-
```
|
30
|
-
>[!NOTE]
|
31
|
-
>`created_at`, `:updated_at` and `:id` are automatically added to all `Stretchy::Records`
|
32
|
-
|
33
|
-
|
34
|
-
## Query
|
35
|
-
```ruby
|
36
|
-
Post.where('author.name': "Jadzia", flagged: true).first
|
37
|
-
#=> <Post id: aW02w3092, title: "Fun Cats", body: "...", flagged: true,
|
38
|
-
# author: {name: "Jadzia", age: 20}, tags: ["cat", "amusing"]>
|
39
|
-
```
|
40
|
-
|
41
|
-
## Aggregations
|
42
|
-
```ruby
|
43
|
-
|
44
|
-
result = Post.filter(:range, 'author.age': {gte: 18})
|
45
|
-
.aggregation(:post_frequency, date_histogram: {
|
46
|
-
field: :created_at,
|
47
|
-
calender_interval: :month
|
48
|
-
})
|
49
|
-
|
50
|
-
result.aggregations.post_frequency
|
51
|
-
#=> {buckets: [{key_as_string: "2024-01-01", doc_count: 20}, ...]}
|
52
|
-
```
|
53
|
-
|
54
|
-
## Scopes
|
55
|
-
|
56
|
-
```ruby
|
57
|
-
class Post < Stretchy::Record
|
58
|
-
# ...attributes
|
59
|
-
|
60
|
-
# Scopes
|
61
|
-
scope :flagged, -> { where(flagged: true) }
|
62
|
-
scope :top_links, lambda do |size=10, url=".com"|
|
63
|
-
aggregation(:links,
|
64
|
-
terms: {
|
65
|
-
field: :links,
|
66
|
-
size: size,
|
67
|
-
include: ".*#{url}.*"
|
68
|
-
})
|
69
|
-
end
|
70
|
-
end
|
15
|
+
* Model fully back by Elasticsearch/Opensearch
|
16
|
+
* Chain queries, scopes and aggregations
|
17
|
+
* Reduce Elasticsearch query complexity
|
18
|
+
* Support for time-based indices and aliases
|
19
|
+
* Associations to both ActiveRecord models and Stretchy::Record
|
20
|
+
* Bulk Operations made easy
|
21
|
+
* Validations, custom attributes, and more...
|
71
22
|
|
72
|
-
|
73
|
-
# links in results.aggregations.links
|
74
|
-
result = Post.flagged.top_links(10, "youtube.com")
|
23
|
+
Follow the guides to learn more about:
|
75
24
|
|
76
|
-
|
25
|
+
* [Models](https://theablefew.github.io/stretchy/#/guides/models?id=models)
|
26
|
+
* [Querying](https://theablefew.github.io/stretchy/#/guides/querying?id=querying)
|
27
|
+
* [Aggregations](https://theablefew.github.io/stretchy/#/guides/aggregations?id=aggregations)
|
28
|
+
* [Scopes](https://theablefew.github.io/stretchy/#/guides/scopes?id=scopes)
|
77
29
|
|
78
|
-
## Bulk Operations
|
79
30
|
|
31
|
+
[Read the Documentation](https://theablefew.github.io/stretchy/#/) or walk through of a simple [Data Analysis](https://theablefew.github.io/stretchy/#/examples/data_analysis?id=data-analysis) example.
|
80
32
|
|
81
|
-
```ruby
|
82
|
-
Model.bulk(records_as_bulk_operations)
|
83
|
-
```
|
84
33
|
|
85
|
-
#### Bulk helper
|
86
|
-
Generates structure for the bulk operation
|
87
|
-
```ruby
|
88
|
-
record.to_bulk # default to_bulk(:index)
|
89
|
-
record.to_bulk(:delete)
|
90
|
-
record.to_bulk(:update)
|
91
|
-
```
|
92
|
-
|
93
|
-
#### In batches
|
94
|
-
Run bulk operations in batches specified by `size`
|
95
|
-
```ruby
|
96
|
-
Model.bulk_in_batches(records, size: 100) do |batch|
|
97
|
-
batch.map! { |record| Model.new(record).to_bulk }
|
98
|
-
end
|
99
|
-
```
|
100
34
|
|
101
35
|
## Installation
|
102
36
|
|
103
37
|
Install the gem and add to the application's Gemfile by executing:
|
104
38
|
|
105
|
-
|
39
|
+
```sh
|
40
|
+
bundle add stretchy-model
|
41
|
+
```
|
106
42
|
|
107
43
|
If bundler is not being used to manage dependencies, install the gem by executing:
|
108
|
-
|
109
|
-
|
44
|
+
```sh
|
45
|
+
gem install stretchy-model
|
46
|
+
```
|
110
47
|
|
111
48
|
<details>
|
112
49
|
<summary>Rails Configuration</summary>
|
@@ -145,8 +82,6 @@ end
|
|
145
82
|
After checking out the repo, run `bin/setup` to install dependencies. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
146
83
|
|
147
84
|
>[!TIP]
|
148
|
-
>This library is built on top of the excellent [elasticsearch-persistence](https://github.com/elastic/elasticsearch-rails/tree/main/elasticsearch-persistence) gem.
|
149
|
-
>
|
150
85
|
> Full documentation on [Elasticsearch Query DSL and Aggregation options](https://github.com/elastic/elasticsearch-rails/tree/main/elasticsearch-persistence)
|
151
86
|
|
152
87
|
## Testing
|
@@ -4,6 +4,8 @@ module Stretchy
|
|
4
4
|
class Railtie < ::Rails::Railtie
|
5
5
|
|
6
6
|
require 'rails/instrumentation/publishers'
|
7
|
+
Stretchy.instrument!
|
8
|
+
|
7
9
|
ActiveSupport::Notifications.subscribe 'search.stretchy' do |name, start, finish, id, payload|
|
8
10
|
message = [
|
9
11
|
Rainbow(" #{payload[:klass]}").bright,
|
@@ -3,7 +3,11 @@ module Stretchy
|
|
3
3
|
extend ActiveSupport::Concern
|
4
4
|
|
5
5
|
def save!
|
6
|
+
if valid?
|
6
7
|
self.save
|
8
|
+
else
|
9
|
+
raise "Record is invalid"
|
10
|
+
end
|
7
11
|
end
|
8
12
|
|
9
13
|
# Required for Elasticsearch < 7
|
@@ -34,7 +38,7 @@ module Stretchy
|
|
34
38
|
end
|
35
39
|
|
36
40
|
def association_reflection(association)
|
37
|
-
|
41
|
+
Stretchy::Relation.new @@_associations[association], (dirty[association.to_sym] || {})
|
38
42
|
end
|
39
43
|
|
40
44
|
def _destroy=(bool)
|
@@ -48,6 +52,7 @@ module Stretchy
|
|
48
52
|
def save_associations
|
49
53
|
@_after_save_objects.each_pair do |association, collection|
|
50
54
|
collection.each do |instance|
|
55
|
+
# TODO: bulk update
|
51
56
|
instance.send("#{@@_association_options[association.to_sym][:foreign_key]}=", self.id)
|
52
57
|
instance.save
|
53
58
|
end
|
@@ -59,59 +64,194 @@ module Stretchy
|
|
59
64
|
@@_associations ||= {}
|
60
65
|
@@_association_options ||= {}
|
61
66
|
|
67
|
+
# The belongs_to method is used to set up a one-to-one connection with another model.
|
68
|
+
# This indicates that this model has exactly one instance of another model.
|
69
|
+
# For example, if your application includes authors and books, and each book can be assigned exactly one author,
|
70
|
+
# you'd declare the book model to belong to the author model.
|
71
|
+
#
|
72
|
+
# association:: [Symbol] the name of the association
|
73
|
+
# options:: [Hash] a hash to set up options for the association
|
74
|
+
# :foreign_key - the foreign key used for the association. Defaults to "#{association}_id"
|
75
|
+
# :primary_key - the primary key used for the association. Defaults to "id"
|
76
|
+
# :class_name - the name of the associated object's class. Defaults to the name of the association
|
77
|
+
#
|
78
|
+
# Example:
|
79
|
+
# belongs_to :author
|
80
|
+
#
|
81
|
+
# This creates a book.author method that returns the author of the book.
|
82
|
+
# It also creates an author= method that allows you to assign the author of the book.
|
83
|
+
#
|
62
84
|
def belongs_to(association, options = {})
|
63
85
|
@@_association_options[association] = {
|
64
86
|
foreign_key: "#{association}_id",
|
65
87
|
primary_key: "id",
|
66
88
|
class_name: association
|
67
|
-
}.
|
89
|
+
}.merge(options)
|
68
90
|
|
69
91
|
klass = @@_association_options[association][:class_name].to_s.singularize.classify.constantize
|
70
92
|
@@_associations[association] = klass
|
71
93
|
|
72
94
|
define_method(association.to_sym) do
|
73
|
-
|
95
|
+
instance_variable_get("@#{association}") ||
|
96
|
+
klass.where(_id: self.send(@@_association_options[association][:foreign_key].to_sym)).first
|
74
97
|
end
|
75
98
|
|
76
99
|
define_method("#{association}=".to_sym) do |val|
|
77
100
|
options = @@_association_options[association]
|
78
|
-
|
101
|
+
self.send("#{options[:foreign_key]}=", val.send(options[:primary_key]))
|
102
|
+
instance_variable_set("@#{association}", val)
|
103
|
+
end
|
104
|
+
|
105
|
+
define_method("build_#{association}") do |*args|
|
106
|
+
associated_object = klass.new(*args)
|
107
|
+
instance_variable_set("@#{association}", associated_object)
|
108
|
+
associated_object
|
109
|
+
end
|
110
|
+
|
111
|
+
before_save do
|
112
|
+
associated_object = instance_variable_get("@#{association}")
|
113
|
+
if associated_object && associated_object.new_record?
|
114
|
+
if associated_object.save!
|
115
|
+
self.send("#{@@_association_options[association][:foreign_key]}=", associated_object.id)
|
116
|
+
end
|
117
|
+
end
|
79
118
|
end
|
80
119
|
end
|
81
120
|
|
82
|
-
def has_one(association, class_name: nil, foreign_key: nil, dependent: :destroy)
|
83
121
|
|
84
|
-
|
85
|
-
|
122
|
+
|
123
|
+
|
124
|
+
|
125
|
+
|
126
|
+
|
127
|
+
|
128
|
+
|
129
|
+
|
130
|
+
# The has_one method is used to set up a one-to-one connection with another model.
|
131
|
+
# This indicates that this model contains the foreign key.
|
132
|
+
#
|
133
|
+
# association:: [Symbol] The name of the association.
|
134
|
+
# options:: [Hash] A hash to set up options for the association.
|
135
|
+
# :class_name - The name of the associated model. If not provided, it's derived from +association+.
|
136
|
+
# :foreign_key - The name of the foreign key on the associated model. If not provided, it's derived from the name of this model.
|
137
|
+
# :dependent - If set to +:destroy+, the associated object will be destroyed when this object is destroyed. This is the default behavior.
|
138
|
+
# :primary_key - The name of the primary key on the associated model. If not provided, it's assumed to be +id+.
|
139
|
+
#
|
140
|
+
#
|
141
|
+
# Example:
|
142
|
+
# has_one :profile
|
143
|
+
#
|
144
|
+
# This creates a user.profile method that returns the profile of the user.
|
145
|
+
# It also creates a profile= method that allows you to assign the profile of the user.
|
146
|
+
#
|
147
|
+
def has_one(association, options = {})
|
148
|
+
|
149
|
+
@@_association_options[association] = {
|
150
|
+
foreign_key: "#{self.name.underscore}_id",
|
151
|
+
primary_key: "id",
|
152
|
+
class_name: association
|
153
|
+
}.merge(options)
|
154
|
+
|
155
|
+
klass = @@_association_options[association][:class_name].to_s.singularize.classify.constantize
|
86
156
|
@@_associations[association] = klass
|
87
157
|
|
158
|
+
foreign_key = @@_association_options[association][:foreign_key]
|
159
|
+
|
88
160
|
define_method(association.to_sym) do
|
89
|
-
|
161
|
+
instance_variable_get("@#{association}") ||
|
162
|
+
klass.where("#{foreign_key}": self.id).first
|
163
|
+
end
|
164
|
+
|
165
|
+
define_method("#{association}=".to_sym) do |val|
|
166
|
+
instance_variable_set("@#{association}", val)
|
167
|
+
save!
|
168
|
+
end
|
169
|
+
|
170
|
+
before_save do
|
171
|
+
associated_object = instance_variable_get("@#{association}")
|
172
|
+
if associated_object
|
173
|
+
associated_object.send("#{foreign_key}=", self.id)
|
174
|
+
associated_object.save!
|
175
|
+
end
|
90
176
|
end
|
91
177
|
end
|
92
178
|
|
93
|
-
def has_many(association, klass, options = {})
|
94
|
-
@@_associations[association] = klass
|
95
179
|
|
96
|
-
opt_fk = options.delete(:foreign_key)
|
97
|
-
foreign_key = opt_fk ? opt_fk : "#{self.name.split("::").last.tableize.singularize}_id"
|
98
180
|
|
99
|
-
|
181
|
+
|
182
|
+
|
183
|
+
|
184
|
+
|
185
|
+
|
186
|
+
|
187
|
+
# The has_many method is used to set up a one-to-many connection with another model.
|
188
|
+
# This indicates that this model can be matched with zero or more instances of another model.
|
189
|
+
# For example, if your application includes authors and books, and each author can have many books,
|
190
|
+
# you'd declare the author model to have many books.
|
191
|
+
#
|
192
|
+
# association:: [Symbol] the name of the association
|
193
|
+
# options:: [Hash] a hash to set up options for the association
|
194
|
+
# :foreign_key - the foreign key used for the association. Defaults to "#{self.name.downcase}_id"
|
195
|
+
# :primary_key - the primary key used for the association. Defaults to "id"
|
196
|
+
# :class_name - the name of the associated object's class. Defaults to the name of the association
|
197
|
+
# :dependent - if set to :destroy, the associated object will be destroyed when this object is destroyed. This is the default behavior.
|
198
|
+
#
|
199
|
+
#
|
200
|
+
# Example:
|
201
|
+
# has_many :books
|
202
|
+
#
|
203
|
+
# This creates an author.books method that returns a collection of books for the author.
|
204
|
+
# It also creates a books= method that allows you to assign the books for the author.
|
205
|
+
#
|
206
|
+
def has_many(association, options = {})
|
207
|
+
@@_association_options[association] = {
|
208
|
+
foreign_key: "#{self.name.underscore}_id",
|
209
|
+
primary_key: "id",
|
210
|
+
class_name: association.to_s.singularize.to_sym
|
211
|
+
}.merge(options)
|
212
|
+
|
213
|
+
klass = @@_association_options[association][:class_name].to_s.classify.constantize
|
214
|
+
foreign_key = @@_association_options[association][:foreign_key]
|
215
|
+
primary_key = @@_association_options[association][:primary_key]
|
216
|
+
@@_associations[association] = klass
|
100
217
|
|
101
218
|
define_method(association.to_sym) do
|
102
219
|
args = {}
|
103
|
-
args[
|
220
|
+
args["_#{primary_key}"] = self.send("#{association.to_s.singularize}_ids")
|
104
221
|
self.new_record? ? association_reflection(association) : klass.where(args)
|
105
222
|
end
|
106
223
|
|
224
|
+
define_method("#{association.to_s.singularize}_ids") do
|
225
|
+
instance_variable_get("@#{association.to_s.singularize}_ids".to_sym)
|
226
|
+
end
|
227
|
+
|
228
|
+
define_method("#{association.to_s.singularize}_ids=") do |val|
|
229
|
+
instance_variable_set("@#{association.to_s.singularize}_ids".to_sym, val)
|
230
|
+
end
|
231
|
+
|
232
|
+
define_method("#{association}=".to_sym) do |val|
|
233
|
+
val.each { |v| after_save_objects(v.attributes, association)}
|
234
|
+
self.send("#{association.to_s.singularize}_ids=", val.map(&:id))
|
235
|
+
dirty
|
236
|
+
end
|
237
|
+
|
107
238
|
define_method("build_#{association}".to_sym) do |*args|
|
108
239
|
opts = {}
|
109
240
|
opts[foreign_key] = self.id
|
110
241
|
args.first.merge! opts
|
111
242
|
klass.new *args
|
112
243
|
end
|
244
|
+
|
245
|
+
after_save do
|
246
|
+
save_associations
|
247
|
+
end
|
113
248
|
end
|
114
249
|
|
250
|
+
|
251
|
+
|
252
|
+
|
253
|
+
|
254
|
+
|
115
255
|
def validates_associated(*attr_names)
|
116
256
|
validates_with AssociatedValidator, _merge_attributes(attr_names)
|
117
257
|
end
|
@@ -131,7 +271,7 @@ module Stretchy
|
|
131
271
|
end
|
132
272
|
|
133
273
|
def reflect_on_association(association)
|
134
|
-
|
274
|
+
Stretchy::Relation.new @@_associations[association]
|
135
275
|
end
|
136
276
|
|
137
277
|
def update_all(records, **attributes)
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module Stretchy::Attributes::Type
|
2
|
+
class Array < Stretchy::Attributes::Type::Base # :nodoc:
|
3
|
+
OPTIONS = [:data_type, :fields]
|
4
|
+
def type
|
5
|
+
:array
|
6
|
+
end
|
7
|
+
|
8
|
+
def type_for_database
|
9
|
+
data_type || :text
|
10
|
+
end
|
11
|
+
|
12
|
+
def mappings(name)
|
13
|
+
options = {type: type_for_database}
|
14
|
+
self.class::OPTIONS.each { |option| options[option] = send(option) unless send(option).nil? }
|
15
|
+
options.delete(:fields) if fields == false
|
16
|
+
options[:fields] = {keyword: {type: :keyword, ignore_above: 256}} if type_for_database == :text && fields.nil?
|
17
|
+
{ name => options }.as_json
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
module Stretchy
|
2
|
+
module Attributes
|
3
|
+
module Type
|
4
|
+
class Base < ActiveModel::Type::Value
|
5
|
+
|
6
|
+
OPTIONS = []
|
7
|
+
|
8
|
+
|
9
|
+
def initialize(**args)
|
10
|
+
|
11
|
+
define_option_methods!
|
12
|
+
|
13
|
+
args.each do |k, v|
|
14
|
+
if self.class::OPTIONS.include?(k)
|
15
|
+
instance_variable_set("@#{k}", v)
|
16
|
+
args.delete(k)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
super
|
20
|
+
end
|
21
|
+
|
22
|
+
def mappings(name)
|
23
|
+
options = {type: type_for_database}
|
24
|
+
self.class::OPTIONS.each { |option| options[option] = send(option) unless send(option).nil? }
|
25
|
+
{ name => options }.as_json
|
26
|
+
end
|
27
|
+
|
28
|
+
def type_for_database
|
29
|
+
type
|
30
|
+
end
|
31
|
+
|
32
|
+
private
|
33
|
+
|
34
|
+
def define_option_methods!
|
35
|
+
self.class::OPTIONS.each do |option|
|
36
|
+
define_singleton_method(option.to_sym) { instance_variable_get("@#{option}") }
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,45 @@
|
|
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
|
+
module Stretchy
|
18
|
+
module Attributes
|
19
|
+
module Type
|
20
|
+
class Binary < ActiveModel::Type::Value
|
21
|
+
OPTIONS = [:doc_values, :store]
|
22
|
+
attr_reader *OPTIONS
|
23
|
+
|
24
|
+
def initialize(**args)
|
25
|
+
args.each do |k, v|
|
26
|
+
instance_variable_set("@#{k}", v) if OPTIONS.include?(k)
|
27
|
+
args.delete(k)
|
28
|
+
end
|
29
|
+
super
|
30
|
+
end
|
31
|
+
|
32
|
+
def type
|
33
|
+
:binary
|
34
|
+
end
|
35
|
+
|
36
|
+
|
37
|
+
def mappings(name)
|
38
|
+
options = {type: type}
|
39
|
+
OPTIONS.each { |_| options[_] = self.send(_) }
|
40
|
+
{ name => options }.as_json
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,48 @@
|
|
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
|
+
module Stretchy
|
24
|
+
module Attributes
|
25
|
+
module Type
|
26
|
+
class Boolean < Stretchy::Attributes::Type::Base
|
27
|
+
|
28
|
+
OPTIONS = [:doc_values, :index, :ignore_malformed, :null_value, :on_script_error, :script, :store, :meta]
|
29
|
+
attr_reader *OPTIONS
|
30
|
+
|
31
|
+
def initialize(**args)
|
32
|
+
args.each do |k, v|
|
33
|
+
instance_variable_set("@#{k}", v) if OPTIONS.include?(k)
|
34
|
+
args.delete(k)
|
35
|
+
end
|
36
|
+
super
|
37
|
+
end
|
38
|
+
|
39
|
+
def type
|
40
|
+
:boolean
|
41
|
+
end
|
42
|
+
|
43
|
+
|
44
|
+
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module Stretchy::Attributes::Type
|
2
|
+
# Public: Defines a completion attribute for the model.
|
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.
|
10
|
+
#
|
11
|
+
# Examples
|
12
|
+
#
|
13
|
+
# class MyModel < Stretchy::Record
|
14
|
+
# attribute :name, :completion, analyzer: 'simple', max_input_length: 100
|
15
|
+
# end
|
16
|
+
#
|
17
|
+
# Returns nothing.
|
18
|
+
class Completion < Stretchy::Attributes::Type::Base
|
19
|
+
OPTIONS = [:analyzer, :search_analyzer, :preserve_separators, :preserve_position_increments, :max_input_length]
|
20
|
+
attr_reader *OPTIONS
|
21
|
+
def type
|
22
|
+
:completion
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,38 @@
|
|
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
|
27
|
+
# attribute :status, :constant_keyword, value: 'active'
|
28
|
+
# end
|
29
|
+
#
|
30
|
+
# Returns nothing.
|
31
|
+
class ConstantKeyword < Stretchy::Attributes::Type::Keyword
|
32
|
+
OPTIONS = OPTIONS + [:value]
|
33
|
+
attr_reader *OPTIONS
|
34
|
+
def type
|
35
|
+
:constant_keyword
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,35 @@
|
|
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
|
20
|
+
# attribute :created_at, :datetime, format: 'strict_date_optional_time||epoch_millis', locale: 'en'
|
21
|
+
# end
|
22
|
+
#
|
23
|
+
# Returns nothing.
|
24
|
+
class DateTime < Stretchy::Attributes::Type::Base
|
25
|
+
OPTIONS = [:doc_values, :format, :locale, :ignore_malformed, :index, :null_value, :on_script_error, :script, :store, :meta]
|
26
|
+
attr_reader *OPTIONS
|
27
|
+
def type
|
28
|
+
:datetime
|
29
|
+
end
|
30
|
+
|
31
|
+
def type_for_database
|
32
|
+
:date
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|