textacular 5.1.0 → 5.4.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: 3bd98a4fec2d72714909b49059a7f8b50298d734
4
- data.tar.gz: 9b3fc608d5192296392da8323048c65440146cbd
2
+ SHA256:
3
+ metadata.gz: d2f44e73cb226a66719aca4bdfb54732d7fc4bb2fe7af548b49aad5c81d97cfe
4
+ data.tar.gz: 7d43ad7e191607aef9c521d5218afd98df5bc5cfa98d84cb8189db9026cc44de
5
5
  SHA512:
6
- metadata.gz: 7e1cd1fdce05d21df1e9803ca0bd1db66fc18ba371a4980175824ad5c46df5eb1e4cfd1aed857e5cea384887734f7f563ce498cc860c8c8827873f669a9ca584
7
- data.tar.gz: 6237664f59c95a81b48283b3f761289a8c63199c908fba347e3931be9f93c59f5be897c971434266a7d1a203dfc3aa11af217736ca2e3313cea180c73c0d16c3
6
+ metadata.gz: 0f8ba724deeb6e82160fea018b012592fd0a407231b81467ba6c52e6f921e9792aeb6653a8adbcf5c6618a5559e434ffd717e13174e21dd04eca8c2fece472fb
7
+ data.tar.gz: b84dfe00f5fd93a8735e3ce37847056fb82fa1ec583b59fa03cfbb411993fc33430e05ffe57986cded34c165261615edc3c524203da2a326d20fd0e3c9dcbc0a
data/CHANGELOG.md CHANGED
@@ -2,6 +2,18 @@
2
2
 
3
3
  ## Unreleased
4
4
 
5
+ ## 5.4.0
6
+
7
+ * ActiveRecord 6.1 compatibility
8
+
9
+ ## 5.3.0
10
+
11
+ * Add `#web_search` method to use Postgres' 11+ `websearch_to_tsquery`
12
+
13
+ ## 5.2.0
14
+
15
+ * Active Record 6.0 compatibility
16
+
5
17
  ## 5.1.0
6
18
 
7
19
  * ActiveRecord 5.2 compatibility by wrapping string queries with `Arel.sql()`
data/Gemfile CHANGED
@@ -2,4 +2,8 @@ source 'https://rubygems.org'
2
2
 
3
3
  gemspec
4
4
 
5
- gem 'activerecord', '~> 5.2.x'
5
+ git 'git://github.com/rails/rails.git', branch: 'master' do
6
+ gem 'activerecord'
7
+ end
8
+
9
+ gem "pg", "~> 1.1"
data/README.md CHANGED
@@ -33,7 +33,7 @@ In the project's Gemfile add
33
33
  gem 'textacular', '~> 4.0'
34
34
  ```
35
35
 
36
- #### Rails 5.0 and Rails 5.1!
36
+ #### Rails > 5.0
37
37
 
38
38
  In the project's Gemfile add
39
39
 
@@ -75,6 +75,20 @@ Game.advanced_search(title: 'Street|Fantasy')
75
75
  Game.advanced_search(system: '!PS2')
76
76
  ```
77
77
 
78
+ The `#web_search` method lets you use Postgres' 11+ `websearch_to_tsquery` function
79
+ supporting websearch like syntax:
80
+
81
+ - unquoted text: text not inside quote marks will be converted to terms separated by & operators, as if processed by plainto_tsquery.
82
+ - "quoted text": text inside quote marks will be converted to terms separated by <-> operators, as if processed by phraseto_tsquery.
83
+ - OR: logical or will be converted to the | operator.
84
+ - -: the logical not operator, converted to the the ! operator.
85
+
86
+ ```ruby
87
+ Game.web_search(title: '"Street Fantasy"')
88
+ Game.web_search(title: 'Street OR Fantasy')
89
+ Game.web_search(system: '-PS2')
90
+ ```
91
+
78
92
  Finally, the `#fuzzy_search` method lets you use Postgres's trigram search
79
93
  functionality.
80
94
 
data/Rakefile CHANGED
@@ -43,12 +43,18 @@ namespace :db do
43
43
 
44
44
  desc 'Run the test database migrations'
45
45
  task :up => :'db:connect' do
