sunspot 1.1.0 → 1.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (66) hide show
  1. data/Gemfile +10 -0
  2. data/Gemfile.lock +32 -0
  3. data/History.txt +24 -0
  4. data/README.rdoc +18 -5
  5. data/lib/sunspot.rb +40 -0
  6. data/lib/sunspot/dsl.rb +2 -2
  7. data/lib/sunspot/dsl/field_query.rb +2 -2
  8. data/lib/sunspot/dsl/fields.rb +0 -10
  9. data/lib/sunspot/dsl/restriction.rb +4 -4
  10. data/lib/sunspot/dsl/restriction_with_near.rb +121 -0
  11. data/lib/sunspot/dsl/scope.rb +55 -67
  12. data/lib/sunspot/dsl/standard_query.rb +11 -15
  13. data/lib/sunspot/field.rb +30 -29
  14. data/lib/sunspot/field_factory.rb +0 -18
  15. data/lib/sunspot/installer/solrconfig_updater.rb +0 -30
  16. data/lib/sunspot/query.rb +4 -3
  17. data/lib/sunspot/query/common_query.rb +2 -2
  18. data/lib/sunspot/query/composite_fulltext.rb +7 -2
  19. data/lib/sunspot/query/connective.rb +21 -6
  20. data/lib/sunspot/query/dismax.rb +1 -0
  21. data/lib/sunspot/query/geo.rb +53 -0
  22. data/lib/sunspot/query/more_like_this.rb +1 -0
  23. data/lib/sunspot/query/restriction.rb +5 -5
  24. data/lib/sunspot/query/standard_query.rb +0 -4
  25. data/lib/sunspot/search/abstract_search.rb +1 -7
  26. data/lib/sunspot/search/hit.rb +10 -10
  27. data/lib/sunspot/search/query_facet.rb +8 -3
  28. data/lib/sunspot/session.rb +10 -2
  29. data/lib/sunspot/session_proxy.rb +16 -0
  30. data/lib/sunspot/session_proxy/master_slave_session_proxy.rb +1 -1
  31. data/lib/sunspot/session_proxy/sharding_session_proxy.rb +7 -0
  32. data/lib/sunspot/session_proxy/silent_fail_session_proxy.rb +42 -0
  33. data/lib/sunspot/session_proxy/thread_local_session_proxy.rb +1 -1
  34. data/lib/sunspot/setup.rb +1 -17
  35. data/lib/sunspot/type.rb +38 -6
  36. data/lib/sunspot/util.rb +21 -31
  37. data/lib/sunspot/version.rb +1 -1
  38. data/solr/solr/conf/solrconfig.xml +0 -4
  39. data/spec/api/binding_spec.rb +12 -0
  40. data/spec/api/indexer/attributes_spec.rb +22 -22
  41. data/spec/api/query/connectives_examples.rb +14 -1
  42. data/spec/api/query/fulltext_examples.rb +3 -3
  43. data/spec/api/query/geo_examples.rb +69 -0
  44. data/spec/api/query/scope_examples.rb +32 -13
  45. data/spec/api/query/standard_spec.rb +1 -1
  46. data/spec/api/search/faceting_spec.rb +5 -1
  47. data/spec/api/search/hits_spec.rb +14 -12
  48. data/spec/api/session_proxy/class_sharding_session_proxy_spec.rb +1 -1
  49. data/spec/api/session_proxy/sharding_session_proxy_spec.rb +1 -1
  50. data/spec/api/session_proxy/silent_fail_session_proxy_spec.rb +24 -0
  51. data/spec/api/session_spec.rb +22 -0
  52. data/spec/integration/local_search_spec.rb +42 -69
  53. data/spec/integration/scoped_search_spec.rb +30 -0
  54. data/spec/mocks/connection.rb +6 -2
  55. data/spec/mocks/photo.rb +0 -1
  56. data/spec/mocks/post.rb +11 -2
  57. data/spec/mocks/user.rb +6 -1
  58. data/spec/spec_helper.rb +2 -12
  59. metadata +209 -177
  60. data/lib/sunspot/query/local.rb +0 -26
  61. data/solr/solr/lib/lucene-spatial-2.9.1.jar +0 -0
  62. data/solr/solr/lib/solr-spatial-light-0.0.6.jar +0 -0
  63. data/spec/api/query/local_examples.rb +0 -38
  64. data/tasks/gemspec.rake +0 -33
  65. data/tasks/rcov.rake +0 -28
  66. data/tasks/spec.rake +0 -24
