ansr 0.0.1 → 0.0.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (83) hide show
  1. checksums.yaml +15 -0
  2. data/README.md +35 -0
  3. data/ansr.gemspec +2 -1
  4. data/ansr_blacklight/Gemfile +3 -0
  5. data/ansr_blacklight/README.md +37 -0
  6. data/ansr_blacklight/ansr_blacklight.gemspec +28 -0
  7. data/ansr_blacklight/lib/ansr_blacklight.rb +57 -0
  8. data/ansr_blacklight/lib/ansr_blacklight/arel.rb +6 -0
  9. data/ansr_blacklight/lib/ansr_blacklight/arel/big_table.rb +57 -0
  10. data/ansr_blacklight/lib/ansr_blacklight/arel/visitors.rb +6 -0
  11. data/ansr_blacklight/lib/ansr_blacklight/arel/visitors/query_builder.rb +217 -0
  12. data/ansr_blacklight/lib/ansr_blacklight/arel/visitors/to_no_sql.rb +14 -0
  13. data/ansr_blacklight/lib/ansr_blacklight/base.rb +21 -0
  14. data/ansr_blacklight/lib/ansr_blacklight/connection_adapters/no_sql_adapter.rb +38 -0
  15. data/ansr_blacklight/lib/ansr_blacklight/model/querying.rb +29 -0
  16. data/ansr_blacklight/lib/ansr_blacklight/relation.rb +50 -0
  17. data/ansr_blacklight/lib/ansr_blacklight/relation/solr_projection_methods.rb +55 -0
  18. data/ansr_blacklight/lib/ansr_blacklight/request_builders.rb +141 -0
  19. data/ansr_blacklight/lib/ansr_blacklight/solr.rb +4 -0
  20. data/ansr_blacklight/lib/ansr_blacklight/solr/request.rb +46 -0
  21. data/ansr_blacklight/lib/ansr_blacklight/solr/response.rb +94 -0
  22. data/ansr_blacklight/lib/ansr_blacklight/solr/response/group.rb +32 -0
  23. data/ansr_blacklight/lib/ansr_blacklight/solr/response/group_response.rb +50 -0
  24. data/ansr_blacklight/lib/ansr_blacklight/solr/response/more_like_this.rb +14 -0
  25. data/ansr_blacklight/lib/ansr_blacklight/solr/response/pagination_methods.rb +35 -0
  26. data/ansr_blacklight/lib/ansr_blacklight/solr/response/spelling.rb +92 -0
  27. data/ansr_blacklight/spec/fixtures/config.yml +0 -0
  28. data/ansr_blacklight/spec/lib/loaded_relation_spec.rb +223 -0
  29. data/ansr_blacklight/spec/lib/queryable_relation_spec.rb +133 -0
  30. data/ansr_blacklight/spec/lib/relation/faceting_spec.rb +475 -0
  31. data/ansr_blacklight/spec/lib/relation/grouping_spec.rb +159 -0
  32. data/ansr_blacklight/spec/spec_helper.rb +72 -0
  33. data/ansr_dpla/Gemfile +3 -0
  34. data/ansr_dpla/Gemfile.lock +138 -0
  35. data/ansr_dpla/README.md +2 -2
  36. data/ansr_dpla/ansr_dpla.gemspec +2 -2
  37. data/ansr_dpla/lib/ansr_dpla.rb +3 -0
  38. data/ansr_dpla/lib/ansr_dpla/api.rb +3 -3
  39. data/ansr_dpla/lib/ansr_dpla/arel.rb +1 -2
  40. data/ansr_dpla/lib/ansr_dpla/arel/big_table.rb +4 -0
  41. data/ansr_dpla/lib/ansr_dpla/arel/visitors.rb +6 -0
  42. data/ansr_dpla/lib/ansr_dpla/arel/visitors/query_builder.rb +188 -0
  43. data/ansr_dpla/lib/ansr_dpla/arel/visitors/to_no_sql.rb +9 -0
  44. data/ansr_dpla/lib/ansr_dpla/connection_adapters/no_sql_adapter.rb +58 -0
  45. data/ansr_dpla/lib/ansr_dpla/model/base.rb +7 -0
  46. data/ansr_dpla/lib/ansr_dpla/model/querying.rb +6 -10
  47. data/ansr_dpla/lib/ansr_dpla/relation.rb +61 -0
  48. data/ansr_dpla/lib/ansr_dpla/request.rb +5 -0
  49. data/ansr_dpla/spec/lib/api_spec.rb +8 -5
  50. data/ansr_dpla/spec/lib/item_spec.rb +2 -2
  51. data/ansr_dpla/spec/lib/relation/facet_spec.rb +27 -19
  52. data/ansr_dpla/spec/lib/relation/select_spec.rb +10 -8
  53. data/ansr_dpla/spec/lib/relation/where_spec.rb +1 -1
  54. data/ansr_dpla/spec/lib/relation_spec.rb +31 -29
  55. data/ansr_dpla/test/system.rb +4 -2
  56. data/lib/ansr.rb +7 -0
  57. data/lib/ansr/arel.rb +3 -0
  58. data/lib/ansr/arel/big_table.rb +43 -3
  59. data/lib/ansr/arel/configured_field.rb +19 -0
  60. data/lib/ansr/arel/nodes.rb +41 -0
  61. data/lib/ansr/arel/visitors.rb +7 -0
  62. data/lib/ansr/arel/visitors/context.rb +13 -0
  63. data/lib/ansr/arel/visitors/query_builder.rb +47 -0
  64. data/lib/ansr/arel/visitors/to_no_sql.rb +41 -0
  65. data/lib/ansr/base.rb +29 -1
  66. data/lib/ansr/configurable.rb +6 -12
  67. data/lib/ansr/connection_adapters.rb +5 -0
  68. data/lib/ansr/connection_adapters/no_sql_adapter.rb +80 -0
  69. data/lib/ansr/dummy_associations.rb +105 -0
  70. data/lib/ansr/facets.rb +103 -0
  71. data/lib/ansr/model.rb +17 -107
  72. data/lib/ansr/model/connection_handler.rb +6 -0
  73. data/lib/ansr/relation.rb +40 -23
  74. data/lib/ansr/relation/group.rb +31 -0
  75. data/lib/ansr/relation/predicate_builder.rb +106 -0
  76. data/lib/ansr/relation/query_methods.rb +192 -45
  77. data/lib/ansr/sanitization.rb +5 -18
  78. data/lib/ansr/utils.rb +89 -0
  79. data/lib/ansr/version.rb +1 -1
  80. metadata +73 -25
  81. data/ansr_dpla/lib/ansr_dpla/arel/connection.rb +0 -81
  82. data/ansr_dpla/lib/ansr_dpla/arel/query_builder.rb +0 -131
  83. data/lib/ansr/model/connection.rb +0 -103