46
- migrations = if ActiveRecord.version.version >= '5.2'
47
- ActiveRecord::Migration.new.migration_context.migrations
46
+ if ActiveRecord.version >= Gem::Version.new('6.0.0')
47
+ context = ActiveRecord::Migration.new.migration_context
48
+ migrations = context.migrations
49
+ schema_migration = context.schema_migration
50
+ elsif ActiveRecord.version >= Gem::Version.new('5.2')
51
+ migrations = ActiveRecord::Migration.new.migration_context.migrations
52
+ schema_migration = nil
48
53
  else
49
- ActiveRecord::Migrator.migrations('db/migrate')
54
+ migrations = ActiveRecord::Migrator.migrations('db/migrate')
55
+ schema_migration = nil
50
56
  end
51
- ActiveRecord::Migrator.new(:up, migrations, nil).migrate
57
+ ActiveRecord::Migrator.new(:up, migrations, schema_migration).migrate
52
58
  end
53
59
 
54
60
  desc 'Reverse the test database migrations'
data/lib/textacular.rb CHANGED
@@ -12,29 +12,36 @@ module Textacular
12
12
  'english'
13
13
  end
14
14
 
15
- def search(query = "", exclusive = true)
16
- basic_search(query, exclusive)
15
+ def search(query = "", exclusive = true, rank_alias = nil)
16
+ basic_search(query, exclusive, rank_alias)
17
17
  end
18
18
 
19
- def basic_search(query = "", exclusive = true)
19
+ def basic_search(query = "", exclusive = true, rank_alias = nil)
20
20
  exclusive, query = munge_exclusive_and_query(exclusive, query)
21
21
  parsed_query_hash = parse_query_hash(query)
22
22
  similarities, conditions = basic_similarities_and_conditions(parsed_query_hash)
23
- assemble_query(similarities, conditions, exclusive)
23
+ assemble_query(similarities, conditions, exclusive, rank_alias)
24
24
  end
25
25
 
26
- def advanced_search(query = "", exclusive = true)
26
+ def advanced_search(query = "", exclusive = true, rank_alias = nil)
27
27
  exclusive, query = munge_exclusive_and_query(exclusive, query)
28
28
  parsed_query_hash = parse_query_hash(query)
29
29
  similarities, conditions = advanced_similarities_and_conditions(parsed_query_hash)
30
- assemble_query(similarities, conditions, exclusive)
30
+ assemble_query(similarities, conditions, exclusive, rank_alias)
31
31
  end
32
32
 
33
- def fuzzy_search(query = '', exclusive = true)
33
+ def fuzzy_search(query = '', exclusive = true, rank_alias = nil)
34
34
  exclusive, query = munge_exclusive_and_query(exclusive, query)
35
35
  parsed_query_hash = parse_query_hash(query)
36
36
  similarities, conditions = fuzzy_similarities_and_conditions(parsed_query_hash)
37
- assemble_query(similarities, conditions, exclusive)
37
+ assemble_query(similarities, conditions, exclusive, rank_alias)
38
+ end
39
+
40
+ def web_search(query = '', exclusive = true, rank_alias = nil)
41
+ exclusive, query = munge_exclusive_and_query(exclusive, query)
42
+ parsed_query_hash = parse_query_hash(query)
43
+ similarities, conditions = web_similarities_and_conditions(parsed_query_hash)
44
+ assemble_query(similarities, conditions, exclusive, rank_alias)
38
45
  end
39
46
 
40
47
  private
@@ -113,15 +120,34 @@ module Textacular
113
120
  end
114
121
 
115
122
  def fuzzy_similarity_string(table_name, column, search_term)
116
- "COALESCE(similarity(#{table_name}.#{column}, #{search_term}), 0)"
123
+ "COALESCE(similarity(#{table_name}.#{column}::text, #{search_term}), 0)"
117
124
  end
118
125
 
119
126
  def fuzzy_condition_string(table_name, column, search_term)
120
- "(#{table_name}.#{column} % #{search_term})"
127
+ "(#{table_name}.#{column}::text % #{search_term})"
128
+ end
129
+
130
+
131
+ def web_similarities_and_conditions(parsed_query_hash)
132
+ parsed_query_hash.inject([[], []]) do |(similarities, conditions), query_args|
133
+ similarities << web_similarity_string(*query_args)
134
+ conditions << web_condition_string(*query_args)
135
+
136
+ [similarities, conditions]
137
+ end
138
+ end
139
+
140
+ def web_similarity_string(table_name, column, search_term)
141
+ "COALESCE(ts_rank(to_tsvector(#{quoted_language}, #{table_name}.#{column}::text), websearch_to_tsquery(#{quoted_language}, #{search_term}::text)), 0)"
142
+ end
143
+
144
+ def web_condition_string(table_name, column, search_term)
145
+ "to_tsvector(#{quoted_language}, #{table_name}.#{column}::text) @@ websearch_to_tsquery(#{quoted_language}, #{search_term}::text)"
121
146
  end