data/Gemfile ADDED
@@ -0,0 +1,10 @@
1
+ source :rubygems
2
+
3
+ require File.expand_path('../lib/sunspot/version', __FILE__)
4
+
5
+ gem 'sunspot', Sunspot::VERSION, :path => File.expand_path('..', __FILE__)
6
+
7
+ group :test do
8
+ gem 'rspec', '~> 1.3'
9
+ gem 'ruby-debug'
10
+ end
data/Gemfile.lock ADDED
@@ -0,0 +1,32 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ sunspot (1.2.rc4)
5
+ escape (= 0.0.4)
6
+ pr_geohash (~> 1.0)
7
+ rsolr (= 0.12.1)
8
+
9
+ GEM
10
+ remote: http://rubygems.org/
11
+ specs:
12
+ builder (3.0.0)
13
+ columnize (0.3.2)
14
+ escape (0.0.4)
15
+ linecache (0.43)
16
+ pr_geohash (1.0.0)
17
+ rsolr (0.12.1)
18
+ builder (>= 2.1.2)
19
+ rspec (1.3.0)
20
+ ruby-debug (0.10.3)
21
+ columnize (>= 0.1)
22
+ ruby-debug-base (~> 0.10.3.0)
23
+ ruby-debug-base (0.10.3)
24
+ linecache (>= 0.3)
25
+
26
+ PLATFORMS
27
+ ruby
28
+
29
+ DEPENDENCIES
30
+ rspec (~> 1.3)
31
+ ruby-debug
32
+ sunspot (= 1.2.rc4)!
data/History.txt CHANGED
@@ -1,3 +1,27 @@
1
+ == 1.2.0 2010-12-28
2
+ * Replace solr-spatial-light with client-side geohash-based spatial search
3
+ * Override Solr field naming conventions using :as option
4
+ * Delegate #id method directly to calling context inside DSL
5
+ * Create a SilentFailSessionProxy that rescues exceptions on write operations.
6
+ * Inclusion by identity
7
+ * Solr optimize command
8
+ * Ignore negative :limit option for query facets
9
+ * Eliminated value sorting for range scopes
10
+ * Correctly cast stored boolean values if they are booleans
11
+ * Correctly cast and return stored values for multi-valued fields
12
+
13
+ == 1.1.0 2010-04-01
14
+ * MoreLikeThis support
15
+ * Allow multiple fulltext queries in one search
16
+ * Function queries
17
+ * Update solr-spatial-light to 0.0.6 build
18
+ * Support for :prefix when faceting.
19
+ * Allow specification of solr jar
20
+ * Updated reindex task to allow setting of batch size and list of models to index
21
+ * Use a '*:*' query for deleting the entire index
22
+ * Ability to specify custom request handler for queries
23
+ * Gracefully handle nonexistent search result
24
+
1
25
  == 1.0.4 2010-03-19
2
26
  * Update solr-spatial-light to 0.0.5
3
27
  * Fix NullPointerException in repeated geo search
data/README.rdoc CHANGED
@@ -63,7 +63,7 @@ field name patterns defined in the packaged <code>schema.xml</code>, so those
63
63
  cannot be modified.
64
64
 
65
65
  You can also run your own instance of Solr wherever you'd like; just copy the solr/config/schema.xml file out of the gem's solr into your installation.
66
- You can change the URL at which Sunspot accesses Solr with:
66
+ You can change the URL at which Sunspot accesses Solr by setting <code>SOLR_URL</code> in your application's environment, or assigning it to Sunspot's configuration directly:
67
67
 
68
68
  Sunspot.config.solr.url = 'http://solr.my.host:9818/solr'
69
69
 
@@ -84,7 +84,7 @@ See the README for that gem or the Sunspot Wiki for more information.
84
84
  end
85
85
 
86
86
  Sunspot.setup(Post) do
87
- text :title, :body
87
+ text :title, :description, :stored => true
88
88
  string :author_name
89
89
  integer :blog_id
90
90
  integer :category_ids
@@ -106,8 +106,10 @@ to define your own. See Sunspot::Adapters for more information.
106
106
 
