activerecord-mysql-search 0.1.3 → 0.2.1
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/Appraisals +8 -0
- data/CHANGELOG.md +26 -0
- data/Gemfile +3 -0
- data/README.md +21 -2
- data/activerecord-mysql-search.gemspec +1 -1
- data/gemfiles/rails_7.0.gemfile +2 -0
- data/gemfiles/rails_7.1.gemfile +2 -0
- data/gemfiles/rails_7.2.gemfile +2 -0
- data/gemfiles/rails_8.0.gemfile +2 -0
- data/gemfiles/rails_8.1.gemfile +21 -0
- data/lib/generators/mysql/search/templates/config/initializers/active_record_ext.rb +0 -4
- data/lib/generators/mysql/search/templates/config/initializers/mysql_search.rb +22 -12
- data/lib/mysql/search/extensions/arel_against.rb +28 -0
- data/lib/mysql/search/queries/full_text_search_query.rb +1 -31
- data/lib/mysql/search/queries/updated_sources_query.rb +25 -12
- data/lib/mysql/search/railtie.rb +4 -0
- data/lib/mysql/search/utils/formatter.rb +8 -2
- data/lib/mysql/search/utils.rb +0 -1
- data/lib/mysql/search.rb +11 -1
- metadata +4 -3
- data/lib/mysql/search/utils/text_normalizer.rb +0 -16
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: bf223abbfa9e47d49642b4f8f52d05eb60256749f6f1ca22b40f9a17db7aad57
|
|
4
|
+
data.tar.gz: 197c6c7c9c15de41a0ed7bd3578a988be3beb366acac94f249e1aa0315747728
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 225730631d733499a6fe947fc8c90ed27f61441939206516f54b5bc951c520130692ffdab155ca072a2986fc9c6d6e2f4bd6e1d10f302be15aca133402dd2d5e
|
|
7
|
+
data.tar.gz: 5475a8bd4aaf08b71c694814abcee48ca74bbf28514e20085ab34cf12ae4f1418e0dee2b57578a97134de1d687d7efd6d79f004bc429b2c2fe7dabc01b6f30de
|
data/Appraisals
CHANGED
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,31 @@
|
|
|
1
1
|
## [Unreleased]
|
|
2
2
|
|
|
3
|
+
## [0.2.1] - 2026-07-15
|
|
4
|
+
|
|
5
|
+
- Adds automatic loading of source classes on Rails startup via new `autoload_sources` configuration option (default: true)
|
|
6
|
+
- Adds `load_source_classes!` method for manually loading source classes
|
|
7
|
+
- Fixes source actualization query composition to preserve base STI scope while appending OR conditions for associated updates
|
|
8
|
+
- Adds regression coverage for STI subclass source relations with associated updates
|
|
9
|
+
- Extracts Arel `AGAINST` integration into a dedicated extension file
|
|
10
|
+
|
|
11
|
+
## [0.2.0] - 2025-09-25
|
|
12
|
+
|
|
13
|
+
- Removes text normalization from default `:text` formatter
|
|
14
|
+
- Search term passed as argument to the scope `.full_text_search` is not pre-normalized anymore
|
|
15
|
+
- Adds `register_format` method to register custom formatters
|
|
16
|
+
|
|
17
|
+
## [0.1.3] - 2025-08-06
|
|
18
|
+
|
|
19
|
+
- Move rake tasks to the proper place + explicit tasks load
|
|
20
|
+
|
|
21
|
+
## [0.1.2] - 2025-08-12
|
|
22
|
+
|
|
23
|
+
- Removes code of conduct
|
|
24
|
+
|
|
25
|
+
## [0.1.1] - 2025-08-05
|
|
26
|
+
|
|
27
|
+
- Fixes load paths to rake tasks
|
|
28
|
+
|
|
3
29
|
## [0.1.0] - 2025-07-22
|
|
4
30
|
|
|
5
31
|
- Initial release
|
data/Gemfile
CHANGED
data/README.md
CHANGED
|
@@ -89,14 +89,23 @@ class ArticleSource < MySQL::Search::Source
|
|
|
89
89
|
end
|
|
90
90
|
```
|
|
91
91
|
|
|
92
|
-
|
|
92
|
+
#### Available Formatters
|
|
93
93
|
|
|
94
94
|
- **`:text`** - Extracts text content from the field
|
|
95
95
|
- **`:date`** - Formats dates using the configured date format (e.g., "12.01.2025", format is configurable)
|
|
96
96
|
- **`:calendar_week`** - Extracts calendar week information (e.g., "week 42", format is configurable)
|
|
97
97
|
- **`Proc`** - Custom extraction logic with access to the attribute value
|
|
98
98
|
- **Nested Associations** - Supports nested associations
|
|
99
|
-
|
|
99
|
+
|
|
100
|
+
#### Registering new formatters
|
|
101
|
+
|
|
102
|
+
You can register new formatters using the `register_format` method:
|
|
103
|
+
|
|
104
|
+
```ruby
|
|
105
|
+
MySQL::Search.configure do |config|
|
|
106
|
+
config.register_format(:upcase) { |value| value.to_s.upcase }
|
|
107
|
+
end
|
|
108
|
+
```
|
|
100
109
|
|
|
101
110
|
### Step 6: Index Existing Data
|
|
102
111
|
|
|
@@ -204,6 +213,13 @@ MySQL::Search.configure do |config|
|
|
|
204
213
|
# Path to search source classes (default: 'app/search_sources')
|
|
205
214
|
config.sources_path = 'app/search_sources'
|
|
206
215
|
|
|
216
|
+
# Automatically load source classes on Rails startup (default: true)
|
|
217
|
+
# Loading of source classes assigns callbacks to the target models
|
|
218
|
+
# to keep the search index updated. In case of loading issue you can load
|
|
219
|
+
# the source classes manually with `MySQL::Search.load_source_classes!`
|
|
220
|
+
# and disable automatic initialization.
|
|
221
|
+
config.autoload_sources = true
|
|
222
|
+
|
|
207
223
|
# Automatically update search index when models change (default: true)
|
|
208
224
|
config.automatic_update = true
|
|
209
225
|
|
|
@@ -215,6 +231,9 @@ MySQL::Search.configure do |config|
|
|
|
215
231
|
|
|
216
232
|
# Calendar week format (default: 'week %V')
|
|
217
233
|
config.calendar_week_format = 'week %V'
|
|
234
|
+
|
|
235
|
+
# Register custom formatters
|
|
236
|
+
config.register_format(:upcase) { |value| value.to_s.upcase }
|
|
218
237
|
end
|
|
219
238
|
```
|
|
220
239
|
|
data/gemfiles/rails_7.0.gemfile
CHANGED
data/gemfiles/rails_7.1.gemfile
CHANGED
data/gemfiles/rails_7.2.gemfile
CHANGED
data/gemfiles/rails_8.0.gemfile
CHANGED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# This file was generated by Appraisal
|
|
4
|
+
|
|
5
|
+
source 'https://rubygems.org'
|
|
6
|
+
|
|
7
|
+
gem 'appraisal'
|
|
8
|
+
gem 'database_cleaner-active_record'
|
|
9
|
+
gem 'mysql2'
|
|
10
|
+
gem 'pry'
|
|
11
|
+
gem 'rails', '~> 8.1.0'
|
|
12
|
+
gem 'rake', '~> 13.0'
|
|
13
|
+
gem 'rspec'
|
|
14
|
+
gem 'rubocop'
|
|
15
|
+
gem 'rubocop-performance'
|
|
16
|
+
gem 'rubocop-rails'
|
|
17
|
+
gem 'rubocop-rake'
|
|
18
|
+
gem 'rubocop-rspec'
|
|
19
|
+
gem 'ruby-lsp'
|
|
20
|
+
|
|
21
|
+
gemspec path: '../'
|
|
@@ -15,11 +15,7 @@ module ActiveRecord
|
|
|
15
15
|
add_column table_name, :updated_at, 'DATETIME ON UPDATE CURRENT_TIMESTAMP', **options
|
|
16
16
|
end
|
|
17
17
|
end
|
|
18
|
-
end
|
|
19
|
-
end
|
|
20
18
|
|
|
21
|
-
module ActiveRecord
|
|
22
|
-
module ConnectionAdapters
|
|
23
19
|
# Overrides the `timestamps` method in `TableDefinition` to use MySQL's `DATETIME ON UPDATE CURRENT_TIMESTAMP`
|
|
24
20
|
# for the `updated_at` column.
|
|
25
21
|
# This allows the `updated_at` column to automatically update its value whenever the row is updated.
|
|
@@ -2,21 +2,31 @@
|
|
|
2
2
|
|
|
3
3
|
# Configure MySQL::Search settings
|
|
4
4
|
MySQL::Search.configure do |config|
|
|
5
|
-
#
|
|
6
|
-
config.search_index_class_name = 'SearchIndex'
|
|
5
|
+
# Model class name for search indices (default: 'SearchIndex')
|
|
6
|
+
# config.search_index_class_name = 'SearchIndex'
|
|
7
7
|
|
|
8
|
-
#
|
|
9
|
-
config.sources_path = 'app/search_sources'
|
|
8
|
+
# Path to search source classes (default: 'app/search_sources')
|
|
9
|
+
# config.sources_path = 'app/search_sources'
|
|
10
10
|
|
|
11
|
-
#
|
|
12
|
-
|
|
11
|
+
# Automatically load source classes on Rails startup (default: true)
|
|
12
|
+
# Loading of source classes assigns callbacks to the target models
|
|
13
|
+
# to keep the search index updated. In case of loading issue you can load
|
|
14
|
+
# the source classes manually with `MySQL::Search.load_source_classes!`
|
|
15
|
+
# and disable automatic initialization.
|
|
16
|
+
# config.autoload_sources = true
|
|
13
17
|
|
|
14
|
-
#
|
|
15
|
-
config.
|
|
18
|
+
# Automatically update search index when models change (default: true)
|
|
19
|
+
# config.automatic_update = true
|
|
16
20
|
|
|
17
|
-
#
|
|
18
|
-
config.
|
|
21
|
+
# Process index updates asynchronously (default: false)
|
|
22
|
+
# config.update_asyncronously = false
|
|
19
23
|
|
|
20
|
-
#
|
|
21
|
-
config.date_format = '%d.%m.%Y'
|
|
24
|
+
# Date format for date fields (default: '%d.%m.%Y')
|
|
25
|
+
# config.date_format = '%d.%m.%Y'
|
|
26
|
+
|
|
27
|
+
# Calendar week format (default: 'week %V')
|
|
28
|
+
# config.calendar_week_format = 'week %V'
|
|
29
|
+
|
|
30
|
+
# Register custom formatters
|
|
31
|
+
# config.register_format(:upcase) { |value| value.to_s.upcase }
|
|
22
32
|
end
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'arel'
|
|
4
|
+
|
|
5
|
+
module Arel
|
|
6
|
+
module Visitors
|
|
7
|
+
# Custom visitor for MySQL to handle the `AGAINST` clause in full-text search.
|
|
8
|
+
class MySQL
|
|
9
|
+
def visit_Arel_Nodes_Against(node, collector) # rubocop:disable Naming/MethodName
|
|
10
|
+
visit(node.left, collector) << ' AGAINST ('
|
|
11
|
+
visit(node.right, collector) << ')'
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
module Nodes
|
|
17
|
+
# Represents the `AGAINST` clause used in MySQL full-text search queries.
|
|
18
|
+
class Against < Arel::Nodes::Matches
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
# Adds a method to the `Arel::Nodes::Node` class to allow for full-text search queries.
|
|
23
|
+
module Predications
|
|
24
|
+
def against(other)
|
|
25
|
+
Arel::Nodes::Against.new(self, quoted_node(other))
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
@@ -1,35 +1,6 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
module Arel
|
|
6
|
-
module Visitors
|
|
7
|
-
# Custom visitor for MySQL to handle the `AGAINST` clause in full-text search.
|
|
8
|
-
class MySQL
|
|
9
|
-
def visit_Arel_Nodes_Against(node, collector) # rubocop:disable Naming/MethodName
|
|
10
|
-
visit(node.left, collector) << ' AGAINST ('
|
|
11
|
-
visit(node.right, collector) << ')'
|
|
12
|
-
end
|
|
13
|
-
end
|
|
14
|
-
end
|
|
15
|
-
end
|
|
16
|
-
|
|
17
|
-
module Arel
|
|
18
|
-
module Nodes
|
|
19
|
-
# Represents the `AGAINST` clause used in MySQL full-text search queries.
|
|
20
|
-
class Against < Arel::Nodes::Matches
|
|
21
|
-
end
|
|
22
|
-
end
|
|
23
|
-
end
|
|
24
|
-
|
|
25
|
-
module Arel
|
|
26
|
-
# Adds a method to the `Arel::Nodes::Node` class to allow for full-text search queries.
|
|
27
|
-
module Predications
|
|
28
|
-
def against(other)
|
|
29
|
-
Arel::Nodes::Against.new(self, quoted_node(other))
|
|
30
|
-
end
|
|
31
|
-
end
|
|
32
|
-
end
|
|
3
|
+
require_relative '../extensions/arel_against'
|
|
33
4
|
|
|
34
5
|
module MySQL
|
|
35
6
|
module Search
|
|
@@ -59,7 +30,6 @@ module MySQL
|
|
|
59
30
|
private
|
|
60
31
|
|
|
61
32
|
def search_expression(search_term, search_column)
|
|
62
|
-
search_term = ::MySQL::Search::Utils::TextNormalizer.normalize(search_term)
|
|
63
33
|
search_indices = ::MySQL::Search.search_index_class_name.constantize.arel_table
|
|
64
34
|
search_columns = Array.wrap(search_column).map { |col| search_indices[col] }
|
|
65
35
|
|
|
@@ -14,24 +14,37 @@ module MySQL
|
|
|
14
14
|
|
|
15
15
|
def call(time_ago)
|
|
16
16
|
joins_args = source_class_name.constantize.joins_args
|
|
17
|
-
|
|
17
|
+
base_relation = source_relation.left_joins(joins_args)
|
|
18
|
+
relation = base_relation.where(source_relation.klass.arel_table[:updated_at].gteq(time_ago))
|
|
18
19
|
|
|
19
|
-
append_conditions(relation, source_relation, joins_args, time_ago)
|
|
20
|
+
append_conditions(relation, source_relation.klass, joins_args, time_ago, base_relation)
|
|
20
21
|
end
|
|
21
22
|
|
|
22
23
|
private
|
|
23
24
|
|
|
24
|
-
def append_conditions(relation, root_class, config, time_ago)
|
|
25
|
+
def append_conditions(relation, root_class, config, time_ago, base_relation)
|
|
25
26
|
case config
|
|
26
|
-
when Array
|
|
27
|
+
when Array
|
|
28
|
+
append_array_conditions(relation, root_class, config, time_ago, base_relation)
|
|
27
29
|
when Hash
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
config.reduce(relation) do |rel, (root_association, nested_config)|
|
|
31
|
-
append_conditions(rel, association_class(root_class, root_association), nested_config, time_ago)
|
|
32
|
-
end
|
|
30
|
+
append_hash_conditions(relation, root_class, config, time_ago, base_relation)
|
|
33
31
|
else
|
|
34
|
-
append_or(relation, association_class(root_class, config), time_ago)
|
|
32
|
+
append_or(relation, association_class(root_class, config), time_ago, base_relation)
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def append_array_conditions(relation, root_class, config, time_ago, base_relation)
|
|
37
|
+
config.reduce(relation) do |rel, item|
|
|
38
|
+
append_conditions(rel, root_class, item, time_ago, base_relation)
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def append_hash_conditions(relation, root_class, config, time_ago, base_relation)
|
|
43
|
+
relation = append_conditions(relation, root_class, config.keys, time_ago, base_relation)
|
|
44
|
+
|
|
45
|
+
config.reduce(relation) do |rel, (root_association, nested_config)|
|
|
46
|
+
append_conditions(rel, association_class(root_class, root_association), nested_config, time_ago,
|
|
47
|
+
base_relation)
|
|
35
48
|
end
|
|
36
49
|
end
|
|
37
50
|
|
|
@@ -39,10 +52,10 @@ module MySQL
|
|
|
39
52
|
root_class.reflect_on_association(association).klass
|
|
40
53
|
end
|
|
41
54
|
|
|
42
|
-
def append_or(relation, model, time_ago)
|
|
55
|
+
def append_or(relation, model, time_ago, base_relation)
|
|
43
56
|
return relation unless model.column_names.include?('updated_at')
|
|
44
57
|
|
|
45
|
-
relation.or(
|
|
58
|
+
relation.or(base_relation.where(model.arel_table[:updated_at].gteq(time_ago)))
|
|
46
59
|
end
|
|
47
60
|
end
|
|
48
61
|
end
|
data/lib/mysql/search/railtie.rb
CHANGED
|
@@ -6,6 +6,10 @@ module MySQL
|
|
|
6
6
|
class Railtie < Rails::Railtie
|
|
7
7
|
railtie_name :mysql_search
|
|
8
8
|
|
|
9
|
+
config.to_prepare do
|
|
10
|
+
MySQL::Search.load_source_classes! if MySQL::Search.autoload_sources
|
|
11
|
+
end
|
|
12
|
+
|
|
9
13
|
rake_tasks do
|
|
10
14
|
load 'tasks/mysql/search/actualize.rake'
|
|
11
15
|
load 'tasks/mysql/search/reindex.rake'
|
|
@@ -7,6 +7,12 @@ module MySQL
|
|
|
7
7
|
class Formatter
|
|
8
8
|
attr_reader :value, :formatter
|
|
9
9
|
|
|
10
|
+
def self.register(name, &)
|
|
11
|
+
define_method(name) do
|
|
12
|
+
yield(value)
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
|
|
10
16
|
def initialize(value, formatter)
|
|
11
17
|
if formatter.instance_of?(Proc)
|
|
12
18
|
@value = formatter.call(value)
|
|
@@ -20,13 +26,13 @@ module MySQL
|
|
|
20
26
|
end
|
|
21
27
|
|
|
22
28
|
def format
|
|
23
|
-
formatter ? send(formatter) : value
|
|
29
|
+
(formatter ? send(formatter) : value).to_s.strip
|
|
24
30
|
end
|
|
25
31
|
|
|
26
32
|
private
|
|
27
33
|
|
|
28
34
|
def text
|
|
29
|
-
|
|
35
|
+
value
|
|
30
36
|
end
|
|
31
37
|
|
|
32
38
|
def calendar_week
|
data/lib/mysql/search/utils.rb
CHANGED
data/lib/mysql/search.rb
CHANGED
|
@@ -18,6 +18,7 @@ module MySQL
|
|
|
18
18
|
# Runtime configuration
|
|
19
19
|
mattr_accessor :automatic_update, default: true
|
|
20
20
|
mattr_accessor :update_asyncronously, default: false
|
|
21
|
+
mattr_accessor :autoload_sources, default: true
|
|
21
22
|
|
|
22
23
|
# Search Index & Sources
|
|
23
24
|
mattr_accessor :search_index_class_name, default: 'SearchIndex'
|
|
@@ -27,16 +28,25 @@ module MySQL
|
|
|
27
28
|
mattr_accessor :calendar_week_format, default: 'week %V'
|
|
28
29
|
mattr_accessor :date_format, default: '%d.%m.%Y'
|
|
29
30
|
|
|
31
|
+
def register_format(name, &)
|
|
32
|
+
Utils::Formatter.register(name, &)
|
|
33
|
+
end
|
|
34
|
+
|
|
30
35
|
def search_index_class
|
|
31
36
|
@search_index_class ||= search_index_class_name.constantize
|
|
32
37
|
end
|
|
33
38
|
|
|
34
39
|
def source_classes
|
|
35
|
-
|
|
40
|
+
Dir.glob("#{sources_path}/**/*.rb").filter_map do |file|
|
|
36
41
|
file.sub("#{sources_path}/", '').sub('.rb', '').camelize.safe_constantize
|
|
37
42
|
end
|
|
38
43
|
end
|
|
39
44
|
|
|
45
|
+
# Keep a separate API entrypoint used by the Railtie initialization hook.
|
|
46
|
+
def load_source_classes!
|
|
47
|
+
source_classes
|
|
48
|
+
end
|
|
49
|
+
|
|
40
50
|
def configure
|
|
41
51
|
yield self
|
|
42
52
|
end
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: activerecord-mysql-search
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.1
|
|
4
|
+
version: 0.2.1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Daydream Unicorn GmbH & Co. KG
|
|
@@ -32,6 +32,7 @@ files:
|
|
|
32
32
|
- gemfiles/rails_7.1.gemfile
|
|
33
33
|
- gemfiles/rails_7.2.gemfile
|
|
34
34
|
- gemfiles/rails_8.0.gemfile
|
|
35
|
+
- gemfiles/rails_8.1.gemfile
|
|
35
36
|
- lib/activerecord-mysql-search.rb
|
|
36
37
|
- lib/generators/mysql/search/create_trigger_generator.rb
|
|
37
38
|
- lib/generators/mysql/search/install_generator.rb
|
|
@@ -42,6 +43,7 @@ files:
|
|
|
42
43
|
- lib/generators/mysql/search/templates/db/migrate/enable_auto_update_of_updated_at.rb
|
|
43
44
|
- lib/mysql/search.rb
|
|
44
45
|
- lib/mysql/search/callbacks.rb
|
|
46
|
+
- lib/mysql/search/extensions/arel_against.rb
|
|
45
47
|
- lib/mysql/search/grabber.rb
|
|
46
48
|
- lib/mysql/search/jobs.rb
|
|
47
49
|
- lib/mysql/search/jobs/scheduled_updater_job.rb
|
|
@@ -55,7 +57,6 @@ files:
|
|
|
55
57
|
- lib/mysql/search/utils.rb
|
|
56
58
|
- lib/mysql/search/utils/duration_parser.rb
|
|
57
59
|
- lib/mysql/search/utils/formatter.rb
|
|
58
|
-
- lib/mysql/search/utils/text_normalizer.rb
|
|
59
60
|
- lib/tasks/mysql/search/actualize.rake
|
|
60
61
|
- lib/tasks/mysql/search/reindex.rake
|
|
61
62
|
homepage: https://github.com/ddunicorn/activerecord-mysql-search.rubygem/blob/main/README.md
|
|
@@ -80,7 +81,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
80
81
|
- !ruby/object:Gem::Version
|
|
81
82
|
version: '0'
|
|
82
83
|
requirements: []
|
|
83
|
-
rubygems_version:
|
|
84
|
+
rubygems_version: 4.0.16
|
|
84
85
|
specification_version: 4
|
|
85
86
|
summary: Full Text Search for ActiveRecord with MySQL
|
|
86
87
|
test_files: []
|
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module MySQL
|
|
4
|
-
module Search
|
|
5
|
-
module Utils
|
|
6
|
-
# Normalizes text by removing non-alphanumeric characters, except for spaces and hyphens.
|
|
7
|
-
class TextNormalizer
|
|
8
|
-
REGEXP = /[[:alnum:][:blank:]+\-*~<>()"@\.]/
|
|
9
|
-
|
|
10
|
-
def self.normalize(value)
|
|
11
|
-
value.to_s.scan(REGEXP).join.squish
|
|
12
|
-
end
|
|
13
|
-
end
|
|
14
|
-
end
|
|
15
|
-
end
|
|
16
|
-
end
|