122
147
 
123
- def assemble_query(similarities, conditions, exclusive)
124
- rank = connection.quote_column_name('rank' + rand(100000000000000000).to_s)
148
+ def assemble_query(similarities, conditions, exclusive, rank_alias)
149
+ rank_alias ||= 'rank' + rand(100000000000000000).to_s
150
+ rank = connection.quote_column_name(rank_alias)
125
151
 
126
152
  select(Arel.sql("#{quoted_table_name + '.*,' if select_values.empty?} #{similarities.join(" + ")} AS #{rank}")).
127
153
  where(conditions.join(exclusive ? " AND " : " OR ")).
@@ -1,5 +1,5 @@
1
1
  module Textacular
2
- VERSION = '5.1.0'
2
+ VERSION = '5.4.0'
3
3
 
4
4
  def self.version
5
5
  VERSION
@@ -3,6 +3,6 @@ timeout: 5000
3
3
  host: localhost
4
4
  adapter: postgresql
5
5
  username: postgres
6
- password:
6
+ password: password
7
7
  database: textacular_test
8
- min_messages: ERROR
8
+ min_messages: ERROR
@@ -113,6 +113,14 @@ RSpec.describe "Searchable" do
113
113
  end
114
114
  end
115
115
 
116
+ describe "web search" do # Uses websearch_to_tsquery
117
+ ["hello \\", "tebow!" , "food &"].each do |search_term|
118
+ it "works with interesting term \"#{search_term}\"" do
119
+ expect(WebComicWithSearchableName.web_search(search_term)).to be_empty
120
+ end
121
+ end
122
+ end
123
+
116
124
  it "does fuzzy searching" do
117
125
  expect(
118
126
  WebComicWithSearchableName.fuzzy_search('Questio')
@@ -182,6 +190,14 @@ RSpec.describe "Searchable" do
182
190
  expect(
183
191
  WebComicWithSearchableNameAndAuthor.advanced_search("Tycho")
184
192
  ).to eq([penny_arcade])
193
+
194
+ expect(
195
+ WebComicWithSearchableNameAndAuthor.web_search("Penny")
196
+ ).to eq([penny_arcade])
197
+
198
+ expect(
199
+ WebComicWithSearchableNameAndAuthor.web_search("Tycho")
200
+ ).to eq([penny_arcade])
185
201
  end
186
202
 
187
203
  it "allows includes" do
@@ -190,5 +206,39 @@ RSpec.describe "Searchable" do
190
206
  ).to eq([penny_arcade])
191
207
  end
192
208
  end
209
+
210
+ context 'custom rank' do
211
+ let!(:questionable_content) do
212
+ WebComicWithSearchableName.create(
213
+ name: 'Questionable Content',
214
+ author: nil,
215
+ )
216
+ end
217
+
218
+ it "is selected for search" do
219
+ search_result = WebComicWithSearchableNameAndAuthor.search('Questionable Content', true, 'my_rank')
220
+ expect(search_result.first.attributes['my_rank']).to be_truthy
221
+ end
222
+
223
+ it "is selected for basic_search" do
224
+ search_result = WebComicWithSearchableNameAndAuthor.basic_search('Questionable Content', true, 'my_rank')
225
+ expect(search_result.first.attributes['my_rank']).to be_truthy
226
+ end
227
+
228
+ it "is selected for advanced_search" do
229
+ search_result = WebComicWithSearchableNameAndAuthor.advanced_search('Questionable Content', true, 'my_rank')
230
+ expect(search_result.first.attributes['my_rank']).to be_truthy
231
+ end
232
+
233
+ it "is selected for fuzzy_search" do
234
+ search_result = WebComicWithSearchableNameAndAuthor.fuzzy_search('Questionable Content', true, 'my_rank')
235
+ expect(search_result.first.attributes['my_rank']).to be_truthy
236
+ end
237
+
238
+ it "is selected for web_search" do
239
+ search_result = WebComicWithSearchableNameAndAuthor.web_search('Questionable Content', true, 'my_rank')
240
+ expect(search_result.first.attributes['my_rank']).to be_truthy
241
+ end
242
+ end
193
243
  end
194
244
  end