@@ -0,0 +1,32 @@
1
+ class Ansr::Blacklight::Solr::Response::Group < Ansr::Group
2
+
3
+ include Ansr::Blacklight::Solr::Response::PaginationMethods
4
+
5
+ attr_reader :response
6
+
7
+ def initialize group_key, model, group, response
8
+ super(group_key, model, group)
9
+ @response = response
10
+ end
11
+
12
+ def doclist
13
+ group[:doclist]
14
+ end
15
+
16
+ # short cut to response['numFound']
17
+ def total
18
+ doclist[:numFound].to_s.to_i
19
+ end
20
+
21
+ def start
22
+ doclist[:start].to_s.to_i
23
+ end
24
+
25
+ def docs
26
+ doclist[:docs].map {|doc| model.new(doc)} #TODO do we need to have the solrResponse in the item?
27
+ end
28
+
29
+ def field
30
+ response.group_field
31
+ end
32
+ end
@@ -0,0 +1,50 @@
1
+ class Ansr::Blacklight::Solr::Response::GroupResponse
2
+
3
+ include Ansr::Blacklight::Solr::Response::PaginationMethods
4
+
5
+ attr_reader :key, :model, :group, :response
6
+
7
+ def initialize key, model, group, response
8
+ @key = key
9
+ @model = model
10
+ @group = group
11
+ @response = response
12
+ end
13
+
14
+ alias_method :group_field, :key
15
+
16
+ def groups
17
+ @groups ||= group["groups"].map do |g|
18
+ Ansr::Blacklight::Solr::Response::Group.new({key => g[:groupValue]}, model, g, self)
19
+ end
20
+ end
21
+
22
+ def group_limit
23
+ params.fetch(:'group.limit', 1).to_s.to_i
24
+ end
25
+
26
+ def total
27
+ # ngroups is only available in Solr 4.1+
28
+ # fall back on the number of facet items for that field?
29
+ (group["ngroups"] || (response.facet_by_field_name(key) || []).length).to_s.to_i
30
+ end
31
+
32
+ def start
33
+ params[:start].to_s.to_i
34
+ end
35
+
36
+ def method_missing meth, *args, &block
37
+
38
+ if response.respond_to? meth
39
+ response.send(meth, *args, &block)
40
+ else
41
+ super
42
+ end
43
+
44
+ end
45
+
46
+ def respond_to? meth
47
+ response.respond_to?(meth) || super
48
+ end
49
+
50
+ end
@@ -0,0 +1,14 @@
1
+ module Ansr::Blacklight::Solr::Response::MoreLikeThis
2
+ def more_like document
3
+ mlt = more_like_this[document.id]
4
+ return [] unless mlt and mlt['docs']
5
+
6
+ mlt['docs']
7
+ end
8
+
9
+ def more_like_this
10
+ return {} unless self[:moreLikeThis]
11
+
12
+ self[:moreLikeThis]
13
+ end
14
+ end
@@ -0,0 +1,35 @@
1
+ require 'kaminari'
2
+ module Ansr::Blacklight::Solr::Response::PaginationMethods
3
+
4
+ include Kaminari::PageScopeMethods
5
+ include Kaminari::ConfigurationMethods::ClassMethods
6
+
7
+ def limit_value #:nodoc:
8
+ rows
9
+ end
10
+
11
+ def offset_value #:nodoc:
12
+ start
13
+ end
14
+
15
+ def total_count #:nodoc:
16
+ total
17
+ end
18
+
19
+ def model_name
20
+ if !docs.empty? and docs.first.respond_to? :model_name
21
+ docs.first.model_name
22
+ end
23
+ end
24
+
25
+ ## Methods in kaminari master that we'd like to use today.
26
+ # Next page number in the collection
27
+ def next_page
28
+ current_page + 1 unless last_page?
29
+ end
30
+
31
+ # Previous page number in the collection
32
+ def prev_page
33
+ current_page - 1 unless first_page?
34
+ end
35
+ end
@@ -0,0 +1,92 @@
1
+ # A mixin for making access to the spellcheck component data easy.
2
+ #
3
+ # response.spelling.words
4
+ #
5
+ module Ansr::Blacklight::Solr::Response::Spelling
6
+
7
+ def spelling
8
+ @spelling ||= Base.new(self)
9
+ end
10
+
11
+ class Base
12
+
13
+ attr :response
14
+
15
+ def initialize(response)
16
+ @response = response
17
+ end
18
+
19
+ # returns an array of spelling suggestion for specific query words,
20
+ # as provided in the solr response. Only includes words with higher
21
+ # frequency of occurrence than word in original query.
22
+ # can't do a full query suggestion because we only get info for each word;
23
+ # combination of words may not have results.
24
+ # Thanks to Naomi Dushay!
25
+ def words
26
+ @words ||= (
27
+ word_suggestions = []
28
+ spellcheck = self.response[:spellcheck]
29
+ if spellcheck && spellcheck[:suggestions]
30
+ suggestions = spellcheck[:suggestions]
31
+ unless suggestions.nil?
32
+ # suggestions is an array:
33
+ # (query term)
34
+ # (hash of term info and term suggestion)
35
+ # ...
36
+ # (query term)
37
+ # (hash of term info and term suggestion)
38
+ # 'correctlySpelled'
39
+ # true/false
40
+ # collation
41
+ # (suggestion for collation)
42
+ if suggestions.index("correctlySpelled") #if extended results
43
+ i_stop = suggestions.index("correctlySpelled")
44
+ elsif suggestions.index("collation")
45
+ i_stop = suggestions.index("collation")
46
+ else
47
+ i_stop = suggestions.length
48
+ end
49
+ # step through array in 2s to get info for each term
50
+ 0.step(i_stop-1, 2) do |i|
51
+ term = suggestions[i]
52
+ term_info = suggestions[i+1]
53
+ # term_info is a hash:
54
+ # numFound =>
55
+ # startOffset =>
56
+ # endOffset =>
57
+ # origFreq =>
58
+ # suggestion => [{ frequency =>, word => }] # for extended results
59
+ # suggestion => ['word'] # for non-extended results
60
+ origFreq = term_info['origFreq']
61
+ if suggestions.index("correctlySpelled")
62
+ word_suggestions << term_info['suggestion'].map do |suggestion|
63
+ suggestion['word'] if suggestion['freq'] > origFreq
64
+ end
65
+ else
66
+ # only extended suggestions have frequency so we just return all suggestions
67
+ word_suggestions << term_info['suggestion']
68
+ end
69
+ end
70
+ end
71
+ end
72
+ word_suggestions.flatten.compact.uniq
73
+ )
74
+ end
75
+
76
+ def collation
77
+ # FIXME: DRY up with words
78
+ spellcheck = self.response[:spellcheck]
79
+ if spellcheck && spellcheck[:suggestions]
80
+ suggestions = spellcheck[:suggestions]
81
+ unless suggestions.nil?
82
+ if suggestions.index("collation")
83
+ suggestions[suggestions.index("collation") + 1]
84
+ end
85
+ end
86
+ end
87
+ end
88
+
89
+ end
90
+
91
+ end
92
+
@@ -0,0 +1,223 @@
1
+ require 'spec_helper'
2
+
3
+ describe Ansr::Blacklight::Relation do
4
+
5
+ def create_response
6
+ raw_response = eval(mock_query_response)
7
+ Blacklight::SolrResponse.new(raw_response, raw_response['params'])
8
+ end
9
+
10
+ def stub_solr
11
+ @solr ||= double('Solr')
12
+ @solr.stub(:send_and_receive).and_return(mock_query_response)
13
+ @solr
14
+ end
15
+
16
+ before do
17
+ Object.const_set('LoadTestModel', Class.new(TestModel))
18
+ LoadTestModel.solr = stub_solr
19
+ LoadTestModel.solr = stub_solr
20
+ @relation = Ansr::Blacklight::Relation.new(LoadTestModel, Ansr::Arel::BigTable.new(LoadTestModel))
21
+ @relation.load
22
+ end
23
+ after do
24
+ Object.send(:remove_const, :LoadTestModel)
25
+ end
26
+ subject { @relation }
27
+
28
+ let(:r) { subject }
29
+
30
+ describe 'pagination' do
31
+ it 'should have accurate pagination numbers' do
32
+ expect(r.limit_value).to eq 11
33
+ expect(r.count).to eq 26
34
+ expect(r.offset_value).to eq 0
35
+ end
36
+
37
+ it "should provide kaminari pagination helpers" do
38
+ expect(r.total_count).to eq(r.count)
39
+ expect(r.next_page).to eq(r.current_page + 1)
40
+ expect(r.prev_page).to eq(nil)
41
+ if Kaminari.config.respond_to? :max_pages
42
+ expect(r.max_pages).to be_nil
43
+ end
44
+ expect(r).to be_a_kind_of Kaminari::PageScopeMethods
45
+ end
46
+ end
47
+
48
+ it 'should create a valid response class' do
49
+ expect(r).to respond_to(:response)
50
+ expect(r.docs).to have(11).docs
51
+ expect(r.params[:echoParams]).to eq 'EXPLICIT'
52
+
53
+ expect(r).to respond_to(:facets)
54
+ end
55
+
56
+ it 'should provide facet helpers' do
57
+ r.facets.size.should == 2
58
+
59
+ field_names = r.facets.collect{|facet|facet.name}
60
+ field_names.include?('cat').should == true
61
+ field_names.include?('manu').should == true
62
+
63
+ first_facet = r.facets.select { |x| x.name == 'cat'}.first
64
+ first_facet.name.should == 'cat'
65
+
66
+ first_facet.items.size.should == 10
67
+
68
+ expected = "electronics - 14, memory - 3, card - 2, connector - 2, drive - 2, graphics - 2, hard - 2, monitor - 2, search - 2, software - 2"
69
+ received = first_facet.items.collect do |item|
70
+ item.value + ' - ' + item.hits.to_s
71
+ end.join(', ')
72
+
73
+ expect(received).to eq expected
74
+
75
+ r.facets.each do |facet|
76
+ expect(facet).to respond_to :name
77
+ expect(facet).to respond_to :sort
78
+ expect(facet).to respond_to :offset
79
+ expect(facet).to respond_to :limit
80
+ facet.items.each do |item|
81
+ expect(item).to respond_to :value
82
+ expect(item).to respond_to :hits
83
+ end
84
+ end
85
+ end
86
+
87
+ it "should provide a model name helper" do
88
+ expect(r.model_name).to eq LoadTestModel.name
89
+ end
90
+
91
+ describe "FacetItem" do
92
+ it "should work with a field,value tuple" do
93
+ item = Ansr::Facets::FacetItem.new('value', 15)
94
+ puts item.class.name
95
+ item.value.should == 'value'
96
+ item.hits.should == 15
97
+ end
98
+
99
+ it "should work with a field,value + hash triple" do
100
+ item = Ansr::Facets::FacetItem.new('value', 15, :a => 1, :value => 'ignored')
101
+ item.value.should == 'value'
102
+ item.hits.should == 15
103
+ item.a.should == 1
104
+ end
105
+
106
+ it "should work like an openstruct" do
107
+ item = Ansr::Facets::FacetItem.new(:value => 'value', :hits => 15)
108
+
109
+ item.hits.should == 15
110
+ item.value.should == 'value'
111
+ item.should be_a_kind_of(OpenStruct)
112
+ end
113
+
114
+ it "should provide a label accessor" do
115
+ item = Ansr::Facets::FacetItem.new('value', :hits => 15)
116
+ item.label.should == 'value'
117
+ end
118
+
119
+ it "should use a provided label" do
120
+ item = Ansr::Facets::FacetItem.new('value', 15, :label => 'custom label')
121
+ item.label.should == 'custom label'
122
+
123
+ end
124
+
125
+ end
126
+
127
+ it 'should return the correct value when calling facet_by_field_name' do
128
+ r = create_response
129
+ facet = r.facet_by_field_name('cat')
130
+ facet.name.should == 'cat'
131
+ end
132
+
133
+ it 'should provide the responseHeader params' do
134
+ raw_response = eval(mock_query_response)
135
+ raw_response['responseHeader']['params']['test'] = :test
136
+ r = Blacklight::SolrResponse.new(raw_response, raw_response['params'])
137
+ r.params['test'].should == :test
138
+ end
139
+
140
+ it 'should provide the solr-returned params and "rows" should be 11' do
141
+ raw_response = eval(mock_query_response)
142
+ r = Blacklight::SolrResponse.new(raw_response, {})
143
+ r.params[:rows].to_s.should == '11'
144
+ end
145
+
146
+ it 'should provide the ruby request params if responseHeader["params"] does not exist' do
147
+ raw_response = eval(mock_query_response)
148
+ raw_response.delete 'responseHeader'
149
+ r = Blacklight::SolrResponse.new(raw_response, :rows => 999)
150
+ r.params[:rows].to_s.should == '999'
151
+ end
152
+
153
+ it 'should provide spelling suggestions for regular spellcheck results' do
154
+ raw_response = eval(mock_response_with_spellcheck)
155
+ r = Blacklight::SolrResponse.new(raw_response, {})
156
+ r.spelling.words.should include("dell")
157
+ r.spelling.words.should include("ultrasharp")
158
+ end
159
+
160
+ it 'should provide spelling suggestions for extended spellcheck results' do
161
+ raw_response = eval(mock_response_with_spellcheck_extended)
162
+ r = Blacklight::SolrResponse.new(raw_response, {})
163
+ r.spelling.words.should include("dell")
164
+ r.spelling.words.should include("ultrasharp")
165
+ end
166
+
167
+ it 'should provide no spelling suggestions when extended results and suggestion frequency is the same as original query frequency' do
168
+ raw_response = eval(mock_response_with_spellcheck_same_frequency)
169
+ r = Blacklight::SolrResponse.new(raw_response, {})
170
+ r.spelling.words.should == []
171
+ end
172
+
173
+ it 'should provide spelling suggestions for a regular spellcheck results with a collation' do
174
+ raw_response = eval(mock_response_with_spellcheck_collation)
175
+ r = Blacklight::SolrResponse.new(raw_response, {})
176
+ r.spelling.words.should include("dell")
177
+ r.spelling.words.should include("ultrasharp")
178
+ end
179
+
180
+ it 'should provide spelling suggestion collation' do
181
+ raw_response = eval(mock_response_with_spellcheck_collation)
182
+ r = Blacklight::SolrResponse.new(raw_response, {})
183
+ r.spelling.collation.should == 'dell ultrasharp'
184
+ end
185
+
186
+ it "should provide MoreLikeThis suggestions" do
187
+ raw_response = eval(mock_response_with_more_like_this)
188
+ r = Blacklight::SolrResponse.new(raw_response, {})
189
+ r.more_like(double(:id => '79930185')).should have(2).items
190
+ end
191
+
192
+ it "should be empty when the response has no results" do
193
+ r = Blacklight::SolrResponse.new({}, {})
194
+ r.stub(:total => 0)
195
+ expect(r).to be_empty
196
+ end
197
+
198
+ def mock_query_response
199
+ %({'responseHeader'=>{'status'=>0,'QTime'=>5,'params'=>{'facet.limit'=>'10','wt'=>'ruby','rows'=>'11','facet'=>'true','facet.field'=>['cat','manu'],'echoParams'=>'EXPLICIT','q'=>'*:*','facet.sort'=>'true'}},'response'=>{'numFound'=>26,'start'=>0,'docs'=>[{'id'=>'SP2514N','inStock'=>true,'manu'=>'Samsung Electronics Co. Ltd.','name'=>'Samsung SpinPoint P120 SP2514N - hard drive - 250 GB - ATA-133','popularity'=>6,'price'=>92.0,'sku'=>'SP2514N','timestamp'=>'2009-03-20T14:42:49.795Z','cat'=>['electronics','hard drive'],'spell'=>['Samsung SpinPoint P120 SP2514N - hard drive - 250 GB - ATA-133'],'features'=>['7200RPM, 8MB cache, IDE Ultra ATA-133','NoiseGuard, SilentSeek technology, Fluid Dynamic Bearing (FDB) motor']},{'id'=>'6H500F0','inStock'=>true,'manu'=>'Maxtor Corp.','name'=>'Maxtor DiamondMax 11 - hard drive - 500 GB - SATA-300','popularity'=>6,'price'=>350.0,'sku'=>'6H500F0','timestamp'=>'2009-03-20T14:42:49.877Z','cat'=>['electronics','hard drive'],'spell'=>['Maxtor DiamondMax 11 - hard drive - 500 GB - SATA-300'],'features'=>['SATA 3.0Gb/s, NCQ','8.5ms seek','16MB cache']},{'id'=>'F8V7067-APL-KIT','inStock'=>false,'manu'=>'Belkin','name'=>'Belkin Mobile Power Cord for iPod w/ Dock','popularity'=>1,'price'=>19.95,'sku'=>'F8V7067-APL-KIT','timestamp'=>'2009-03-20T14:42:49.937Z','weight'=>4.0,'cat'=>['electronics','connector'],'spell'=>['Belkin Mobile Power Cord for iPod w/ Dock'],'features'=>['car power adapter, white']},{'id'=>'IW-02','inStock'=>false,'manu'=>'Belkin','name'=>'iPod & iPod Mini USB 2.0 Cable','popularity'=>1,'price'=>11.5,'sku'=>'IW-02','timestamp'=>'2009-03-20T14:42:49.944Z','weight'=>2.0,'cat'=>['electronics','connector'],'spell'=>['iPod & iPod Mini USB 2.0 Cable'],'features'=>['car power adapter for iPod, white']},{'id'=>'MA147LL/A','inStock'=>true,'includes'=>'earbud headphones, USB cable','manu'=>'Apple Computer Inc.','name'=>'Apple 60 GB iPod with Video Playback Black','popularity'=>10,'price'=>399.0,'sku'=>'MA147LL/A','timestamp'=>'2009-03-20T14:42:49.962Z','weight'=>5.5,'cat'=>['electronics','music'],'spell'=>['Apple 60 GB iPod with Video Playback Black'],'features'=>['iTunes, Podcasts, Audiobooks','Stores up to 15,000 songs, 25,000 photos, or 150 hours of video','2.5-inch, 320x240 color TFT LCD display with LED backlight','Up to 20 hours of battery life','Plays AAC, MP3, WAV, AIFF, Audible, Apple Lossless, H.264 video','Notes, Calendar, Phone book, Hold button, Date display, Photo wallet, Built-in games, JPEG photo playback, Upgradeable firmware, USB 2.0 compatibility, Playback speed control, Rechargeable capability, Battery level indication']},{'id'=>'TWINX2048-3200PRO','inStock'=>true,'manu'=>'Corsair Microsystems Inc.','name'=>'CORSAIR XMS 2GB (2 x 1GB) 184-Pin DDR SDRAM Unbuffered DDR 400 (PC 3200) Dual Channel Kit System Memory - Retail','popularity'=>5,'price'=>185.0,'sku'=>'TWINX2048-3200PRO','timestamp'=>'2009-03-20T14:42:49.99Z','cat'=>['electronics','memory'],'spell'=>['CORSAIR XMS 2GB (2 x 1GB) 184-Pin DDR SDRAM Unbuffered DDR 400 (PC 3200) Dual Channel Kit System Memory - Retail'],'features'=>['CAS latency 2, 2-3-3-6 timing, 2.75v, unbuffered, heat-spreader']},{'id'=>'VS1GB400C3','inStock'=>true,'manu'=>'Corsair Microsystems Inc.','name'=>'CORSAIR ValueSelect 1GB 184-Pin DDR SDRAM Unbuffered DDR 400 (PC 3200) System Memory - Retail','popularity'=>7,'price'=>74.99,'sku'=>'VS1GB400C3','timestamp'=>'2009-03-20T14:42:50Z','cat'=>['electronics','memory'],'spell'=>['CORSAIR ValueSelect 1GB 184-Pin DDR SDRAM Unbuffered DDR 400 (PC 3200) System Memory - Retail']},{'id'=>'VDBDB1A16','inStock'=>true,'manu'=>'A-DATA Technology Inc.','name'=>'A-DATA V-Series 1GB 184-Pin DDR SDRAM Unbuffered DDR 400 (PC 3200) System Memory - OEM','popularity'=>5,'sku'=>'VDBDB1A16','timestamp'=>'2009-03-20T14:42:50.004Z','cat'=>['electronics','memory'],'spell'=>['A-DATA V-Series 1GB 184-Pin DDR SDRAM Unbuffered DDR 400 (PC 3200) System Memory - OEM'],'features'=>['CAS latency 3, 2.7v']},{'id'=>'3007WFP','inStock'=>true,'includes'=>'USB cable','manu'=>'Dell, Inc.','name'=>'Dell Widescreen UltraSharp 3007WFP','popularity'=>6,'price'=>2199.0,'sku'=>'3007WFP','timestamp'=>'2009-03-20T14:42:50.017Z','weight'=>401.6,'cat'=>['electronics','monitor'],'spell'=>['Dell Widescreen UltraSharp 3007WFP'],'features'=>['30" TFT active matrix LCD, 2560 x 1600, .25mm dot pitch, 700:1 contrast']},{'id'=>'VA902B','inStock'=>true,'manu'=>'ViewSonic Corp.','name'=>'ViewSonic VA902B - flat panel display - TFT - 19"','popularity'=>6,'price'=>279.95,'sku'=>'VA902B','timestamp'=>'2009-03-20T14:42:50.034Z','weight'=>190.4,'cat'=>['electronics','monitor'],'spell'=>['ViewSonic VA902B - flat panel display - TFT - 19"'],'features'=>['19" TFT active matrix LCD, 8ms response time, 1280 x 1024 native resolution']},{'id'=>'0579B002','inStock'=>true,'manu'=>'Canon Inc.','name'=>'Canon PIXMA MP500 All-In-One Photo Printer','popularity'=>6,'price'=>179.99,'sku'=>'0579B002','timestamp'=>'2009-03-20T14:42:50.062Z','weight'=>352.0,'cat'=>['electronics','multifunction printer','printer','scanner','copier'],'spell'=>['Canon PIXMA MP500 All-In-One Photo Printer'],'features'=>['Multifunction ink-jet color photo printer','Flatbed scanner, optical scan resolution of 1,200 x 2,400 dpi','2.5" color LCD preview screen','Duplex Copying','Printing speed up to 29ppm black, 19ppm color','Hi-Speed USB','memory card: CompactFlash, Micro Drive, SmartMedia, Memory Stick, Memory Stick Pro, SD Card, and MultiMediaCard']}]},'facet_counts'=>{'facet_queries'=>{},'facet_fields'=>{'cat'=>['electronics',14,'memory',3,'card',2,'connector',2,'drive',2,'graphics',2,'hard',2,'monitor',2,'search',2,'software',2],'manu'=>['inc',8,'apach',2,'belkin',2,'canon',2,'comput',2,'corp',2,'corsair',2,'foundat',2,'microsystem',2,'softwar',2]},'facet_dates'=>{}}})
200
+ end
201
+
202
+ # These spellcheck responses are all Solr 1.4 responses
203
+ def mock_response_with_spellcheck
204
+ %|{'responseHeader'=>{'status'=>0,'QTime'=>9,'params'=>{'spellcheck'=>'true','spellcheck.collate'=>'true','wt'=>'ruby','q'=>'hell ultrashar'}},'response'=>{'numFound'=>0,'start'=>0,'docs'=>[]},'spellcheck'=>{'suggestions'=>['hell',{'numFound'=>1,'startOffset'=>0,'endOffset'=>4,'suggestion'=>['dell']},'ultrashar',{'numFound'=>1,'startOffset'=>5,'endOffset'=>14,'suggestion'=>['ultrasharp']},'collation','dell ultrasharp']}}|
205
+ end
206
+
207
+ def mock_response_with_spellcheck_extended
208
+ %|{'responseHeader'=>{'status'=>0,'QTime'=>8,'params'=>{'spellcheck'=>'true','spellcheck.collate'=>'true','wt'=>'ruby','spellcheck.extendedResults'=>'true','q'=>'hell ultrashar'}},'response'=>{'numFound'=>0,'start'=>0,'docs'=>[]},'spellcheck'=>{'suggestions'=>['hell',{'numFound'=>1,'startOffset'=>0,'endOffset'=>4,'origFreq'=>0,'suggestion'=>[{'word'=>'dell','freq'=>1}]},'ultrashar',{'numFound'=>1,'startOffset'=>5,'endOffset'=>14,'origFreq'=>0,'suggestion'=>[{'word'=>'ultrasharp','freq'=>1}]},'correctlySpelled',false,'collation','dell ultrasharp']}}|
209
+ end
210
+
211
+ def mock_response_with_spellcheck_same_frequency
212
+ %|{'responseHeader'=>{'status'=>0,'QTime'=>8,'params'=>{'spellcheck'=>'true','spellcheck.collate'=>'true','wt'=>'ruby','spellcheck.extendedResults'=>'true','q'=>'hell ultrashar'}},'response'=>{'numFound'=>0,'start'=>0,'docs'=>[]},'spellcheck'=>{'suggestions'=>['hell',{'numFound'=>1,'startOffset'=>0,'endOffset'=>4,'origFreq'=>1,'suggestion'=>[{'word'=>'dell','freq'=>1}]},'ultrashard',{'numFound'=>1,'startOffset'=>5,'endOffset'=>14,'origFreq'=>1,'suggestion'=>[{'word'=>'ultrasharp','freq'=>1}]},'correctlySpelled',false,'collation','dell ultrasharp']}}|
213
+ end
214
+
215
+ # it can be the case that extended results are off and collation is on
216
+ def mock_response_with_spellcheck_collation
217
+ %|{'responseHeader'=>{'status'=>0,'QTime'=>3,'params'=>{'spellspellcheck.build'=>'true','spellcheck'=>'true','q'=>'hell','spellcheck.q'=>'hell ultrashar','wt'=>'ruby','spellcheck.collate'=>'true'}},'response'=>{'numFound'=>0,'start'=>0,'docs'=>[]},'spellcheck'=>{'suggestions'=>['hell',{'numFound'=>1,'startOffset'=>0,'endOffset'=>4,'suggestion'=>['dell']},'ultrashar',{'numFound'=>1,'startOffset'=>5,'endOffset'=>14,'suggestion'=>['ultrasharp']},'collation','dell ultrasharp']}}|
218
+ end
219
+
220
+ def mock_response_with_more_like_this
221
+ %({'responseHeader'=>{'status'=>0,'QTime'=>8,'params'=>{'facet'=>'false','mlt.mindf'=>'1','mlt.fl'=>'subject_t','fl'=>'id','mlt.count'=>'3','mlt.mintf'=>'0','mlt'=>'true','q.alt'=>'*:*','qt'=>'search','wt'=>'ruby'}},'response'=>{'numFound'=>30,'start'=>0,'docs'=>[{'id'=>'00282214'},{'id'=>'00282371'},{'id'=>'00313831'},{'id'=>'00314247'},{'id'=>'43037890'},{'id'=>'53029833'},{'id'=>'77826928'},{'id'=>'78908283'},{'id'=>'79930185'},{'id'=>'85910001'}]},'moreLikeThis'=>{'00282214'=>{'numFound'=>0,'start'=>0,'docs'=>[]},'00282371'=>{'numFound'=>0,'start'=>0,'docs'=>[]},'00313831'=>{'numFound'=>1,'start'=>0,'docs'=>[{'id'=>'96933325'}]},'00314247'=>{'numFound'=>3,'start'=>0,'docs'=>[{'id'=>'2008543486'},{'id'=>'96933325'},{'id'=>'2009373513'}]},'43037890'=>{'numFound'=>0,'start'=>0,'docs'=>[]},'53029833'=>{'numFound'=>0,'start'=>0,'docs'=>[]},'77826928'=>{'numFound'=>1,'start'=>0,'docs'=>[{'id'=>'94120425'}]},'78908283'=>{'numFound'=>0,'start'=>0,'docs'=>[]},'79930185'=>{'numFound'=>2,'start'=>0,'docs'=>[{'id'=>'94120425'},{'id'=>'2007020969'}]},'85910001'=>{'numFound'=>0,'start'=>0,'docs'=>[]}}})
222
+ end
223
+ end