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 +5 -5
- data/CHANGELOG.md +12 -0
- data/Gemfile +5 -1
- data/README.md +15 -1
- data/Rakefile +10 -4
- data/lib/textacular.rb +38 -12
- data/lib/textacular/version.rb +1 -1
- data/spec/config.travis.yml +2 -2
- data/spec/textacular/searchable_spec.rb +50 -0
- data/spec/textacular_spec.rb +7 -0
- metadata +26 -14
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: d2f44e73cb226a66719aca4bdfb54732d7fc4bb2fe7af548b49aad5c81d97cfe
|
4
|
+
data.tar.gz: 7d43ad7e191607aef9c521d5218afd98df5bc5cfa98d84cb8189db9026cc44de
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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
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
|
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
|
-
|
47
|
-
ActiveRecord::Migration.new.migration_context
|
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,
|
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
|
-
|
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 ")).
|
data/lib/textacular/version.rb
CHANGED
data/spec/config.travis.yml
CHANGED
@@ -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
|
data/spec/textacular_spec.rb
CHANGED
@@ -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.
|
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:
|
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:
|
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:
|
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.
|
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.
|
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
|
-
|
184
|
-
|
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:
|