pg_search 0.7.4 → 0.7.5
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +4 -0
- data/README.md +30 -13
- data/lib/pg_search/document.rb +4 -2
- data/lib/pg_search/features/tsearch.rb +23 -19
- data/lib/pg_search/migration/associated_against_generator.rb +4 -16
- data/lib/pg_search/migration/dmetaphone_generator.rb +4 -16
- data/lib/pg_search/migration/generator.rb +28 -0
- data/lib/pg_search/migration/multisearch_generator.rb +4 -8
- data/lib/pg_search/migration/templates/add_pg_search_associated_against_support_functions.rb.erb +2 -2
- data/lib/pg_search/migration/templates/{create_pg_search_documents.rb → create_pg_search_documents.rb.erb} +0 -0
- data/lib/pg_search/scope_options.rb +3 -1
- data/lib/pg_search/version.rb +1 -1
- data/pg_search.gemspec +1 -1
- data/spec/integration/associations_spec.rb +25 -25
- data/spec/integration/pagination_spec.rb +4 -4
- data/spec/integration/pg_search_spec.rb +123 -103
- data/spec/lib/pg_search/configuration/column_spec.rb +2 -2
- data/spec/lib/pg_search/document_spec.rb +11 -4
- data/spec/lib/pg_search/features/dmetaphone_spec.rb +4 -2
- data/spec/lib/pg_search/features/tsearch_spec.rb +4 -2
- data/spec/lib/pg_search/multisearch/rebuilder_spec.rb +16 -16
- data/spec/lib/pg_search/multisearch_spec.rb +15 -18
- data/spec/lib/pg_search/multisearchable_spec.rb +39 -38
- data/spec/lib/pg_search/normalizer_spec.rb +8 -8
- data/spec/spec_helper.rb +8 -94
- data/spec/support/coveralls.rb +7 -0
- data/spec/support/database.rb +83 -0
- data/spec/support/with_model.rb +5 -0
- metadata +14 -7
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 56abc4100a0dcd75ed558ec0ec631d53088ab5bf
|
4
|
+
data.tar.gz: 33edaaa054f61e59efa18c8e96f0ecd9cc12630c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d4e3fb0b2571d4fec4f447f1f6df9108b70e6219c7381e9d79e95d559bb063389a4ded5f50db1c320a1564647a83a9050ba6dfb32f3b87adae9866028c7633b8
|
7
|
+
data.tar.gz: f618f99de2af02e89346f6cde00036e6c56b88292511b434140276d470b6033da2717ebc132b5364e4ba9bdf32afc29ed6811098c532916db6973154b0db0d9e
|
data/CHANGELOG.md
CHANGED
data/README.md
CHANGED
@@ -1,17 +1,10 @@
|
|
1
|
-
# pg_search
|
1
|
+
# [pg_search](http://github.com/Casecommons/pg_search/)
|
2
2
|
|
3
|
-
|
4
|
-
|
5
|
-
[
|
6
|
-
|
7
|
-
[
|
8
|
-
/>](https://gemnasium.com/Casecommons/pg_search)
|
9
|
-
[<img src="https://codeclimate.com/github/Casecommons/pg_search.png"
|
10
|
-
/>](https://codeclimate.com/github/Casecommons/pg_search)
|
11
|
-
[<img src="https://coveralls.io/repos/Casecommons/pg_search/badge.png?branch=master"
|
12
|
-
alt="Coverage Status" />](https://coveralls.io/r/Casecommons/pg_search)
|
13
|
-
[<img src="https://badge.fury.io/rb/pg_search.png" alt="Gem Version"
|
14
|
-
/>](http://badge.fury.io/rb/pg_search)
|
3
|
+
[![Build Status](https://secure.travis-ci.org/Casecommons/pg_search.svg?branch=master)](https://travis-ci.org/Casecommons/pg_search)
|
4
|
+
[![Code Climate](https://img.shields.io/codeclimate/github/Casecommons/pg_search.svg)](https://codeclimate.com/github/Casecommons/pg_search)
|
5
|
+
[![Coverage Status](https://img.shields.io/coveralls/Casecommons/pg_search/master.svg)](https://coveralls.io/r/Casecommons/pg_search)
|
6
|
+
[![Gem Version](https://badge.fury.io/rb/pg_search.svg)](https://rubygems.org/gems/pg_search)
|
7
|
+
[![Dependency Status](https://gemnasium.com/Casecommons/pg_search.svg)](https://gemnasium.com/Casecommons/pg_search)
|
15
8
|
|
16
9
|
## DESCRIPTION
|
17
10
|
|
@@ -670,6 +663,30 @@ Number.search_any_word('one two three') # => [one, two, three]
|
|
670
663
|
Number.search_all_words('one two three') # => []
|
671
664
|
```
|
672
665
|
|
666
|
+
##### :sort_only
|
667
|
+
|
668
|
+
Setting this attribute to true will make this feature available for sorting,
|
669
|
+
but will not include it in the query's WHERE condition.
|
670
|
+
|
671
|
+
```ruby
|
672
|
+
class Person < ActiveRecord::Base
|
673
|
+
include PgSearch
|
674
|
+
pg_search_scope :search,
|
675
|
+
:against => :name,
|
676
|
+
:using => {
|
677
|
+
:tsearch => {:any_word => true}
|
678
|
+
:dmetaphone => {:any_word => true, :sort_only => true}
|
679
|
+
}
|
680
|
+
end
|
681
|
+
|
682
|
+
exact = Person.create!(:name => 'ash hines')
|
683
|
+
one_exact_one_close = Person.create!(:name => 'ash heinz')
|
684
|
+
one_exact = Person.create!(:name => 'ash smith')
|
685
|
+
one_close = Person.create!(:name => 'leigh heinz')
|
686
|
+
|
687
|
+
Person.search('ash hines') # => [exact, one_exact_one_close, one_exact]
|
688
|
+
```
|
689
|
+
|
673
690
|
#### :dmetaphone (Double Metaphone soundalike search)
|
674
691
|
|
675
692
|
[Double Metaphone](http://en.wikipedia.org/wiki/Double_Metaphone) is an
|
data/lib/pg_search/document.rb
CHANGED
@@ -1,12 +1,14 @@
|
|
1
|
-
require
|
1
|
+
require 'logger'
|
2
2
|
|
3
3
|
module PgSearch
|
4
4
|
class Document < ActiveRecord::Base
|
5
5
|
include PgSearch
|
6
|
+
|
6
7
|
self.table_name = 'pg_search_documents'
|
7
8
|
belongs_to :searchable, :polymorphic => true
|
8
9
|
|
9
|
-
before_validation :update_content
|
10
|
+
before_validation :update_content,
|
11
|
+
:unless => Proc.new { |doc| doc.searchable.nil? }
|
10
12
|
|
11
13
|
# The logger might not have loaded yet.
|
12
14
|
# https://github.com/Casecommons/pg_search/issues/26
|
@@ -54,25 +54,8 @@ module PgSearch
|
|
54
54
|
end
|
55
55
|
|
56
56
|
def tsdocument
|
57
|
-
tsdocument_terms = []
|
58
|
-
|
59
|
-
columns_to_use = options[:tsvector_column] ?
|
60
|
-
columns.select { |c| c.is_a?(PgSearch::Configuration::ForeignColumn) } :
|
61
|
-
columns
|
62
|
-
|
63
|
-
if columns_to_use.present?
|
64
|
-
tsdocument_terms << columns_to_use.map do |search_column|
|
65
|
-
tsvector = Arel::Nodes::NamedFunction.new(
|
66
|
-
"to_tsvector",
|
67
|
-
[dictionary, Arel.sql(normalize(search_column.to_sql))]
|
68
|
-
).to_sql
|
69
|
-
|
70
|
-
if search_column.weight.nil?
|
71
|
-
tsvector
|
72
|
-
else
|
73
|
-
"setweight(#{tsvector}, #{connection.quote(search_column.weight)})"
|
74
|
-
end
|
75
|
-
end.join(" || ")
|
57
|
+
tsdocument_terms = (columns_to_use || []).map do |search_column|
|
58
|
+
column_to_tsvector(search_column)
|
76
59
|
end
|
77
60
|
|
78
61
|
if options[:tsvector_column]
|
@@ -107,6 +90,27 @@ module PgSearch
|
|
107
90
|
def arel_wrap(sql_string)
|
108
91
|
Arel::Nodes::Grouping.new(Arel.sql(sql_string))
|
109
92
|
end
|
93
|
+
|
94
|
+
def columns_to_use
|
95
|
+
if options[:tsvector_column]
|
96
|
+
columns.select { |c| c.is_a?(PgSearch::Configuration::ForeignColumn) }
|
97
|
+
else
|
98
|
+
columns
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
def column_to_tsvector(search_column)
|
103
|
+
tsvector = Arel::Nodes::NamedFunction.new(
|
104
|
+
"to_tsvector",
|
105
|
+
[dictionary, Arel.sql(normalize(search_column.to_sql))]
|
106
|
+
).to_sql
|
107
|
+
|
108
|
+
if search_column.weight.nil?
|
109
|
+
tsvector
|
110
|
+
else
|
111
|
+
"setweight(#{tsvector}, #{connection.quote(search_column.weight)})"
|
112
|
+
end
|
113
|
+
end
|
110
114
|
end
|
111
115
|
end
|
112
116
|
end
|
@@ -1,22 +1,10 @@
|
|
1
|
-
require
|
1
|
+
require 'pg_search/migration/generator'
|
2
2
|
|
3
3
|
module PgSearch
|
4
4
|
module Migration
|
5
|
-
class AssociatedAgainstGenerator <
|
6
|
-
|
7
|
-
|
8
|
-
def create_migration
|
9
|
-
now = Time.now.utc
|
10
|
-
filename = "#{now.strftime('%Y%m%d%H%M%S')}_add_pg_search_associated_against_support_functions.rb"
|
11
|
-
template "add_pg_search_associated_against_support_functions.rb.erb", "db/migrate/#{filename}"
|
12
|
-
end
|
13
|
-
|
14
|
-
private
|
15
|
-
|
16
|
-
def read_sql_file(filename)
|
17
|
-
sql_directory = File.expand_path("../../../../sql", __FILE__)
|
18
|
-
source_path = File.join(sql_directory, "#{filename}.sql")
|
19
|
-
File.read(source_path)
|
5
|
+
class AssociatedAgainstGenerator < Generator
|
6
|
+
def migration_name
|
7
|
+
'add_pg_search_associated_against_support_functions'.freeze
|
20
8
|
end
|
21
9
|
end
|
22
10
|
end
|
@@ -1,22 +1,10 @@
|
|
1
|
-
require
|
1
|
+
require 'pg_search/migration/generator'
|
2
2
|
|
3
3
|
module PgSearch
|
4
4
|
module Migration
|
5
|
-
class DmetaphoneGenerator <
|
6
|
-
|
7
|
-
|
8
|
-
def create_migration
|
9
|
-
now = Time.now.utc
|
10
|
-
filename = "#{now.strftime('%Y%m%d%H%M%S')}_add_pg_search_dmetaphone_support_functions.rb"
|
11
|
-
template "add_pg_search_dmetaphone_support_functions.rb.erb", "db/migrate/#{filename}"
|
12
|
-
end
|
13
|
-
|
14
|
-
private
|
15
|
-
|
16
|
-
def read_sql_file(filename)
|
17
|
-
sql_directory = File.expand_path("../../../../sql", __FILE__)
|
18
|
-
source_path = File.join(sql_directory, "#{filename}.sql")
|
19
|
-
File.read(source_path)
|
5
|
+
class DmetaphoneGenerator < Generator
|
6
|
+
def migration_name
|
7
|
+
'add_pg_search_dmetaphone_support_functions'.freeze
|
20
8
|
end
|
21
9
|
end
|
22
10
|
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
require 'rails/generators/base'
|
2
|
+
|
3
|
+
module PgSearch
|
4
|
+
module Migration
|
5
|
+
class Generator < Rails::Generators::Base
|
6
|
+
hide!
|
7
|
+
|
8
|
+
def self.inherited(subclass)
|
9
|
+
super
|
10
|
+
subclass.source_root File.expand_path('../templates', __FILE__)
|
11
|
+
end
|
12
|
+
|
13
|
+
def create_migration
|
14
|
+
now = Time.now.utc
|
15
|
+
filename = "#{now.strftime('%Y%m%d%H%M%S')}_#{migration_name}.rb"
|
16
|
+
template "#{migration_name}.rb.erb", "db/migrate/#{filename}"
|
17
|
+
end
|
18
|
+
|
19
|
+
private
|
20
|
+
|
21
|
+
def read_sql_file(filename)
|
22
|
+
sql_directory = File.expand_path('../../../../sql', __FILE__)
|
23
|
+
source_path = File.join(sql_directory, "#{filename}.sql")
|
24
|
+
File.read(source_path).strip
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -1,14 +1,10 @@
|
|
1
|
-
require
|
1
|
+
require 'pg_search/migration/generator'
|
2
2
|
|
3
3
|
module PgSearch
|
4
4
|
module Migration
|
5
|
-
class MultisearchGenerator <
|
6
|
-
|
7
|
-
|
8
|
-
def create_migration
|
9
|
-
now = Time.now.utc
|
10
|
-
filename = "#{now.strftime('%Y%m%d%H%M%S')}_create_pg_search_documents.rb"
|
11
|
-
copy_file "create_pg_search_documents.rb", "db/migrate/#{filename}"
|
5
|
+
class MultisearchGenerator < Generator
|
6
|
+
def migration_name
|
7
|
+
'create_pg_search_documents'
|
12
8
|
end
|
13
9
|
end
|
14
10
|
end
|
data/lib/pg_search/migration/templates/add_pg_search_associated_against_support_functions.rb.erb
CHANGED
@@ -3,7 +3,7 @@ class AddPgSearchAssociatedAgainstSupportFunctions < ActiveRecord::Migration
|
|
3
3
|
say_with_time("Adding support functions for pg_search :associated_against") do
|
4
4
|
if ActiveRecord::Base.connection.send(:postgresql_version) < 80400
|
5
5
|
execute <<-'SQL'
|
6
|
-
|
6
|
+
<%= read_sql_file "array_agg" %>
|
7
7
|
SQL
|
8
8
|
end
|
9
9
|
end
|
@@ -13,7 +13,7 @@ class AddPgSearchAssociatedAgainstSupportFunctions < ActiveRecord::Migration
|
|
13
13
|
say_with_time("Dropping support functions for pg_search :associated_against") do
|
14
14
|
if ActiveRecord::Base.connection.send(:postgresql_version) < 80400
|
15
15
|
execute <<-'SQL'
|
16
|
-
|
16
|
+
<%= read_sql_file "uninstall_array_agg" %>
|
17
17
|
SQL
|
18
18
|
end
|
19
19
|
end
|
File without changes
|
@@ -33,7 +33,9 @@ module PgSearch
|
|
33
33
|
delegate :connection, :quoted_table_name, :to => :@model
|
34
34
|
|
35
35
|
def conditions
|
36
|
-
config.features.
|
36
|
+
config.features.reject do |feature_name, feature_options|
|
37
|
+
feature_options && feature_options[:sort_only]
|
38
|
+
end.map do |feature_name, feature_options|
|
37
39
|
feature_for(feature_name).conditions
|
38
40
|
end.inject do |accumulator, expression|
|
39
41
|
Arel::Nodes::Or.new(accumulator, expression)
|
data/lib/pg_search/version.rb
CHANGED
data/pg_search.gemspec
CHANGED
@@ -24,7 +24,7 @@ Gem::Specification.new do |s|
|
|
24
24
|
|
25
25
|
s.add_development_dependency 'rake'
|
26
26
|
s.add_development_dependency 'pry'
|
27
|
-
s.add_development_dependency 'rspec', '
|
27
|
+
s.add_development_dependency 'rspec', '~> 3.0'
|
28
28
|
s.add_development_dependency 'with_model'
|
29
29
|
|
30
30
|
s.required_ruby_version = ">= 1.9.2"
|
@@ -34,8 +34,8 @@ describe PgSearch do
|
|
34
34
|
]
|
35
35
|
|
36
36
|
results = ModelWithoutAgainst.with_another('abcdef')
|
37
|
-
results.map(&:title).
|
38
|
-
results.
|
37
|
+
expect(results.map(&:title)).to match_array(included.map(&:title))
|
38
|
+
expect(results).not_to include(excluded)
|
39
39
|
end
|
40
40
|
end
|
41
41
|
|
@@ -70,8 +70,8 @@ describe PgSearch do
|
|
70
70
|
:another_model => AssociatedModel.create!(:title => 'stuvwx'))
|
71
71
|
|
72
72
|
results = ModelWithBelongsTo.with_associated('abcdef')
|
73
|
-
results.map(&:title).
|
74
|
-
results.
|
73
|
+
expect(results.map(&:title)).to match_array(included.map(&:title))
|
74
|
+
expect(results).not_to include(excluded)
|
75
75
|
end
|
76
76
|
end
|
77
77
|
|
@@ -113,8 +113,8 @@ describe PgSearch do
|
|
113
113
|
])
|
114
114
|
|
115
115
|
results = ModelWithHasMany.with_associated('foo bar')
|
116
|
-
results.map(&:title).
|
117
|
-
results.
|
116
|
+
expect(results.map(&:title)).to match_array(included.map(&:title))
|
117
|
+
expect(results).not_to include(excluded)
|
118
118
|
end
|
119
119
|
|
120
120
|
it "uses an unscoped relation of the assocated model" do
|
@@ -129,8 +129,8 @@ describe PgSearch do
|
|
129
129
|
]
|
130
130
|
|
131
131
|
results = ModelWithHasMany.limit(1).order("id ASC").with_associated('foo bar')
|
132
|
-
results.map(&:title).
|
133
|
-
results.
|
132
|
+
expect(results.map(&:title)).to match_array(included.map(&:title))
|
133
|
+
expect(results).not_to include(excluded)
|
134
134
|
end
|
135
135
|
|
136
136
|
end
|
@@ -192,8 +192,8 @@ describe PgSearch do
|
|
192
192
|
]
|
193
193
|
|
194
194
|
results = ModelWithManyAssociations.with_associated('foo bar')
|
195
|
-
results.map(&:title).
|
196
|
-
excluded.each { |object| results.
|
195
|
+
expect(results.map(&:title)).to match_array(included.map(&:title))
|
196
|
+
excluded.each { |object| expect(results).not_to include(object) }
|
197
197
|
end
|
198
198
|
end
|
199
199
|
|
@@ -247,8 +247,8 @@ describe PgSearch do
|
|
247
247
|
]
|
248
248
|
|
249
249
|
results = ModelWithDoubleAssociation.with_associated('foo bar')
|
250
|
-
results.map(&:title).
|
251
|
-
excluded.each { |object| results.
|
250
|
+
expect(results.map(&:title)).to match_array(included.map(&:title))
|
251
|
+
excluded.each { |object| expect(results).not_to include(object) }
|
252
252
|
end
|
253
253
|
end
|
254
254
|
end
|
@@ -300,9 +300,9 @@ describe PgSearch do
|
|
300
300
|
|
301
301
|
results = ModelWithAssociation.with_associated('foo bar')
|
302
302
|
|
303
|
-
results.to_sql.scan("INNER JOIN").length.
|
304
|
-
included.each { |object| results.
|
305
|
-
excluded.each { |object| results.
|
303
|
+
expect(results.to_sql.scan("INNER JOIN").length).to eq(1)
|
304
|
+
included.each { |object| expect(results).to include(object) }
|
305
|
+
excluded.each { |object| expect(results).not_to include(object) }
|
306
306
|
end
|
307
307
|
|
308
308
|
end
|
@@ -339,8 +339,8 @@ describe PgSearch do
|
|
339
339
|
]
|
340
340
|
|
341
341
|
results = Model.with_associated('123')
|
342
|
-
results.map(&:number).
|
343
|
-
results.
|
342
|
+
expect(results.map(&:number)).to match_array(included.map(&:number))
|
343
|
+
expect(results).not_to include(excluded)
|
344
344
|
end
|
345
345
|
end
|
346
346
|
|
@@ -375,8 +375,8 @@ describe PgSearch do
|
|
375
375
|
results = Parent.search_name('bar.foo').includes(:children)
|
376
376
|
results.to_a
|
377
377
|
|
378
|
-
results.
|
379
|
-
results.
|
378
|
+
expect(results).to include(included)
|
379
|
+
expect(results).not_to include(excluded)
|
380
380
|
end
|
381
381
|
end
|
382
382
|
end
|
@@ -422,8 +422,8 @@ describe PgSearch do
|
|
422
422
|
|
423
423
|
results = ModelWithAssociation.joins(:associated_models).merge(relation)
|
424
424
|
|
425
|
-
results.
|
426
|
-
results.
|
425
|
+
expect(results).to include(*included)
|
426
|
+
expect(results).not_to include(*excluded)
|
427
427
|
end
|
428
428
|
end
|
429
429
|
|
@@ -463,8 +463,8 @@ describe PgSearch do
|
|
463
463
|
|
464
464
|
results = company.positions.search('teller 1')
|
465
465
|
|
466
|
-
results.
|
467
|
-
results.
|
466
|
+
expect(results).to include(*included)
|
467
|
+
expect(results).not_to include(*excluded)
|
468
468
|
end
|
469
469
|
end
|
470
470
|
|
@@ -505,8 +505,8 @@ describe PgSearch do
|
|
505
505
|
|
506
506
|
results = company.positions.search('teller 1')
|
507
507
|
|
508
|
-
results.
|
509
|
-
results.
|
508
|
+
expect(results).to include(*included)
|
509
|
+
expect(results).not_to include(*excluded)
|
510
510
|
end
|
511
511
|
end
|
512
512
|
end
|
@@ -23,8 +23,8 @@ describe "pagination" do
|
|
23
23
|
best = PaginatedModel.create!(:name => "foo foo foo")
|
24
24
|
good = PaginatedModel.create!(:name => "foo bar bar")
|
25
25
|
|
26
|
-
PaginatedModel.page(1).search_name("foo").
|
27
|
-
PaginatedModel.page(2).search_name("foo").
|
26
|
+
expect(PaginatedModel.page(1).search_name("foo")).to eq([best, better])
|
27
|
+
expect(PaginatedModel.page(2).search_name("foo")).to eq([good])
|
28
28
|
end
|
29
29
|
|
30
30
|
it "is chainable after a search scope" do
|
@@ -32,8 +32,8 @@ describe "pagination" do
|
|
32
32
|
best = PaginatedModel.create!(:name => "foo foo foo")
|
33
33
|
good = PaginatedModel.create!(:name => "foo bar bar")
|
34
34
|
|
35
|
-
PaginatedModel.search_name("foo").page(1).
|
36
|
-
PaginatedModel.search_name("foo").page(2).
|
35
|
+
expect(PaginatedModel.search_name("foo").page(1)).to eq([best, better])
|
36
|
+
expect(PaginatedModel.search_name("foo").page(2)).to eq([good])
|
37
37
|
end
|
38
38
|
end
|
39
39
|
end
|