107
107
  === Search for objects:
108
108
 
109
- search = Sunspot.search Post do
110
- keywords 'great pizza'
109
+ @search = Sunspot.search Post do
110
+ keywords 'great pizza' do
111
+ hightlight :title, :description
112
+ end
111
113
  with :author_name, 'Mark Twain'
112
114
  with(:blog_id).any_of [2, 14]
113
115
  with(:category_ids).all_of [4, 10]
@@ -143,7 +145,7 @@ See Sunspot.search for more information.
143
145
  - if hit.score
144
146
  %span.relevance== (#{hit.score})
145
147
  %p= hit.highlight(:description).format { |word| "<span class=\"highlight\">#{word}</span>" }
146
- .pagination= will_paginate(search.hits)
148
+ .pagination= will_paginate(@search.hits)
147
149
 
148
150
  == About the API documentation
149
151
 
@@ -208,14 +210,20 @@ Sunspot repository at `upstream`:
208
210
  * {Sunspot Full-text Search for Rails/Ruby}[http://therailworld.com/posts/23-Sunspot-Full-text-Search-for-Rails-Ruby] (The Rail World)
209
211
  * {A Few Sunspot Tips}[http://blog.trydionel.com/2009/11/19/a-few-sunspot-tips/] (spiral_code)
210
212
  * {Sunspot: A Solr-Powered Search Engine for Ruby}[http://www.linux-mag.com/id/7341] (Linux Magazine)
213
+ * {Sunspot Showed Me the Light}[http://bennyfreshness.com/2010/05/sunspot-helped-me-see-the-light/] (ben koonse)
214
+ * {rails3 + heroku + sunspot : madness}[http://anhaminha.tumblr.com/post/632682537/rails3-heroku-sunspot-madness] (anhaminha)
215
+ * {How to get full text search working with Sunspot}[http://cookbook.hobocentral.net/recipes/57-how-to-get-full-text-search] (Hobo Cookbook)
211
216
  * {Using Sunspot for Free-Text Search with Redis}[http://masonoise.wordpress.com/2010/02/06/using-sunspot-for-free-text-search-with-redis/] (While I Pondered...)
217
+ * {Fuzzy searching in SOLR with Sunspot}[http://www.pipetodevnull.com/past/2010/8/5/fuzzy_searching_in_solr_with_sunspot/] (pipe :to => /dev/null)
212
218
  * {Default scope with Sunspot}[http://www.cloudspace.com/blog/2010/01/15/default-scope-with-sunspot/] (Cloudspace)
213
219
  * {Chef recipe for Sunspot in production}[http://gist.github.com/336403]
220
+ * {Cucumber and Sunspot}[http://opensoul.org/2010/4/7/cucumber-and-sunspot] (opensoul.org)
214
221
  * {Testing Sunspot with Cucumber}[http://blog.trydionel.com/2010/02/06/testing-sunspot-with-cucumber/] (spiral_code)
215
222
  * {Running cucumber features with sunspot_rails}[http://blog.kabisa.nl/2010/02/03/running-cucumber-features-with-sunspot_rails] (Kabisa Blog)
216
223
  * {How To Use Twitter Lists to Determine Influence}[http://www.untitledstartup.com/2010/01/how-to-use-twitter-lists-to-determine-influence/] (Untitled Startup)
217
224
  * {Sunspot Quickstart}[http://wiki.websolr.com/index.php/Sunspot_Quickstart] (WebSolr)
218
225
  * {Solr, and Sunspot}[http://www.kuahyeow.com/2009/08/solr-and-sunspot.html] (YT!)
226
+ * {The Saga of the Switch}[http://mrb.github.com/2010/04/08/the-saga-of-the-switch.html] (mrb -- includes comparison of Sunspot and Ultrasphinx)
219
227
 
220
228
  == Contributors
221
229
 
@@ -233,6 +241,11 @@ Sunspot repository at `upstream`:
233
241
  * Kieran Topping
234
242
  * Nicolas Braem (nicolas.braem@gmail.com)
235
243
  * Jeremy Ashkenas (jashkenas@gmail.com)
244
+ * Dylan Vaughn (dylanvaughn@yahoo.com)
245
+ * Brian Durand (brian@embellishedvisions.com)
246
+ * Sam Granieri (sam@samgranieri.com)
247
+ * Nick Zadrozny (nick@onemorecloud.com)
248
+ * Jason Ronallo (jronallo@gmail.com)
236
249
 
237
250
  == License
238
251
 
data/lib/sunspot.rb CHANGED
@@ -206,6 +206,18 @@ module Sunspot
206
206
  session.commit
207
207
  end
208
208
 
209
+ # Optimizes the index on the singletion session.
210
+ #
211
+ # Frequently adding and deleting documents to Solr, leaves the index in a
212
+ # fragmented state. The optimize command merges all index segments into
213
+ # a single segment and removes any deleted documents, making it faster to
214
+ # search. Since optimize rebuilds the index from scratch, it takes some
215
+ # time and requires double the space on the hard disk while it's rebuilding.
216
+ # Note that optimize also commits.
217
+ def optimize
218
+ session.optimize
219
+ end
220
+
209
221
  #
210
222
  # Create a new Search instance, but do not execute it immediately. Generally
211
223
  # you will want to use the #search method to build and execute searches in
@@ -327,6 +339,34 @@ module Sunspot
327
339
  session.new_more_like_this(object, *types, &block)
328
340
  end
329
341
 
342
+ #
343
+ # Initiate a MoreLikeThis search. MoreLikeThis is a special type of search
344
+ # that finds similar documents using fulltext comparison. The fields to be
345
+ # compared are `text` fields set up with the `:more_like_this` option set to
346
+ # `true`. By default, more like this returns objects of the same type as the
347
+ # object used for comparison, but a list of types can optionally be passed
348
+ # to this method to return similar documents of other types. This will only
349
+ # work for types that have common fields.
350
+ #
351
+ # The DSL for MoreLikeThis search exposes several methods for setting
352
+ # options specific to this type of search. See the
353
+ # Sunspot::DSL::MoreLikeThis class and the MoreLikeThis documentation on
354
+ # the Solr wiki: http://wiki.apache.org/solr/MoreLikeThis
355
+ #
356
+ # MoreLikeThis searches have all of the same scoping, ordering, and faceting
357
+ # functionality as standard searches; the only thing you can't do in a MLT
358
+ # search is fulltext matching (since the MLT itself is a fulltext query).
359
+ #
360
+ # ==== Example
361
+ #
362
+ # post = Post.first
363
+ # Sunspot.more_like_this(post, Post, Page) do
364
+ # fields :title, :body
365
+ # with(:updated_at).greater_than(1.month.ago)
366
+ # facet(:category_ids)
367
+ # end
368
+ #
369
+ #
330
370
  def more_like_this(object, *types, &block)
331
371
  session.more_like_this(object, *types, &block)
332
372
  end
data/lib/sunspot/dsl.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  %w(fields scope paginatable adjustable field_query standard_query query_facet
2
- functional fulltext restriction search more_like_this_query
3
- function).each do |file|
2
+ functional fulltext restriction restriction_with_near search
3
+ more_like_this_query function).each do |file|
4
4
  require File.join(File.dirname(__FILE__), 'dsl', file)
5
5
  end
@@ -196,7 +196,7 @@ module Sunspot
196
196
  search_facet = @search.add_field_facet(field, options)
197
197
  Util.Array(options[:only]).each do |value|
198
198
  facet = Sunspot::Query::QueryFacet.new
199
- facet.add_restriction(field, Sunspot::Query::Restriction::EqualTo, value)
199
+ facet.add_positive_restriction(field, Sunspot::Query::Restriction::EqualTo, value)
200
200
  @query.add_query_facet(facet)
201
201
  search_facet.add_row(value, facet.to_boolean_phrase)
202
202
  end
@@ -236,7 +236,7 @@ module Sunspot
236
236
  nil
237
237
  )
238
238
  when :none
239
- extra_facet.add_restriction(
239
+ extra_facet.add_positive_restriction(
240
240
  field,
241
241
  Sunspot::Query::Restriction::EqualTo,
242
242
  nil
@@ -43,16 +43,6 @@ module Sunspot
43
43
  end
44
44
  end
45
45
 
46
- #
47
- # Specify a method or block that returns the geographical coordinates
48
- # associated with the document. The object returned must respond to #first
49
- # and #last (e.g., a two-element Array); or to #lat and one of #lng, #lon,
50
- # or #long
51
- #
52
- def coordinates(name = nil, &block)
53
- @setup.set_coordinates_field(name, &block)
54
- end
55
-
56
46
  #
57
47
  # Specify a document-level boost. As with fields, you have the option of
58
48
  # passing an attribute name which will be called on each model, or a block
@@ -8,15 +8,15 @@ module Sunspot
8
8
  # restriction.
9
9
  #
10
10
  class Restriction
11
- def initialize(field_name, query, negative) #:nodoc:
12
- @field_name, @scope, @negative = field_name, query, negative
11
+ def initialize(field, scope, negative) #:nodoc:
12
+ @field, @scope, @negative = field, scope, negative
13
13
  end
14
14
 
15
15
  Sunspot::Query::Restriction.names.each do |class_name|
16
16
  method_name = Util.snake_case(class_name.to_s)
17
17
  module_eval(<<-RUBY, __FILE__, __LINE__ + 1)
18
- def #{method_name}(value)
19
- @scope.add_restriction(@field_name, Sunspot::Query::Restriction::#{class_name}, value, @negative)
18
+ def #{method_name}(*value)
19
+ @scope.add_restriction(@negative, @field, Sunspot::Query::Restriction::#{class_name}, *value)
20
20
  end
21
21
  RUBY
22
22
  end
@@ -0,0 +1,121 @@
1
+ module Sunspot
2
+ module DSL
3
+ class RestrictionWithNear < Restriction
4
+ def initialize(field, scope, query, negated)
5
+ super(field, scope, negated)
6
+ @query = query
7
+ end
8
+
9
+ #
10
+ # Perform a Geohash-based location restriction for the given `location`
11
+ # field. Though this uses the same API as other attribute-field
12
+ # restrictions, there are several differences between this and other
13
+ # scoping methods:
14
+ #
15
+ # * It can only be called from the top-level query; it cannot be nested
16
+ # in a `dynamic`, `any_of`, or `all_of` block. This is because geohash
17
+ # queries are not sent to Solr as filter queries like other scopes, but
18
+ # rather are part of the fulltext query sent to Solr.
19
+ # * Because it is included with the fulltext query (if any), location
20
+ # restrictions can be given boost. By default, an "exact"
21
+ # (maximum-precision) match will give the result a boost of 1.0; each
22
+ # lower level of precision gives a boost of 1/2 the next highest
23
+ # precision. See below for options to modify this behavior.
24
+ #
25
+ # ==== What is a Geohash?
26
+ #
27
+ # Geohash is a clever algorithm that creates a decodable digest of a
28
+ # geographical point. It does this by dividing the globe into
29
+ # quadrants, encoding the quadrant in which the point sits in the hash,
30
+ # dividing the quadrant into smaller quadrants, and repeating an arbitrary
31
+ # number of times (the "precision"). Because of the way Geohash are
32
+ # built, the shared Geohash prefix length of two locations will
33
+ # <em>usually</em> increase as the distance between the points decreases.
34
+ # Put another way, the geohashes of two nearby points will
35
+ # <em>usually</em> have a longer shared prefix than two points which are
36
+ # distant from one another.
37
+ #
38
+ # Read more about Geohashes on
39
+ # {Wikipedia}[http://en.wikipedia.org/wiki/Geohash] or play around with
40
+ # generating your own at {geohash.org}[http://geohash.org/].
41
+ #
42
+ # In Sunspot, GeoHashes can have a precision between 3 and 12; this is the
43
+ # number of characters in the hash. The precisions have the following
44
+ # maximum bounding box sizes, in miles:
45
+ #
46
+ # <dt>3</dt>
47
+ # <dd>389.07812</dd>
48
+ # <dt>4</dt>
49
+ # <dd>97.26953</dd>
50
+ # <dt>5</dt>
51
+ # <dd>24.31738</dd>
52
+ # <dt>6</dt>
53
+ # <dd>6.07935</dd>
54
+ # <dt>7</dt>
55
+ # <dd>1.51984
56
+ # <dt>8</dt>
57
+ # <dd>0.37996</dd>
58
+ # <dt>9</dt>
59
+ # <dd>0.09499</dd>
60
+ # <dt>10</dt>
61
+ # <dd>0.02375</dd>
62
+ # <dt>11</dt>
63
+ # <dd>0.00594</dd>
64
+ # <dt>12</dt>
65
+ # <dd>0.00148</dd>
66
+ #
67
+ # ==== Score, boost, and sorting with location search
68
+ #
69
+ # The concept of relevance scoring is a familiar one from fulltext search;
70
+ # Solr (or Lucene, actually) gives each result document a score based on
71
+ # how relevant the document's text is to the search phrase. Sunspot's
72
+ # location search also uses scoring to determine geographical relevance;
73
+ # using boosts, longer prefix matches (which are, in general,
74
+ # geographically closer to the search origin) are assigned higher
75
+ # relevance. This means that the results of a pure location search are
76
+ # <em>roughly</em> in order of geographical distance, as long as no other
77
+ # sort is specified explicitly.
78
+ #
79
+ # This geographical relevance plays on the same field as fulltext scoring;
80
+ # if you use both fulltext and geographical components in a single search,
81
+ # both types of relevance will be taken into account when scoring the
82
+ # matches. Thus, a very close fulltext match that's further away from the
83
+ # geographical origin will be scored similarly to a less precise fulltext
84
+ # match that is very close to the geographical origin. That's likely to be
85
+ # consistent with the way most users would expect a fulltext geographical
86
+ # search to work.
87
+ #
88
+ # ==== Options
89
+ #
90
+ # <dt><code>:precision</code></dt>
91
+ # <dd>The minimum precision at which locations should match. See the table
92
+ # of precisions and bounding-box sizes above; the proximity value will
93
+ # ensure that all matching documents share a bounding box of the
94
+ # corresponding maximum size with your origin point. The default value
95
+ # is 7, meaning all results will share a bounding box with edges of
96
+ # about one and a half miles with the origin.</dd>
97
+ # <dt><code>:boost</code></dt>
98
+ # <dd>The boost to apply to maximum-precision matches. Default is 1.0. You
99
+ # can use this option to adjust the weight given to geographic
100
+ # proximity versus fulltext matching, if you are doing both in a
101
+ # search.</dd>
102
+ # <dt><code>:precision_factor</code></dt>
103
+ # <dd>This option determines how much boost is applied to matches at lower
104
+ # precisions. The default value, 16.0, means that a match at precision
105
+ # N is 1/16 as relevant as a match at precision N+1 (this is consistent
106
+ # with the fact that each precision's bounding box is about sixteen
107
+ # times the size of the next highest precision.)</dd>
108
+ #
109
+ # ==== Example
110
+ #
111
+ # Sunspot.search(Post) do
112
+ # fulltext('pizza')
113
+ # with(:location).near(-40.0, -70.0, :boost => 2, :precision => 6)
114
+ # end
115
+ #
116
+ def near(lat, lng, options = {})
117
+ @query.fulltext.add_location(@field, lat, lng, options)
118
+ end
119
+ end
120
+ end
121
+ end
@@ -15,24 +15,35 @@ module Sunspot
15
15
  end
16
16
 
17
17
  #
18
- # Build a positive restriction. With one argument, this method returns
19
- # another DSL object which presents methods for attaching various
20
- # restriction types. With two arguments, this creates a shorthand
21
- # restriction: if the second argument is a scalar, an equality restriction
22
- # is created; if it is a Range, a between restriction will be created; and
23
- # if it is an Array, an any_of restriction will be created.
24
- #
25
- # ==== Parameters
18
+ # Build a positive restriction. This method can take three forms: equality
19
+ # restriction, restriction by another restriction, or identity
20
+ # restriction.
21
+ # In the first two forms, the first argument is a field name. If only a
22
+ # field name is specified, this method returns another DSL object which
23
+ # presents methods for attaching various restriction types.
24
+ # With two arguments, this creates a shorthand restriction: if the second
25
+ # argument is a scalar, an equality restriction is created; if it is a
26
+ # Range, a between restriction will be created; and if it is an Array, an
27
+ # any_of restriction will be created.
28
+ # The third from restricts the search results to a specific instance.
29
+ #
30
+ # ==== Parameters (restriction by field value)
26
31
  #
27
32
  # field_name<Symbol>:: Name of the field on which to place the restriction
28
33
  # value<Object,Range,Array>::
29
34
  # If passed, creates an equality, range, or any-of restriction based on
30
35
  # the type of value passed.
31
36
  #
37
+ # ==== Parameters (restriction by identity)
38
+ #
39
+ # args<Object>...::
40
+ # One or more instances that should be included in the results
41
+ #
32
42
  # ==== Returns
33
43
  #
34
44
  # Sunspot::DSL::Query::Restriction::
35
- # Restriction DSL object (if only one argument is passed)
45
+ # Restriction DSL object (if only one argument is passed which is a
46
+ # field name)
36
47
  #
37
48
  # ==== Examples
38
49
  #
@@ -60,71 +71,22 @@ module Sunspot
60
71
  # with(:average_rating).greater_than(3.0)
61
72
  # end
62
73
  #
63
- def with(field_name, value = NONE)
64
- if value == NONE
65
- DSL::Restriction.new(@setup.field(field_name.to_sym), @scope, false)
66
- else
67
- @scope.add_shorthand_restriction(@setup.field(field_name), value)
68
- end
69
- end
70
-
71
- #
72
- # Build a negative restriction (exclusion). This method can take three
73
- # forms: equality exclusion, exclusion by another restriction, or identity
74
- # exclusion. The first two forms work the same way as the #with method;
75
- # the third excludes a specific instance from the search results.
76
- #
77
- # ==== Parameters (exclusion by field value)
78
- #
79
- # field_name<Symbol>:: Name of the field on which to place the exclusion
80
- # value<Symbol>::
81
- # If passed, creates an equality exclusion with this value
82
- #
83
- # ==== Parameters (exclusion by identity)
84
- #
85
- # args<Object>...::
86
- # One or more instances that should be excluded from the results
87
- #
88
- # ==== Examples
89
- #
90
- # An equality exclusion:
91
- #
92
- # Sunspot.search(Post) do
93
- # without(:blog_id, 1)
94
- # end
95
- #
96
- # Other restriction types:
74
+ # Restriction by identity:
97
75
  #
98
76
  # Sunspot.search(Post) do
99
- # without(:average_rating).greater_than(3.0)
77
+ # with(some_post_instance)
100
78
  # end
101
79
  #
102
- # Exclusion by identity:
80
+ def with(*args)
81
+ add_restriction(false, *args)
82
+ end
83
+
103
84
  #
104
- # Sunspot.search(Post) do
105
- # without(some_post_instance)
106
- # end
85
+ # Build a negative restriction (exclusion). This method works the same way
86
+ # asthe #with method.
107
87
  #
108
88
  def without(*args)
109
- case args.first
110
- when String, Symbol
111
- field_name = args[0]
112
- value = args.length > 1 ? args[1] : NONE
113
- if value == NONE
114
- DSL::Restriction.new(@setup.field(field_name.to_sym), @scope, true)
115
- else
116
- @scope.add_negated_shorthand_restriction(@setup.field(field_name.to_sym), value)
117
- end
118
- else
119
- instances = args
120
- instances.flatten.each do |instance|
121
- @scope.add_negated_restriction(
122
- IdField.instance,
123
- Sunspot::Query::Restriction::EqualTo,
124
- Sunspot::Adapters::InstanceAdapter.adapt(instance).index_id
125
- )
126
- end
127
- end
89
+ add_restriction(true, *args)
128
90
  end
129
91
 
130
92
  #
@@ -224,6 +186,32 @@ module Sunspot
224
186
  &block
225
187
  )
226
188
  end
189
+
190
+ private
191
+
192
+ def add_restriction(negated, *args)
193
+ case args.first
194
+ when String, Symbol
195
+ raise ArgumentError if args.length > 2
196
+ field_name = args[0]
197
+ value = args.length > 1 ? args[1] : NONE
198
+ if value == NONE
199
+ DSL::Restriction.new(@setup.field(field_name.to_sym), @scope, negated)
200
+ else
201
+ @scope.add_shorthand_restriction(negated, @setup.field(field_name.to_sym), value)
202
+ end
203
+ else
204
+ instances = args.flatten
205
+ @scope.add_restriction(
206
+ negated,
207
+ IdField.instance,
208
+ Sunspot::Query::Restriction::AnyOf,
209
+ instances.flatten.map { |instance|
210
+ Sunspot::Adapters::InstanceAdapter.adapt(instance).index_id }
211
+ )
212
+ end
213
+ end
214
+
227
215
  end
228
216
  end
229
217
  end