activerecord-mysql-search 0.2.0 → 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 +18 -10
- data/Gemfile +1 -0
- data/README.md +10 -0
- 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 -30
- 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 +2 -2
- data/lib/mysql/search.rb +7 -1
- metadata +4 -2
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,23 +1,31 @@
|
|
|
1
1
|
## [Unreleased]
|
|
2
2
|
|
|
3
|
-
## [0.1
|
|
3
|
+
## [0.2.1] - 2026-07-15
|
|
4
4
|
|
|
5
|
-
-
|
|
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
|
|
6
10
|
|
|
7
|
-
## [0.
|
|
11
|
+
## [0.2.0] - 2025-09-25
|
|
8
12
|
|
|
9
|
-
-
|
|
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
|
|
10
20
|
|
|
11
21
|
## [0.1.2] - 2025-08-12
|
|
12
22
|
|
|
13
23
|
- Removes code of conduct
|
|
14
24
|
|
|
15
|
-
## [0.1.
|
|
25
|
+
## [0.1.1] - 2025-08-05
|
|
16
26
|
|
|
17
|
-
-
|
|
27
|
+
- Fixes load paths to rake tasks
|
|
18
28
|
|
|
19
|
-
## [0.
|
|
29
|
+
## [0.1.0] - 2025-07-22
|
|
20
30
|
|
|
21
|
-
-
|
|
22
|
-
- Search term passed as argument to the scope `.full_text_search` is not pre-normalized anymore
|
|
23
|
-
- Adds `register_format` method to register custom formatters
|
|
31
|
+
- Initial release
|
data/Gemfile
CHANGED
data/README.md
CHANGED
|
@@ -213,6 +213,13 @@ MySQL::Search.configure do |config|
|
|
|
213
213
|
# Path to search source classes (default: 'app/search_sources')
|
|
214
214
|
config.sources_path = 'app/search_sources'
|
|
215
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
|
+
|
|
216
223
|
# Automatically update search index when models change (default: true)
|
|
217
224
|
config.automatic_update = true
|
|
218
225
|
|
|
@@ -224,6 +231,9 @@ MySQL::Search.configure do |config|
|
|
|
224
231
|
|
|
225
232
|
# Calendar week format (default: 'week %V')
|
|
226
233
|
config.calendar_week_format = 'week %V'
|
|
234
|
+
|
|
235
|
+
# Register custom formatters
|
|
236
|
+
config.register_format(:upcase) { |value| value.to_s.upcase }
|
|
227
237
|
end
|
|
228
238
|
```
|
|
229
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
|
|
@@ -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'
|
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'
|
|
@@ -36,11 +37,16 @@ module MySQL
|
|
|
36
37
|
end
|
|
37
38
|
|
|
38
39
|
def source_classes
|
|
39
|
-
|
|
40
|
+
Dir.glob("#{sources_path}/**/*.rb").filter_map do |file|
|
|
40
41
|
file.sub("#{sources_path}/", '').sub('.rb', '').camelize.safe_constantize
|
|
41
42
|
end
|
|
42
43
|
end
|
|
43
44
|
|
|
45
|
+
# Keep a separate API entrypoint used by the Railtie initialization hook.
|
|
46
|
+
def load_source_classes!
|
|
47
|
+
source_classes
|
|
48
|
+
end
|
|
49
|
+
|
|
44
50
|
def configure
|
|
45
51
|
yield self
|
|
46
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.2.
|
|
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
|
|
@@ -79,7 +81,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
79
81
|
- !ruby/object:Gem::Version
|
|
80
82
|
version: '0'
|
|
81
83
|
requirements: []
|
|
82
|
-
rubygems_version:
|
|
84
|
+
rubygems_version: 4.0.16
|
|
83
85
|
specification_version: 4
|
|
84
86
|
summary: Full Text Search for ActiveRecord with MySQL
|
|
85
87
|
test_files: []
|