@@ -181,6 +181,13 @@ RSpec.describe Textacular do
181
181
  expect(GameExtendedWithTextacular).to respond_to(:search)
182
182
  end
183
183
 
184
+ describe "#fuzzy_search" do
185
+ it 'searches non-text columns' do
186
+ expect(GameExtendedWithTextacular.fuzzy_search(id: mario.id)
187
+ ).to eq([mario])
188
+ end
189
+ end
190
+
184
191
  describe "#advanced_search" do
185
192
  context "with a String argument" do
186
193
  it "searches across all :string columns (if not indexes have been specified)" do
metadata CHANGED
@@ -1,32 +1,32 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: textacular
3
3
  version: !ruby/object:Gem::Version
4
- version: 5.1.0
4
+ version: 5.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ben Hamill
8
8
  - ecin
9
9
  - Aaron Patterson
10
10
  - Greg Molnar
11
- autorequire:
11
+ autorequire:
12
12
  bindir: bin
13
13
  cert_chain: []
14
- date: 2018-05-12 00:00:00.000000000 Z
14
+ date: 2020-12-31 00:00:00.000000000 Z
15
15
  dependencies:
16
16
  - !ruby/object:Gem::Dependency
17
17
  name: pg
18
18
  requirement: !ruby/object:Gem::Requirement
19
19
  requirements:
20
- - - "~>"
20
+ - - ">="
21
21
  - !ruby/object:Gem::Version
22
- version: 1.0.0
22
+ version: '0'
23
23
  type: :development
24
24
  prerelease: false
25
25
  version_requirements: !ruby/object:Gem::Requirement
26
26
  requirements:
27
- - - "~>"
27
+ - - ">="
28
28
  - !ruby/object:Gem::Version
29
- version: 1.0.0
29
+ version: '0'
30
30
  - !ruby/object:Gem::Dependency
31
31
  name: rspec
32
32
  requirement: !ruby/object:Gem::Requirement
@@ -97,6 +97,20 @@ dependencies:
97
97
  - - ">="
98
98
  - !ruby/object:Gem::Version
99
99
  version: '0'
100
+ - !ruby/object:Gem::Dependency
101
+ name: byebug
102
+ requirement: !ruby/object:Gem::Requirement
103
+ requirements:
104
+ - - ">="
105
+ - !ruby/object:Gem::Version
106
+ version: '0'
107
+ type: :development
108
+ prerelease: false
109
+ version_requirements: !ruby/object:Gem::Requirement
110
+ requirements:
111
+ - - ">="
112
+ - !ruby/object:Gem::Version
113
+ version: '0'
100
114
  - !ruby/object:Gem::Dependency
101
115
  name: activerecord
102
116
  requirement: !ruby/object:Gem::Requirement
@@ -106,7 +120,7 @@ dependencies:
106
120
  version: '5.0'
107
121
  - - "<"
108
122
  - !ruby/object:Gem::Version
109
- version: '6.0'
123
+ version: '6.2'
110
124
  type: :runtime
111
125
  prerelease: false
112
126
  version_requirements: !ruby/object:Gem::Requirement
@@ -116,7 +130,7 @@ dependencies:
116
130
  version: '5.0'
117
131
  - - "<"
118
132
  - !ruby/object:Gem::Version
119
- version: '6.0'
133
+ version: '6.2'
120
134
  description: |-
121
135
  Textacular exposes full text search capabilities from PostgreSQL, extending
122
136
  ActiveRecord with scopes making search easy and fun!
@@ -165,7 +179,7 @@ homepage: http://textacular.github.com/textacular
165
179
  licenses:
166
180
  - MIT
167
181
  metadata: {}
168
- post_install_message:
182
+ post_install_message:
169
183
  rdoc_options: []
170
184
  require_paths:
171
185
  - lib
@@ -180,9 +194,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
180
194
  - !ruby/object:Gem::Version
181
195
  version: '0'
182
196
  requirements: []
183
- rubyforge_project:
184
- rubygems_version: 2.6.8
185
- signing_key:
197
+ rubygems_version: 3.0.3
198
+ signing_key:
186
199
  specification_version: 4
187
200
  summary: Textacular exposes full text search capabilities from PostgreSQL
188
201
  test_files:
@@ -207,4 +220,3 @@ test_files:
207
220
  - spec/textacular/migration_generator_spec.rb
208
221
  - spec/textacular/searchable_spec.rb
209
222
  - spec/textacular/trigram_installer_spec.rb
210
- has_rdoc: