sunspot 1.1.0 → 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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