sunspot 2.0.0 → 2.5.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +1 -0
- data/.rspec +2 -0
- data/Appraisals +7 -0
- data/Gemfile +0 -2
- data/History.txt +10 -0
- data/lib/sunspot.rb +55 -17
- data/lib/sunspot/adapters.rb +68 -18
- data/lib/sunspot/batcher.rb +1 -1
- data/lib/sunspot/configuration.rb +4 -2
- data/lib/sunspot/data_extractor.rb +36 -6
- data/lib/sunspot/dsl.rb +4 -3
- data/lib/sunspot/dsl/adjustable.rb +2 -2
- data/lib/sunspot/dsl/field_query.rb +69 -16
- data/lib/sunspot/dsl/field_stats.rb +25 -0
- data/lib/sunspot/dsl/fields.rb +28 -8
- data/lib/sunspot/dsl/fulltext.rb +9 -1
- data/lib/sunspot/dsl/group.rb +118 -0
- data/lib/sunspot/dsl/paginatable.rb +4 -1
- data/lib/sunspot/dsl/scope.rb +19 -10
- data/lib/sunspot/dsl/search.rb +1 -1
- data/lib/sunspot/dsl/spellcheckable.rb +14 -0
- data/lib/sunspot/dsl/standard_query.rb +63 -35
- data/lib/sunspot/field.rb +76 -4
- data/lib/sunspot/field_factory.rb +60 -11
- data/lib/sunspot/indexer.rb +70 -18
- data/lib/sunspot/query.rb +5 -4
- data/lib/sunspot/query/abstract_field_facet.rb +0 -2
- data/lib/sunspot/query/abstract_fulltext.rb +76 -0
- data/lib/sunspot/query/abstract_json_field_facet.rb +70 -0
- data/lib/sunspot/query/bbox.rb +5 -1
- data/lib/sunspot/query/common_query.rb +31 -6
- data/lib/sunspot/query/composite_fulltext.rb +58 -8
- data/lib/sunspot/query/date_field_json_facet.rb +25 -0
- data/lib/sunspot/query/dismax.rb +25 -71
- data/lib/sunspot/query/field_json_facet.rb +19 -0
- data/lib/sunspot/query/field_list.rb +15 -0
- data/lib/sunspot/query/field_stats.rb +61 -0
- data/lib/sunspot/query/function_query.rb +1 -2
- data/lib/sunspot/query/geo.rb +1 -1
- data/lib/sunspot/query/geofilt.rb +8 -3
- data/lib/sunspot/query/group.rb +46 -0
- data/lib/sunspot/query/group_query.rb +17 -0
- data/lib/sunspot/query/join.rb +88 -0
- data/lib/sunspot/query/more_like_this.rb +1 -1
- data/lib/sunspot/query/pagination.rb +12 -4
- data/lib/sunspot/query/range_json_facet.rb +28 -0
- data/lib/sunspot/query/restriction.rb +99 -13
- data/lib/sunspot/query/sort.rb +41 -0
- data/lib/sunspot/query/sort_composite.rb +7 -0
- data/lib/sunspot/query/spellcheck.rb +19 -0
- data/lib/sunspot/query/standard_query.rb +24 -2
- data/lib/sunspot/query/text_field_boost.rb +1 -3
- data/lib/sunspot/schema.rb +12 -3
- data/lib/sunspot/search.rb +4 -2
- data/lib/sunspot/search/abstract_search.rb +93 -43
- data/lib/sunspot/search/cursor_paginated_collection.rb +32 -0
- data/lib/sunspot/search/field_facet.rb +4 -4
- data/lib/sunspot/search/field_json_facet.rb +33 -0
- data/lib/sunspot/search/field_stats.rb +21 -0
- data/lib/sunspot/search/hit.rb +6 -1
- data/lib/sunspot/search/hit_enumerable.rb +4 -1
- data/lib/sunspot/search/json_facet_row.rb +40 -0
- data/lib/sunspot/search/json_facet_stats.rb +23 -0
- data/lib/sunspot/search/paginated_collection.rb +1 -0
- data/lib/sunspot/search/query_group.rb +74 -0
- data/lib/sunspot/search/standard_search.rb +70 -3
- data/lib/sunspot/search/stats_facet.rb +25 -0
- data/lib/sunspot/search/stats_json_row.rb +82 -0
- data/lib/sunspot/search/stats_row.rb +68 -0
- data/lib/sunspot/session.rb +62 -37
- data/lib/sunspot/session_proxy/class_sharding_session_proxy.rb +6 -4
- data/lib/sunspot/session_proxy/id_sharding_session_proxy.rb +16 -8
- data/lib/sunspot/session_proxy/master_slave_session_proxy.rb +2 -2
- data/lib/sunspot/session_proxy/retry_5xx_session_proxy.rb +1 -1
- data/lib/sunspot/session_proxy/sharding_session_proxy.rb +4 -2
- data/lib/sunspot/session_proxy/silent_fail_session_proxy.rb +1 -1
- data/lib/sunspot/session_proxy/thread_local_session_proxy.rb +6 -4
- data/lib/sunspot/setup.rb +42 -0
- data/lib/sunspot/type.rb +20 -0
- data/lib/sunspot/util.rb +78 -14
- data/lib/sunspot/version.rb +1 -1
- data/spec/api/adapters_spec.rb +40 -15
- data/spec/api/batcher_spec.rb +15 -15
- data/spec/api/binding_spec.rb +3 -3
- data/spec/api/class_set_spec.rb +6 -6
- data/spec/api/data_extractor_spec.rb +39 -0
- data/spec/api/hit_enumerable_spec.rb +32 -9
- data/spec/api/indexer/attributes_spec.rb +35 -30
- data/spec/api/indexer/batch_spec.rb +8 -7
- data/spec/api/indexer/dynamic_fields_spec.rb +8 -8
- data/spec/api/indexer/fixed_fields_spec.rb +16 -11
- data/spec/api/indexer/fulltext_spec.rb +8 -8
- data/spec/api/indexer/removal_spec.rb +24 -14
- data/spec/api/indexer_spec.rb +2 -2
- data/spec/api/query/advanced_manipulation_examples.rb +3 -3
- data/spec/api/query/connectives_examples.rb +26 -14
- data/spec/api/query/dsl_spec.rb +24 -6
- data/spec/api/query/dynamic_fields_examples.rb +18 -18
- data/spec/api/query/faceting_examples.rb +80 -61
- data/spec/api/query/fulltext_examples.rb +194 -40
- data/spec/api/query/function_spec.rb +116 -13
- data/spec/api/query/geo_examples.rb +8 -12
- data/spec/api/query/group_spec.rb +27 -5
- data/spec/api/query/highlighting_examples.rb +26 -26
- data/spec/api/query/join_spec.rb +19 -0
- data/spec/api/query/more_like_this_spec.rb +40 -27
- data/spec/api/query/ordering_pagination_examples.rb +37 -23
- data/spec/api/query/scope_examples.rb +39 -39
- data/spec/api/query/spatial_examples.rb +3 -3
- data/spec/api/query/spellcheck_examples.rb +20 -0
- data/spec/api/query/standard_spec.rb +3 -1
- data/spec/api/query/stats_examples.rb +66 -0
- data/spec/api/query/text_field_scoping_examples.rb +5 -5
- data/spec/api/query/types_spec.rb +4 -4
- data/spec/api/search/cursor_paginated_collection_spec.rb +35 -0
- data/spec/api/search/dynamic_fields_spec.rb +4 -4
- data/spec/api/search/faceting_spec.rb +55 -52
- data/spec/api/search/highlighting_spec.rb +7 -7
- data/spec/api/search/hits_spec.rb +43 -29
- data/spec/api/search/paginated_collection_spec.rb +19 -18
- data/spec/api/search/results_spec.rb +13 -13
- data/spec/api/search/search_spec.rb +3 -3
- data/spec/api/search/stats_spec.rb +94 -0
- data/spec/api/session_proxy/class_sharding_session_proxy_spec.rb +23 -16
- data/spec/api/session_proxy/id_sharding_session_proxy_spec.rb +16 -4
- data/spec/api/session_proxy/master_slave_session_proxy_spec.rb +10 -6
- data/spec/api/session_proxy/retry_5xx_session_proxy_spec.rb +11 -11
- data/spec/api/session_proxy/sharding_session_proxy_spec.rb +15 -14
- data/spec/api/session_proxy/silent_fail_session_proxy_spec.rb +3 -3
- data/spec/api/session_proxy/spec_helper.rb +1 -1
- data/spec/api/session_proxy/thread_local_session_proxy_spec.rb +40 -26
- data/spec/api/session_spec.rb +78 -38
- data/spec/api/sunspot_spec.rb +7 -4
- data/spec/helpers/integration_helper.rb +11 -1
- data/spec/helpers/query_helper.rb +1 -1
- data/spec/helpers/search_helper.rb +30 -0
- data/spec/integration/atomic_updates_spec.rb +58 -0
- data/spec/integration/dynamic_fields_spec.rb +31 -20
- data/spec/integration/faceting_spec.rb +252 -39
- data/spec/integration/field_grouping_spec.rb +47 -15
- data/spec/integration/field_lists_spec.rb +57 -0
- data/spec/integration/geospatial_spec.rb +34 -8
- data/spec/integration/highlighting_spec.rb +8 -8
- data/spec/integration/indexing_spec.rb +7 -6
- data/spec/integration/join_spec.rb +45 -0
- data/spec/integration/keyword_search_spec.rb +68 -38
- data/spec/integration/local_search_spec.rb +4 -4
- data/spec/integration/more_like_this_spec.rb +7 -7
- data/spec/integration/scoped_search_spec.rb +193 -74
- data/spec/integration/spellcheck_spec.rb +119 -0
- data/spec/integration/stats_spec.rb +88 -0
- data/spec/integration/stored_fields_spec.rb +1 -1
- data/spec/integration/test_pagination.rb +4 -4
- data/spec/integration/unicode_spec.rb +1 -1
- data/spec/mocks/adapters.rb +36 -0
- data/spec/mocks/connection.rb +5 -3
- data/spec/mocks/photo.rb +32 -1
- data/spec/mocks/post.rb +18 -3
- data/spec/spec_helper.rb +13 -8
- data/sunspot.gemspec +6 -4
- data/tasks/rdoc.rake +22 -14
- metadata +101 -44
- data/lib/sunspot/dsl/field_group.rb +0 -57
- data/lib/sunspot/query/field_group.rb +0 -37
@@ -19,8 +19,8 @@ describe "field grouping" do
|
|
19
19
|
group :title
|
20
20
|
end
|
21
21
|
|
22
|
-
search.group(:title).groups.
|
23
|
-
search.group(:title).groups.
|
22
|
+
expect(search.group(:title).groups).to include { |g| g.value == "Title1" }
|
23
|
+
expect(search.group(:title).groups).to include { |g| g.value == "Title2" }
|
24
24
|
end
|
25
25
|
|
26
26
|
it "returns the number of matches unique groups" do
|
@@ -28,7 +28,7 @@ describe "field grouping" do
|
|
28
28
|
group :title
|
29
29
|
end
|
30
30
|
|
31
|
-
search.group(:title).total.
|
31
|
+
expect(search.group(:title).total).to eq(2)
|
32
32
|
end
|
33
33
|
|
34
34
|
it "provides access to the number of matches before grouping" do
|
@@ -36,7 +36,7 @@ describe "field grouping" do
|
|
36
36
|
group :title
|
37
37
|
end
|
38
38
|
|
39
|
-
search.group(:title).matches.
|
39
|
+
expect(search.group(:title).matches).to eq(@posts.length)
|
40
40
|
end
|
41
41
|
|
42
42
|
it "allows grouping by multiple fields" do
|
@@ -44,8 +44,8 @@ describe "field grouping" do
|
|
44
44
|
group :title, :sort_title
|
45
45
|
end
|
46
46
|
|
47
|
-
search.group(:title).groups.
|
48
|
-
search.group(:sort_title).groups.
|
47
|
+
expect(search.group(:title).groups).not_to be_empty
|
48
|
+
expect(search.group(:sort_title).groups).not_to be_empty
|
49
49
|
end
|
50
50
|
|
51
51
|
it "allows specification of the number of documents per group" do
|
@@ -56,7 +56,7 @@ describe "field grouping" do
|
|
56
56
|
end
|
57
57
|
|
58
58
|
title1_group = search.group(:title).groups.detect { |g| g.value == "Title1" }
|
59
|
-
title1_group.hits.length.
|
59
|
+
expect(title1_group.hits.length).to eq(2)
|
60
60
|
end
|
61
61
|
|
62
62
|
it "allows specification of the sort within groups" do
|
@@ -69,7 +69,20 @@ describe "field grouping" do
|
|
69
69
|
highest_ranked_post = @posts.sort_by { |p| -p.ratings_average }.first
|
70
70
|
|
71
71
|
title1_group = search.group(:title).groups.detect { |g| g.value == "Title1" }
|
72
|
-
title1_group.hits.first.primary_key.to_i.
|
72
|
+
expect(title1_group.hits.first.primary_key.to_i).to eq(highest_ranked_post.id)
|
73
|
+
end
|
74
|
+
|
75
|
+
it "allows specification of an ordering function within groups" do
|
76
|
+
search = Sunspot.search(Post) do
|
77
|
+
group :title do
|
78
|
+
order_by_function(:product, :average_rating, -2, :asc)
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
highest_ranked_post = @posts.sort_by { |p| -p.ratings_average }.first
|
83
|
+
|
84
|
+
title1_group = search.group(:title).groups.detect { |g| g.value == "Title1" }
|
85
|
+
expect(title1_group.hits.first.primary_key.to_i).to eq(highest_ranked_post.id)
|
73
86
|
end
|
74
87
|
|
75
88
|
it "allows pagination within groups" do
|
@@ -78,8 +91,27 @@ describe "field grouping" do
|
|
78
91
|
paginate :per_page => 1, :page => 2
|
79
92
|
end
|
80
93
|
|
81
|
-
search.group(:title).groups.length.
|
82
|
-
search.group(:title).groups.first.results.
|
94
|
+
expect(search.group(:title).groups.length).to eql(1)
|
95
|
+
expect(search.group(:title).groups.first.results).to eq([ @posts.last ])
|
96
|
+
end
|
97
|
+
|
98
|
+
context "returns a not paginated collection" do
|
99
|
+
subject do
|
100
|
+
search = Sunspot.search(Post) do
|
101
|
+
group :title do
|
102
|
+
ngroups false
|
103
|
+
end
|
104
|
+
paginate :per_page => 1, :page => 2
|
105
|
+
|
106
|
+
end
|
107
|
+
search.group(:title).groups
|
108
|
+
end
|
109
|
+
|
110
|
+
it { expect(subject.per_page).to eql(1) }
|
111
|
+
it { expect(subject.total_pages).to eql(0) }
|
112
|
+
it { expect(subject.current_page).to eql(2) }
|
113
|
+
it { expect(subject.first_page?).to be(false) }
|
114
|
+
it { expect(subject.last_page?).to be(true) }
|
83
115
|
end
|
84
116
|
|
85
117
|
context "returns a paginated collection" do
|
@@ -91,10 +123,10 @@ describe "field grouping" do
|
|
91
123
|
search.group(:title).groups
|
92
124
|
end
|
93
125
|
|
94
|
-
it { subject.per_page.
|
95
|
-
it { subject.total_pages.
|
96
|
-
it { subject.current_page.
|
97
|
-
it { subject.first_page
|
98
|
-
it { subject.last_page
|
126
|
+
it { expect(subject.per_page).to eql(1) }
|
127
|
+
it { expect(subject.total_pages).to eql(2) }
|
128
|
+
it { expect(subject.current_page).to eql(2) }
|
129
|
+
it { expect(subject.first_page?).to be(false) }
|
130
|
+
it { expect(subject.last_page?).to be(true) }
|
99
131
|
end
|
100
132
|
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
require File.expand_path('../spec_helper', File.dirname(__FILE__))
|
2
|
+
|
3
|
+
describe 'fields lists' do
|
4
|
+
before :all do
|
5
|
+
Sunspot.remove_all
|
6
|
+
@post = Post.new(title: 'A Title', body: 'A Body', featured: true, tags: ['tag'])
|
7
|
+
Sunspot.index!(@post)
|
8
|
+
end
|
9
|
+
|
10
|
+
let(:stored_field_names) do
|
11
|
+
(Sunspot::Setup.for(Post).fields + Sunspot::Setup.for(Post).all_text_fields)
|
12
|
+
.select { |f| f.stored? }
|
13
|
+
.map { |f| f.name }
|
14
|
+
end
|
15
|
+
|
16
|
+
it 'loads all stored fields by dafault' do
|
17
|
+
hit = Sunspot.search(Post).hits.first
|
18
|
+
|
19
|
+
stored_field_names.each do |field|
|
20
|
+
expect(hit.stored(field)).not_to be_nil
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
it 'loads only filtered fields' do
|
25
|
+
hit = Sunspot.search(Post) { field_list(:title) }.hits.first
|
26
|
+
|
27
|
+
expect(hit.stored(:title)).to eq(@post.title)
|
28
|
+
|
29
|
+
(stored_field_names - [:title]).each do |field|
|
30
|
+
expect(hit.stored(field)).to be_nil
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
it 'does not raise Sunspot::UnrecognizedFieldError when listing existing text fields' do
|
35
|
+
expect do
|
36
|
+
Sunspot.search(Post) {
|
37
|
+
field_list(:body)
|
38
|
+
}
|
39
|
+
end.to_not raise_error
|
40
|
+
end
|
41
|
+
|
42
|
+
it 'does raise Sunspot::UnrecognizedFieldError when listing a non-existent text fields' do
|
43
|
+
expect do
|
44
|
+
Sunspot.search(Post) {
|
45
|
+
field_list(:bogus_body)
|
46
|
+
}
|
47
|
+
end.to raise_error(Sunspot::UnrecognizedFieldError)
|
48
|
+
end
|
49
|
+
|
50
|
+
it 'does not load any stored fields' do
|
51
|
+
hit = Sunspot.search(Post) { without_stored_fields }.hits.first
|
52
|
+
|
53
|
+
stored_field_names.each do |field|
|
54
|
+
expect(hit.stored(field)).to be_nil
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
@@ -15,7 +15,7 @@ describe "geospatial search" do
|
|
15
15
|
with(:coordinates_new).in_radius(32, -68, 1)
|
16
16
|
}.results
|
17
17
|
|
18
|
-
results.
|
18
|
+
expect(results).to include(@post)
|
19
19
|
end
|
20
20
|
|
21
21
|
it "filters out posts not in the radius" do
|
@@ -23,18 +23,44 @@ describe "geospatial search" do
|
|
23
23
|
with(:coordinates_new).in_radius(33, -68, 1)
|
24
24
|
}.results
|
25
25
|
|
26
|
-
results.
|
26
|
+
expect(results).not_to include(@post)
|
27
27
|
end
|
28
28
|
|
29
|
-
it "
|
29
|
+
it "filters out posts in the radius" do
|
30
|
+
results = Sunspot.search(Post) {
|
31
|
+
without(:coordinates_new).in_radius(32, -68, 1)
|
32
|
+
}.results
|
33
|
+
|
34
|
+
expect(results).not_to include(@post)
|
35
|
+
end
|
36
|
+
|
37
|
+
it "allows conjunction queries with radius" do
|
38
|
+
post = Post.new(:title => "Howdy",
|
39
|
+
:coordinates => Sunspot::Util::Coordinates.new(35, -68))
|
40
|
+
|
41
|
+
Sunspot.index!(post)
|
42
|
+
|
30
43
|
results = Sunspot.search(Post) {
|
31
44
|
any_of do
|
32
45
|
with(:coordinates_new).in_radius(32, -68, 1)
|
33
46
|
with(:coordinates_new).in_radius(35, 68, 1)
|
47
|
+
without(:coordinates_new).in_radius(35, -68, 1)
|
48
|
+
end
|
49
|
+
}.results
|
50
|
+
|
51
|
+
expect(results).to include(@post)
|
52
|
+
expect(results).not_to include(post)
|
53
|
+
end
|
54
|
+
|
55
|
+
it "allows conjunction queries with bounding box" do
|
56
|
+
results = Sunspot.search(Post) {
|
57
|
+
any_of do
|
58
|
+
with(:coordinates_new).in_bounding_box([31, -69], [33, -67])
|
59
|
+
with(:coordinates_new).in_bounding_box([35, 68], [36, 69])
|
34
60
|
end
|
35
61
|
}.results
|
36
62
|
|
37
|
-
results.
|
63
|
+
expect(results).to include(@post)
|
38
64
|
end
|
39
65
|
end
|
40
66
|
|
@@ -52,7 +78,7 @@ describe "geospatial search" do
|
|
52
78
|
with(:coordinates_new).in_bounding_box [31, -69], [33, -67]
|
53
79
|
}.results
|
54
80
|
|
55
|
-
results.
|
81
|
+
expect(results).to include(@post)
|
56
82
|
end
|
57
83
|
|
58
84
|
it "filters out posts not in the bounding box" do
|
@@ -60,7 +86,7 @@ describe "geospatial search" do
|
|
60
86
|
with(:coordinates_new).in_bounding_box [20, -70], [21, -69]
|
61
87
|
}.results
|
62
88
|
|
63
|
-
results.
|
89
|
+
expect(results).not_to include(@post)
|
64
90
|
end
|
65
91
|
end
|
66
92
|
|
@@ -82,7 +108,7 @@ describe "geospatial search" do
|
|
82
108
|
order_by_geodist(:coordinates_new, 32, -68)
|
83
109
|
}.results
|
84
110
|
|
85
|
-
results.
|
111
|
+
expect(results).to eq(@posts.reverse)
|
86
112
|
end
|
87
113
|
|
88
114
|
it "orders posts by distance descending" do
|
@@ -90,7 +116,7 @@ describe "geospatial search" do
|
|
90
116
|
order_by_geodist(:coordinates_new, 32, -68, :desc)
|
91
117
|
}.results
|
92
118
|
|
93
|
-
results.
|
119
|
+
expect(results).to eq(@posts)
|
94
120
|
end
|
95
121
|
end
|
96
122
|
end
|
@@ -11,22 +11,22 @@ describe 'keyword highlighting' do
|
|
11
11
|
end
|
12
12
|
|
13
13
|
it 'should include highlights in the results' do
|
14
|
-
@search_result.hits.first.highlights.length.
|
14
|
+
expect(@search_result.hits.first.highlights.length).to eq(1)
|
15
15
|
end
|
16
16
|
|
17
17
|
it 'should return formatted highlight fragments' do
|
18
|
-
@search_result.hits.first.highlights(:body).first.format.
|
18
|
+
expect(@search_result.hits.first.highlights(:body).first.format).to eq('And the <em>fox</em> laughed')
|
19
19
|
end
|
20
20
|
|
21
21
|
it 'should be empty for non-keyword searches' do
|
22
22
|
search_result = Sunspot.search(Post){ with :blog_id, 1 }
|
23
|
-
search_result.hits.first.highlights.
|
23
|
+
expect(search_result.hits.first.highlights).to be_empty
|
24
24
|
end
|
25
25
|
|
26
26
|
it "should process multiple keyword request on different fields with highlights correctly" do
|
27
27
|
search_results = nil
|
28
|
-
|
29
|
-
search_results = Sunspot.search(Post) do
|
28
|
+
expect do
|
29
|
+
search_results = Sunspot.search(Post) do
|
30
30
|
keywords 'Lorem ipsum', :fields => [:body] do
|
31
31
|
highlight :body
|
32
32
|
end
|
@@ -34,9 +34,9 @@ describe 'keyword highlighting' do
|
|
34
34
|
highlight :title
|
35
35
|
end
|
36
36
|
end
|
37
|
-
end.
|
38
|
-
search_results.results.length.
|
39
|
-
search_results.results.first.
|
37
|
+
end.to_not raise_error
|
38
|
+
expect(search_results.results.length).to eq(1)
|
39
|
+
expect(search_results.results.first).to eq(@posts.last)
|
40
40
|
# this one might be a Solr bug, therefore not related to Sunspot itself
|
41
41
|
# search_results.hits.first.highlights.should_not be_empty
|
42
42
|
end
|
@@ -2,33 +2,34 @@ require File.expand_path('../spec_helper', File.dirname(__FILE__))
|
|
2
2
|
|
3
3
|
describe 'indexing' do
|
4
4
|
it 'should index non-multivalued field with newlines' do
|
5
|
-
|
5
|
+
expect do
|
6
6
|
Sunspot.index!(Post.new(:title => "A\nTitle"))
|
7
|
-
end.
|
7
|
+
end.not_to raise_error
|
8
8
|
end
|
9
9
|
|
10
10
|
it 'should correctly remove by model instance' do
|
11
11
|
post = Post.new(:title => 'test post')
|
12
12
|
Sunspot.index!(post)
|
13
13
|
Sunspot.remove!(post)
|
14
|
-
Sunspot.search(Post) { with(:title, 'test post') }.results.
|
14
|
+
expect(Sunspot.search(Post) { with(:title, 'test post') }.results).to be_empty
|
15
15
|
end
|
16
16
|
|
17
17
|
it 'should correctly delete by ID' do
|
18
18
|
post = Post.new(:title => 'test post')
|
19
19
|
Sunspot.index!(post)
|
20
20
|
Sunspot.remove_by_id!(Post, post.id)
|
21
|
-
Sunspot.search(Post) { with(:title, 'test post') }.results.
|
21
|
+
expect(Sunspot.search(Post) { with(:title, 'test post') }.results).to be_empty
|
22
22
|
end
|
23
23
|
|
24
24
|
it 'removes documents by query' do
|
25
25
|
Sunspot.remove_all!
|
26
26
|
posts = [Post.new(:title => 'birds'), Post.new(:title => 'monkeys')]
|
27
27
|
Sunspot.index!(posts)
|
28
|
-
|
28
|
+
|
29
|
+
Sunspot.remove!(Post) do
|
29
30
|
with(:title, 'birds')
|
30
31
|
end
|
31
|
-
Sunspot.search(Post).
|
32
|
+
expect(Sunspot.search(Post).results.size).to eq(1)
|
32
33
|
end
|
33
34
|
|
34
35
|
|
@@ -0,0 +1,45 @@
|
|
1
|
+
require File.expand_path('../spec_helper', File.dirname(__FILE__))
|
2
|
+
|
3
|
+
describe "searching by joined fields" do
|
4
|
+
before :each do
|
5
|
+
Sunspot.remove_all!
|
6
|
+
|
7
|
+
@container1 = PhotoContainer.new(:id => 1)
|
8
|
+
@container2 = PhotoContainer.new(:id => 2).tap { |c| allow(c).to receive(:id).and_return(2) }
|
9
|
+
@container3 = PhotoContainer.new(:id => 3).tap { |c| allow(c).to receive(:id).and_return(3) }
|
10
|
+
|
11
|
+
@picture = Picture.new(:photo_container_id => @container1.id, :description => "one")
|
12
|
+
@photo1 = Photo.new(:photo_container_id => @container1.id, :description => "two")
|
13
|
+
@photo2 = Photo.new(:photo_container_id => @container2.id, :description => "three")
|
14
|
+
|
15
|
+
Sunspot.index!(@container1, @container2, @photo1, @photo2, @picture)
|
16
|
+
end
|
17
|
+
|
18
|
+
it "matches by joined fields" do
|
19
|
+
{
|
20
|
+
"one" => [],
|
21
|
+
"two" => [@container1],
|
22
|
+
"three" => [@container2]
|
23
|
+
}.each do |key, res|
|
24
|
+
results = Sunspot.search(PhotoContainer) {
|
25
|
+
fulltext(key, :fields => [:photo_description])
|
26
|
+
}.results
|
27
|
+
|
28
|
+
expect(results).to eq res
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
it "doesn't match by joined fields with the same name from other collections" do
|
33
|
+
{
|
34
|
+
"one" => [@container1],
|
35
|
+
"two" => [],
|
36
|
+
"three" => []
|
37
|
+
}.each do |key, res|
|
38
|
+
results = Sunspot.search(PhotoContainer) {
|
39
|
+
fulltext(key, :fields => [:picture_description])
|
40
|
+
}.results
|
41
|
+
|
42
|
+
expect(results).to eq res
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -16,36 +16,64 @@ describe 'keyword search' do
|
|
16
16
|
Sunspot.index!(@comment)
|
17
17
|
end
|
18
18
|
|
19
|
+
context 'edismax' do
|
20
|
+
it 'matches with wildcards' do
|
21
|
+
results = Sunspot.search(Post) { keywords '*oas*' }.results
|
22
|
+
[0,2].each { |i| expect(results).to include(@posts[i])}
|
23
|
+
[1].each { |i| expect(results).not_to include(@posts[i])}
|
24
|
+
end
|
25
|
+
|
26
|
+
it 'matches multiple keywords on different fields with wildcards using subqueries' do
|
27
|
+
results = Sunspot.search(Post) do
|
28
|
+
keywords 'insuffic*',:fields=>[:title]
|
29
|
+
keywords 'win*',:fields=>[:body]
|
30
|
+
end.results
|
31
|
+
[0].each {|i| expect(results).to include(@posts[i])}
|
32
|
+
[1,2].each {|i| expect(results).not_to include(@posts[i])}
|
33
|
+
end
|
34
|
+
|
35
|
+
it 'matches with proximity' do
|
36
|
+
results = Sunspot.search(Post) { keywords '"wind buffer"~4' }.results
|
37
|
+
[0,1].each {|i| expect(results).not_to include(@posts[i])}
|
38
|
+
[2].each {|i| expect(results).to include(@posts[i])}
|
39
|
+
end
|
40
|
+
|
41
|
+
it 'does not match if not within proximity' do
|
42
|
+
results = Sunspot.search(Post) { keywords '"wind buffer"~1' }.results
|
43
|
+
expect(results).to eq([])
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
19
47
|
it 'matches a single keyword out of a single field' do
|
20
48
|
results = Sunspot.search(Post) { keywords 'toast' }.results
|
21
|
-
[0, 2].each { |i| results.
|
22
|
-
[1].each { |i| results.
|
49
|
+
[0, 2].each { |i| expect(results).to include(@posts[i]) }
|
50
|
+
[1].each { |i| expect(results).not_to include(@posts[i]) }
|
23
51
|
end
|
24
52
|
|
25
53
|
it 'matches multiple words out of a single field' do
|
26
54
|
results = Sunspot.search(Post) { keywords 'elects toast' }.results
|
27
|
-
results.
|
55
|
+
expect(results).to eq([@posts[0]])
|
28
56
|
end
|
29
57
|
|
30
58
|
it 'matches multiple words in multiple fields' do
|
31
59
|
results = Sunspot.search(Post) { keywords 'toast wind' }.results
|
32
|
-
[0, 2].each { |i| results.
|
33
|
-
[1].each { |i| results.
|
60
|
+
[0, 2].each { |i| expect(results).to include(@posts[i]) }
|
61
|
+
[1].each { |i| expect(results).not_to include(@posts[i]) }
|
34
62
|
end
|
35
63
|
|
36
64
|
it 'matches multiple types' do
|
37
65
|
results = Sunspot.search(Post, Namespaced::Comment) do
|
38
66
|
keywords 'toast'
|
39
67
|
end.results
|
40
|
-
[@posts[0], @posts[2], @comment].each { |obj| results.
|
41
|
-
results.
|
68
|
+
[@posts[0], @posts[2], @comment].each { |obj| expect(results).to include(obj) }
|
69
|
+
expect(results).not_to include(@posts[1])
|
42
70
|
end
|
43
71
|
|
44
72
|
it 'matches keywords from only the fields specified' do
|
45
73
|
results = Sunspot.search(Post) do
|
46
74
|
keywords 'moron', :fields => [:title]
|
47
75
|
end.results
|
48
|
-
results.
|
76
|
+
expect(results).to eq([@posts[1]])
|
49
77
|
end
|
50
78
|
|
51
79
|
it 'matches multiple keywords on different fields using subqueries' do
|
@@ -53,13 +81,13 @@ describe 'keyword search' do
|
|
53
81
|
keywords 'moron', :fields => [:title]
|
54
82
|
keywords 'wind', :fields => [:body]
|
55
83
|
end
|
56
|
-
search.results.
|
84
|
+
expect(search.results).to eq([])
|
57
85
|
|
58
86
|
search = Sunspot.search(Post) do
|
59
87
|
keywords 'moron', :fields => [:title]
|
60
88
|
keywords 'buffer', :fields => [:body]
|
61
89
|
end
|
62
|
-
search.results.
|
90
|
+
expect(search.results).to eq([@posts[1]])
|
63
91
|
end
|
64
92
|
|
65
93
|
it 'matches multiple keywords with escaped characters' do
|
@@ -67,7 +95,7 @@ describe 'keyword search' do
|
|
67
95
|
keywords 'spirit', :fields => [:title]
|
68
96
|
keywords 'host\'s', :fields => [:body]
|
69
97
|
end
|
70
|
-
search.results.
|
98
|
+
expect(search.results).to eq([@posts[2]])
|
71
99
|
end
|
72
100
|
|
73
101
|
it 'matches multiple keywords with phrase-based search' do
|
@@ -76,7 +104,7 @@ describe 'keyword search' do
|
|
76
104
|
keywords '"interpret the buffer"', :fields => [:body]
|
77
105
|
keywords '"does the"', :fields => [:body]
|
78
106
|
end
|
79
|
-
search.results.
|
107
|
+
expect(search.results).to eq([@posts[2]])
|
80
108
|
end
|
81
109
|
|
82
110
|
it 'matches multiple keywords different options' do
|
@@ -84,7 +112,7 @@ describe 'keyword search' do
|
|
84
112
|
keywords 'insufficient nonexistent', :fields => [:title], :minimum_match => 1
|
85
113
|
keywords 'wind does', :fields => [:body], :minimum_match => 2
|
86
114
|
end
|
87
|
-
search.results.
|
115
|
+
expect(search.results).to eq([@posts[0]])
|
88
116
|
end
|
89
117
|
end
|
90
118
|
|
@@ -97,9 +125,10 @@ describe 'keyword search' do
|
|
97
125
|
|
98
126
|
it 'should assign a higher score to the result matching the higher-boosted field' do
|
99
127
|
search = Sunspot.search(Post) { keywords 'rhinoceros' }
|
100
|
-
search.hits.map { |hit| hit.primary_key }.
|
128
|
+
expect(search.hits.map { |hit| hit.primary_key }).to eq(
|
101
129
|
@posts.map { |post| post.id.to_s }
|
102
|
-
|
130
|
+
)
|
131
|
+
expect(search.hits.first.score).to be > search.hits.last.score
|
103
132
|
end
|
104
133
|
end
|
105
134
|
|
@@ -114,9 +143,10 @@ describe 'keyword search' do
|
|
114
143
|
|
115
144
|
it 'should assign a higher score to the higher-boosted document' do
|
116
145
|
search = Sunspot.search(Post) { keywords 'test' }
|
117
|
-
search.hits.map { |hit| hit.primary_key }.
|
146
|
+
expect(search.hits.map { |hit| hit.primary_key }).to eq(
|
118
147
|
@posts.map { |post| post.id.to_s }
|
119
|
-
|
148
|
+
)
|
149
|
+
expect(search.hits.first.score).to be > search.hits.last.score
|
120
150
|
end
|
121
151
|
end
|
122
152
|
|
@@ -136,8 +166,8 @@ describe 'keyword search' do
|
|
136
166
|
phrase_fields :body => 2.0
|
137
167
|
end
|
138
168
|
end.hits
|
139
|
-
hits.first.instance.
|
140
|
-
hits.first.score.
|
169
|
+
expect(hits.first.instance).to eq(@comments.first)
|
170
|
+
expect(hits.first.score).to be > hits.last.score
|
141
171
|
end
|
142
172
|
|
143
173
|
it 'assigns a higher score to documents in which the search terms appear in a boosted field' do
|
@@ -146,8 +176,8 @@ describe 'keyword search' do
|
|
146
176
|
fields :body => 2.0, :author_name => 0.75
|
147
177
|
end
|
148
178
|
end.hits
|
149
|
-
hits.first.instance.
|
150
|
-
hits.first.score.
|
179
|
+
expect(hits.first.instance).to eq(@comments.first)
|
180
|
+
expect(hits.first.score).to be > hits.last.score
|
151
181
|
end
|
152
182
|
|
153
183
|
it 'assigns a higher score to documents in which the search terms appear in a higher boosted phrase field' do
|
@@ -156,8 +186,8 @@ describe 'keyword search' do
|
|
156
186
|
phrase_fields :body => 2.0, :author_name => 0.75
|
157
187
|
end
|
158
188
|
end.hits
|
159
|
-
hits.first.instance.
|
160
|
-
hits.first.score.
|
189
|
+
expect(hits.first.instance).to eq(@comments.first)
|
190
|
+
expect(hits.first.score).to be > hits.last.score
|
161
191
|
end
|
162
192
|
end
|
163
193
|
|
@@ -182,8 +212,8 @@ describe 'keyword search' do
|
|
182
212
|
end
|
183
213
|
query.without(@posts[1])
|
184
214
|
end
|
185
|
-
search.results.
|
186
|
-
search.hits[0].score.
|
215
|
+
expect(search.results).to eq([@posts[0], @posts[2]])
|
216
|
+
expect(search.hits[0].score).to be > search.hits[1].score
|
187
217
|
end
|
188
218
|
|
189
219
|
it 'should assign scores in order of multiple boost query match' do
|
@@ -193,9 +223,9 @@ describe 'keyword search' do
|
|
193
223
|
boost(1.5) { with(:average_rating).greater_than(3.0) }
|
194
224
|
end
|
195
225
|
end
|
196
|
-
search.results.
|
197
|
-
search.hits[0].score.
|
198
|
-
search.hits[1].score.
|
226
|
+
expect(search.results).to eq(@posts)
|
227
|
+
expect(search.hits[0].score).to be > search.hits[1].score
|
228
|
+
expect(search.hits[1].score).to be > search.hits[2].score
|
199
229
|
end
|
200
230
|
end
|
201
231
|
|
@@ -213,11 +243,11 @@ describe 'keyword search' do
|
|
213
243
|
end
|
214
244
|
|
215
245
|
it 'should match documents that contain the minimum_match number of search terms' do
|
216
|
-
@search.results.
|
246
|
+
expect(@search.results).to include(@posts[0])
|
217
247
|
end
|
218
248
|
|
219
249
|
it 'should not match documents that do not contain the minimum_match number of search terms' do
|
220
|
-
@search.results.
|
250
|
+
expect(@search.results).not_to include(@posts[1])
|
221
251
|
end
|
222
252
|
end
|
223
253
|
|
@@ -236,15 +266,15 @@ describe 'keyword search' do
|
|
236
266
|
end
|
237
267
|
|
238
268
|
it 'should match exact phrase' do
|
239
|
-
@search.results.
|
269
|
+
expect(@search.results).to include(@posts[0])
|
240
270
|
end
|
241
271
|
|
242
272
|
it 'should match phrase divided by query phrase slop terms' do
|
243
|
-
@search.results.
|
273
|
+
expect(@search.results).to include(@posts[1])
|
244
274
|
end
|
245
275
|
|
246
276
|
it 'should not match phrase divided by more than query phrase slop terms' do
|
247
|
-
@search.results.
|
277
|
+
expect(@search.results).not_to include(@posts[2])
|
248
278
|
end
|
249
279
|
end
|
250
280
|
|
@@ -270,15 +300,15 @@ describe 'keyword search' do
|
|
270
300
|
end
|
271
301
|
|
272
302
|
it 'should give phrase field boost to exact match' do
|
273
|
-
@sorted_hits[0].score.
|
303
|
+
expect(@sorted_hits[0].score).to be > @sorted_hits[1].score
|
274
304
|
end
|
275
305
|
|
276
306
|
it 'should give phrase field boost to match within slop' do
|
277
|
-
@sorted_hits[2].score.
|
307
|
+
expect(@sorted_hits[2].score).to be > @sorted_hits[3].score
|
278
308
|
end
|
279
309
|
|
280
310
|
it 'should not give phrase field boost to match beyond slop' do
|
281
|
-
@sorted_hits[4].score.
|
311
|
+
expect(@sorted_hits[4].score).to eq(@sorted_hits[5].score)
|
282
312
|
end
|
283
313
|
end
|
284
314
|
|
@@ -288,8 +318,8 @@ describe 'keyword search' do
|
|
288
318
|
end
|
289
319
|
|
290
320
|
after :each do
|
291
|
-
@search.results.
|
292
|
-
@search.hits.first.score.
|
321
|
+
expect(@search.results).to eq(@posts)
|
322
|
+
expect(@search.hits.first.score).to be > @search.hits.last.score
|
293
323
|
end
|
294
324
|
|
295
325
|
it 'boosts via function query with float' do
|