textacular 5.0.0 → 5.4.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: d18e4abd3e1261c47932907d778c6733c930cdff
4
- data.tar.gz: bf77d7e537625947e8b610dfd5177079418fff5c
2
+ SHA256:
3
+ metadata.gz: d2f44e73cb226a66719aca4bdfb54732d7fc4bb2fe7af548b49aad5c81d97cfe
4
+ data.tar.gz: 7d43ad7e191607aef9c521d5218afd98df5bc5cfa98d84cb8189db9026cc44de
5
5
  SHA512:
6
- metadata.gz: 43dc9f5012e6c8bb1da0811e55e48d0795a7fa8eff3a0127ec29c08b02c8fafca339f2e9ecb397abfd77baed9267893ed6efd53809a217903752c3fec1368fd8
7
- data.tar.gz: 28b8cdc8eb8f7c00f12d5827c47aad0204f5e509abab74699618a9feff177a9e8d6b7c6dad053bddde0d6e8c6c03c3ef9fa223824cbd543ddd2746cab2a3a95f
6
+ metadata.gz: 0f8ba724deeb6e82160fea018b012592fd0a407231b81467ba6c52e6f921e9792aeb6653a8adbcf5c6618a5559e434ffd717e13174e21dd04eca8c2fece472fb
7
+ data.tar.gz: b84dfe00f5fd93a8735e3ce37847056fb82fa1ec583b59fa03cfbb411993fc33430e05ffe57986cded34c165261615edc3c524203da2a326d20fd0e3c9dcbc0a
@@ -2,6 +2,24 @@
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
+
17
+ ## 5.1.0
18
+
19
+ * ActiveRecord 5.2 compatibility by wrapping string queries with `Arel.sql()`
20
+ * Adds latest Ruby, Rails and Postgres-dependencies to Travis.
21
+ * Rewrite development migration code to support Rails 5.0-5.2.
22
+
5
23
  ## 5.0.0
6
24
 
7
25
  * ActiveRecord 5.1 compatibility
data/Gemfile CHANGED
@@ -1,3 +1,9 @@
1
1
  source 'https://rubygems.org'
2
2
 
3
3
  gemspec
4
+
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,28 @@ namespace :db do
43
43
 
44
44
  desc 'Run the test database migrations'
45
45
  task :up => :'db:connect' do
46
- ActiveRecord::Migrator.up 'db/migrate'
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
53
+ else
54
+ migrations = ActiveRecord::Migrator.migrations('db/migrate')
55
+ schema_migration = nil
56
+ end
57
+ ActiveRecord::Migrator.new(:up, migrations, schema_migration).migrate
47
58
  end
48
59
 
49
60
  desc 'Reverse the test database migrations'
50
61
  task :down => :'db:connect' do
51
- ActiveRecord::Migrator.down 'db/migrate'
62
+ migrations = if ActiveRecord.version.version >= '5.2'
63
+ ActiveRecord::Migration.new.migration_context.migrations
64
+ else
65
+ ActiveRecord::Migrator.migrations('db/migrate')
66
+ end
67
+ ActiveRecord::Migrator.new(:down, migrations, nil).migrate
52
68
  end
53
69
  end
54
70
  task :migrate => :'migrate:up'
@@ -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,19 +120,38 @@ 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
- select("#{quoted_table_name + '.*,' if select_values.empty?} #{similarities.join(" + ")} AS #{rank}").
152
+ select(Arel.sql("#{quoted_table_name + '.*,' if select_values.empty?} #{similarities.join(" + ")} AS #{rank}")).
127
153
  where(conditions.join(exclusive ? " AND " : " OR ")).
128
- order("#{rank} DESC")
154
+ order(Arel.sql("#{rank} DESC"))
129
155
  end
130
156
 
131
157
  def select_values
@@ -151,7 +177,7 @@ module Textacular
151
177
  module Helper
152
178
  class << self
153
179
  def normalize(query)
154
- query.to_s.gsub(/\s(?![\&|\!|\|])/, '\\\\ ')
180
+ query.to_s.gsub(/\s(?![\&\!\|])/, '\\\\ ')
155
181
  end
156
182
  end
157
183
  end
@@ -4,6 +4,7 @@ class Textacular::MigrationGenerator
4
4
  def initialize(filename, content)
5
5
  @filename = filename
6
6
  @content = content
7
+ @output_stream = nil
7
8
  end
8
9
 
9
10
  def generate_migration
@@ -1,5 +1,5 @@
1
1
  module Textacular
2
- VERSION = '5.0.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.0.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: 2017-06-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: '0.14'
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: '0.14'
29
+ version: '0'
30
30
  - !ruby/object:Gem::Dependency
31
31
  name: rspec
32
32
  requirement: !ruby/object:Gem::Requirement
@@ -97,26 +97,40 @@ 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
103
117
  requirements:
104
118
  - - ">="
105
119
  - !ruby/object:Gem::Version
106
- version: '3.0'
107
- - - "<="
120
+ version: '5.0'
121
+ - - "<"
108
122
  - !ruby/object:Gem::Version
109
- version: '5.1'
123
+ version: '6.2'
110
124
  type: :runtime
111
125
  prerelease: false
112
126
  version_requirements: !ruby/object:Gem::Requirement
113
127
  requirements:
114
128
  - - ">="
115
129
  - !ruby/object:Gem::Version
116
- version: '3.0'
117
- - - "<="
130
+ version: '5.0'
131
+ - - "<"
118
132
  - !ruby/object:Gem::Version
119
- version: '5.1'
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.4.5
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: