outoftime-sunspot 0.7.3 → 0.8.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.
data/History.txt CHANGED
@@ -1,6 +1,31 @@
1
- == 0.0.5 TBD
1
+ == 0.8.0 2009-05-22
2
+ * Access query API directly; instantiate search without running it
3
+ * Dynamic fields
4
+ * Search blocks can be evaluated in calling context
5
+
6
+ == 0.7.3 2009-05-06
7
+ * Better exception handling when class doesn't have adapter/setup
8
+
9
+ == 0.7.2 2009-04-29
10
+ * Dirty sessions
11
+
12
+ == 0.7.1 2009-04-29
13
+ * Removed extlib dependency from gemspec
14
+
15
+ == 0.7.0 2009-04-28
16
+ * Less magic in the DSL
17
+ * Restrict by empty values
2
18
  * Negative scoping using without() method
3
19
  * Exclusion by object identity using without(instance)
20
+ * Support for faceting
21
+ * Explicit commits
22
+ * Boolean field type
23
+ * Attribute field flexibility
24
+ * Virtual field blocks can be evaluated in calling context
25
+ * Order available by multiple fields
26
+ * New adapter API
27
+ * Got rid of builder API
28
+ * Full documentation
4
29
 
5
30
  == 0.0.2 2009-02-14
6
31
  * Run sunspot's built-in Solr instance using
data/LICENSE ADDED
@@ -0,0 +1,18 @@
1
+ Permission is hereby granted, free of charge, to any person obtaining
2
+ a copy of this software and associated documentation files (the
3
+ 'Software'), to deal in the Software without restriction, including
4
+ without limitation the rights to use, copy, modify, merge, publish,
5
+ distribute, sublicense, and/or sell copies of the Software, and to
6
+ permit persons to whom the Software is furnished to do so, subject to
7
+ the following conditions:
8
+
9
+ The above copyright notice and this permission notice shall be
10
+ included in all copies or substantial portions of the Software.
11
+
12
+ THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
13
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
14
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
15
+ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
16
+ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
17
+ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
18
+ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.rdoc CHANGED
@@ -1,11 +1,11 @@
1
- = sunspot
1
+ = Sunspot
2
2
 
3
- http://github.com/outoftime/sunspot
3
+ http://outoftime.github.com/sunspot
4
4
 
5
5
  Sunspot is a Ruby library for expressive, powerful interaction with the Solr search engine.
6
6
  Sunspot is built on top of the solr-ruby gem, which provides a low-level interface for Solr
7
7
  interaction; Sunspot provides a simple, intuitive, expressive DSL backed by powerful
8
- features for indexing objects and searching against them.
8
+ features for indexing objects and searching for them.
9
9
 
10
10
  Sunspot is designed to be easily plugged in to any ORM, or even non-database-backed
11
11
  objects such as the filesystem.
@@ -65,6 +65,13 @@ integrating Sunspot into Rails drop-in easy.
65
65
 
66
66
  See Sunspot.setup for more information.
67
67
 
68
+ Note that in order for a class to be searchable, it must have an adapter
69
+ registered for itself or one of its subclasses. Adapters allow Sunspot to load
70
+ objects out of persistent storage, and to determine their primary key for
71
+ indexing. {Sunspot::Rails}[http://github.com/outoftime/sunspot_rails] comes with
72
+ an adapter for ActiveRecord objects, but for other types of models you will need
73
+ to define your own. See Sunspot::Adapters for more information.
74
+
68
75
  === Search for objects:
69
76
 
70
77
  search = Sunspot.search Post do
@@ -92,6 +99,23 @@ See Sunspot.search for more information.
92
99
  search.per_page
93
100
  search.facet(:blog_id)
94
101
 
102
+ === Building searches manually:
103
+
104
+ The search DSL is great for building searches from fairly static parameters,
105
+ but a highly dynamic search might want to leverage an intermediate approach
106
+ (such as an application of the Builder pattern). For these cases, Sunspot
107
+ exposes direct access to the Query object:
108
+
109
+ search = Sunspot.new_search(Post)
110
+ search.query.keywords = 'great pizza'
111
+ search.query.add_restriction(:author_name, :equal_to, 'Mark Twain')
112
+ search.query.add_restriction(:title, :equal_to, 'Bad Title', true) # negate the restriction
113
+ search.query.exclude_instance(bad_instance)
114
+ search.query.paginate(3, 15)
115
+ search.query.order_by(:average_rating, :desc)
116
+ search.query.add_field_facet(:blog_id)
117
+ search.execute!
118
+
95
119
  == About the API documentation
96
120
 
97
121
  All of the methods documented in the RDoc are considered part of Sunspot's
@@ -105,6 +129,8 @@ me so I can rectify the situation!
105
129
  1. solr-ruby
106
130
  2. Java
107
131
 
132
+ Sunspot has been tested with MRI 1.8.6, YARV 1.9.1, and JRuby 1.2.0
133
+
108
134
  == Bugs
109
135
 
110
136
  Please submit bug reports to
@@ -112,6 +138,7 @@ http://outoftime.lighthouseapp.com/projects/20339-sunspot
112
138
 
113
139
  == Further Reading
114
140
 
141
+ Sunspot Discussion: http://groups.google.com/group/ruby-sunspot
115
142
  Posts about Sunspot from my tumblog: http://outofti.me/tagged/sunspot
116
143
 
117
144
  == Contributors
@@ -120,25 +147,4 @@ Mat Brown <mat@patch.com>
120
147
 
121
148
  == LICENSE:
122
149
 
123
- (The MIT License)
124
-
125
- Copyright (c) 2008-2009 Mat Brown
126
-
127
- Permission is hereby granted, free of charge, to any person obtaining
128
- a copy of this software and associated documentation files (the
129
- 'Software'), to deal in the Software without restriction, including
130
- without limitation the rights to use, copy, modify, merge, publish,
131
- distribute, sublicense, and/or sell copies of the Software, and to
132
- permit persons to whom the Software is furnished to do so, subject to
133
- the following conditions:
134
-
135
- The above copyright notice and this permission notice shall be
136
- included in all copies or substantial portions of the Software.
137
-
138
- THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
139
- EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
140
- MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
141
- IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
142
- CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
143
- TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
144
- SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
150
+ Sunspot is distributed under the MIT License, copyright (c) 2008-2009 Mat Brown
data/Rakefile CHANGED
@@ -8,4 +8,4 @@ end
8
8
 
9
9
  Dir['tasks/**/*.rake'].each { |t| load t }
10
10
 
11
- task :default => :spec
11
+ task :default => 'spec:api'
data/TODO CHANGED
@@ -1,8 +1,4 @@
1
- === 0.8 ===
2
- * Expose Query as part of public API
3
- * Facet by type
4
- * Dynamic fields
5
- * ActiveRecord, DataMapper, File adapters
6
1
  === 0.9 ===
2
+ * Facet by type (?)
7
3
  * Switch to RSolr
8
4
  * Query-based faceting (?)
data/VERSION.yml CHANGED
@@ -1,4 +1,4 @@
1
1
  ---
2
- :patch: 3
3
2
  :major: 0
4
- :minor: 7
3
+ :minor: 8
4
+ :patch: 0
@@ -1,15 +1,32 @@
1
1
  module Sunspot
2
- # Sunspot provides an adapter architecture that allows applications or plugin
3
- # developers to define adapters for any type of object. An adapter is composed
4
- # of two classes, an InstanceAdapter and a DataAccessor. Note that an adapter
5
- # does not need to provide both classes - InstanceAdapter is only needed if
6
- # you wish to index instances of the class, and DataAccessor is only needed if
7
- # you wish to retrieve instances of this class in search results. Of course,
8
- # both will be the case most of the time.
2
+ #
3
+ # Sunspot works by saving references to the primary key (or natural ID) of
4
+ # each indexed object, and then retrieving the objects from persistent storage
5
+ # when their IDs are referenced in search results. In order for Sunspot to
6
+ # know what an object's primary key is, and how to retrieve objects from
7
+ # persistent storage given a primary key, an adapter must be registered for
8
+ # that object's class or one of its superclasses (for instance, an adapter
9
+ # registered for ActiveRecord::Base would be used for all ActiveRecord
10
+ # models).
9
11
  #
10
- # See Sunspot::Adapters::DataAccessor.register and
11
- # Sunspot::Adapters::InstanceAdapter.register for information on how to enable
12
- # an adapter for use by Sunspot.
12
+ # To provide Sunspot with this ability, adapters must have two roles:
13
+ #
14
+ # Data accessor::
15
+ # A subclass of Sunspot::Adapters::DataAccessor, this object is instantiated
16
+ # with a particular class and must respond to the #load() method, which
17
+ # returns an object from persistent storage given that object's primary key.
18
+ # It can also optionally implement the #load_all() method, which returns
19
+ # a collection of objects given a collection of primary keys, if that can be
20
+ # done more efficiently than calling #load() on each key.
21
+ # Instance adapter::
22
+ # A subclass of Sunspot::Adapters::InstanceAdapter, this object is
23
+ # instantiated with a particular instance. Its only job is to tell Sunspot
24
+ # what the object's primary key is, by implementing the #id() method.
25
+ #
26
+ # Adapters are registered by registering their two components, telling Sunspot
27
+ # that they are available for one or more classes, and all of their
28
+ # subclasses. See Sunspot::Adapters::DataAccessor.register and
29
+ # Sunspot::Adapters::InstanceAdapter.register for the details.
13
30
  #
14
31
  # See spec/mocks/mock_adapter.rb for an example of how adapter classes should
15
32
  # be implemented.
@@ -178,7 +195,7 @@ module Sunspot
178
195
  # DataAccessor::
179
196
  # DataAccessor implementation which provides access to given class
180
197
  #
181
- def create(clazz)
198
+ def create(clazz) #:nodoc:
182
199
  self.for(clazz).new(clazz)
183
200
  end
184
201
 
@@ -0,0 +1,37 @@
1
+ module Sunspot
2
+ #
3
+ # DataExtractors present an internal API for the indexer to use to extract
4
+ # field values from models for indexing. They must implement the #value_for
5
+ # method, which takes an object and returns the value extracted from it.
6
+ #
7
+ module DataExtractor #:nodoc: all
8
+ #
9
+ # AttributeExtractors extract data by simply calling a method on the block.
10
+ #
11
+ class AttributeExtractor
12
+ def initialize(attribute_name)
13
+ @attribute_name = attribute_name
14
+ end
15
+
16
+ def value_for(object)
17
+ object.send(@attribute_name)
18
+ end
19
+ end
20
+
21
+ #
22
+ # BlockExtractors extract data by evaluating a block in the context of the
23
+ # object instance, or if the block takes an argument, by passing the object
24
+ # as the argument to the block. Either way, the return value of the block is
25
+ # the value returned by the extractor.
26
+ #
27
+ class BlockExtractor
28
+ def initialize(&block)
29
+ @block = block
30
+ end
31
+
32
+ def value_for(object)
33
+ Util.instance_eval_or_call(object, &@block)
34
+ end
35
+ end
36
+ end
37
+ end
@@ -1,5 +1,5 @@
1
1
  module Sunspot
2
- module DSL
2
+ module DSL #:nodoc:
3
3
  # The Fields class provides a DSL for specifying field definitions in the
4
4
  # Sunspot.setup block. As well as the #text method, which creates fulltext
5
5
  # fields, uses #method_missing to allow definition of typed fields. The
@@ -23,7 +23,7 @@ module Sunspot
23
23
  #
24
24
  def text(*names, &block)
25
25
  for name in names
26
- @setup.add_text_fields(build_field(name, Type::TextType, &block))
26
+ @setup.add_text_fields(Field::StaticField.build(name, Type::TextType, &block))
27
27
  end
28
28
  end
29
29
 
@@ -43,26 +43,15 @@ module Sunspot
43
43
  #
44
44
  def method_missing(method, *args, &block)
45
45
  begin
46
- type = Type.const_get("#{Util.camel_case(method.to_s)}Type")
46
+ type = Type.const_get("#{Util.camel_case(method.to_s.sub(/^dynamic_/, ''))}Type")
47
47
  rescue(NameError)
48
48
  super(method.to_sym, *args, &block) and return
49
49
  end
50
50
  name = args.shift
51
- @setup.add_fields(build_field(name, type, *args, &block))
52
- end
53
-
54
- private
55
-
56
- # Factory method for field instances, used by the public methods in this
57
- # class. Create a VirtualField if a block is passed, or an AttributeField
58
- # if not.
59
- #
60
- def build_field(name, type, *args, &block) #:nodoc:
61
- options = args.shift if args.first.is_a?(Hash)
62
- unless block
63
- Field::AttributeField.new(name, type, options || {})
51
+ if method.to_s =~ /^dynamic_/
52
+ @setup.add_dynamic_fields(Field::DynamicField.build(name, type, *args, &block))
64
53
  else
65
- Field::VirtualField.new(name, type, options || {}, &block)
54
+ @setup.add_fields(Field::StaticField.build(name, type, *args, &block))
66
55
  end
67
56
  end
68
57
  end
@@ -1,19 +1,15 @@
1
1
  module Sunspot
2
- module DSL
2
+ module DSL #:nodoc:
3
3
  #
4
4
  # This class presents a DSL for constructing queries using the
5
5
  # Sunspot.search method. Methods of this class are available inside the
6
- # search block.
6
+ # search block. Methods that take field names as arguments are implemented
7
+ # in the superclass Sunspot::DSL::Scope, as that DSL is also available in
8
+ # the #dynamic() block.
7
9
  #
8
10
  # See Sunspot.search for usage examples
9
11
  #
10
- class Query
11
- NONE = Object.new
12
-
13
- def initialize(query) #:nodoc:
14
- @query = query
15
- end
16
-
12
+ class Query < Scope
17
13
  # Specify a phrase that should be searched as fulltext. Only +text+
18
14
  # fields are searched - see DSL::Fields.text
19
15
  #
@@ -31,100 +27,6 @@ module Sunspot
31
27
  @query.keywords = keywords
32
28
  end
33
29
 
34
- #
35
- # Build a positive restriction. With one argument, this method returns
36
- # another DSL object which presents methods for attaching various
37
- # restriction types. With two arguments, acts as a shorthand for creating
38
- # an equality restriction.
39
- #
40
- # ==== Parameters
41
- #
42
- # field_name<Symbol>:: Name of the field on which to place the restriction
43
- # value<Symbol>::
44
- # If passed, creates an equality restriction with this value
45
- #
46
- # ==== Returns
47
- #
48
- # Sunspot::DSL::Restriction::
49
- # Restriction DSL object (if only one argument is passed)
50
- #
51
- # ==== Examples
52
- #
53
- # An equality restriction:
54
- #
55
- # Sunspot.search do
56
- # with(:blog_id, 1)
57
- # end
58
- #
59
- # Other restriction types:
60
- #
61
- # Sunspot.search(Post) do
62
- # with(:average_rating).greater_than(3.0)
63
- # end
64
- #
65
- def with(field_name, value = NONE)
66
- if value == NONE
67
- DSL::Restriction.new(field_name.to_sym, @query, false)
68
- else
69
- @query.add_restriction(field_name, Sunspot::Restriction::EqualTo, value, false)
70
- end
71
- end
72
-
73
- #
74
- # Build a negative restriction (exclusion). This method can take three
75
- # forms: equality exclusion, exclusion by another restriction, or identity
76
- # exclusion. The first two forms work the same way as the #with method;
77
- # the third excludes a specific instance from the search results.
78
- #
79
- # ==== Parameters (exclusion by field value)
80
- #
81
- # field_name<Symbol>:: Name of the field on which to place the exclusion
82
- # value<Symbol>::
83
- # If passed, creates an equality exclusion with this value
84
- #
85
- # ==== Parameters (exclusion by identity)
86
- #
87
- # args<Object>...::
88
- # One or more instances that should be excluded from the results
89
- #
90
- # ==== Examples
91
- #
92
- # An equality exclusion:
93
- #
94
- # Sunspot.search(Post) do
95
- # without(:blog_id, 1)
96
- # end
97
- #
98
- # Other restriction types:
99
- #
100
- # Sunspot.search(Post) do
101
- # without(:average_rating).greater_than(3.0)
102
- # end
103
- #
104
- # Exclusion by identity:
105
- #
106
- # Sunspot.search(Post) do
107
- # without(some_post_instance)
108
- # end
109
- #
110
- def without(*args)
111
- case args.first
112
- when String, Symbol
113
- field_name = args[0]
114
- value = args.length > 1 ? args[1] : NONE
115
- if value == NONE
116
- DSL::Restriction.new(field_name.to_sym, @query, true)
117
- else
118
- @query.add_restriction(field_name, Sunspot::Restriction::EqualTo, value, true)
119
- end
120
- else
121
- instances = args
122
- for instance in instances.flatten
123
- @query.add_component(Sunspot::Restriction::SameAs.new(instance, true))
124
- end
125
- end
126
- end
127
-
128
30
  # Paginate your search. This works the same way as WillPaginate's
129
31
  # paginate().
130
32
  #
@@ -148,28 +50,28 @@ module Sunspot
148
50
  @query.paginate(page, per_page)
149
51
  end
150
52
 
151
- # Specify the order that results should be returned in. This method can
152
- # be called multiple times; precedence will be in the order given.
153
53
  #
54
+ # Apply restrictions, facets, and ordering to dynamic field instances.
55
+ # The block API is implemented by Sunspot::DSL::Scope, which is a
56
+ # superclass of the Query DSL (thus providing a subset of the API, in
57
+ # particular only methods that refer to particular fields).
58
+ #
154
59
  # ==== Parameters
60
+ #
61
+ # base_name<Symbol>:: The base name for the dynamic field definition
155
62
  #
156
- # field_name<Symbol>:: the field to use for ordering
157
- # direction<Symbol>:: :asc or :desc (default :asc)
158
- #
159
- def order_by(field_name, direction = nil)
160
- @query.order_by(field_name, direction)
161
- end
162
-
163
- # Request facets on the given field names. See Sunspot::Search#facet and
164
- # Sunspot::Facet for information on what is returned.
63
+ # ==== Example
165
64
  #
166
- # ==== Parameters
65
+ # Sunspot.search Post do
66
+ # dynamic :custom do
67
+ # with :cuisine, 'Pizza'
68
+ # facet :atmosphere
69
+ # order_by :chef_name
70
+ # end
71
+ # end
167
72
  #
168
- # field_names...<Symbol>:: fields for which to return field facets
169
- def facet(*field_names)
170
- for field_name in field_names
171
- @query.add_field_facet(field_name)
172
- end
73
+ def dynamic(base_name, &block)
74
+ Scope.new(@query.dynamic_query(base_name)).instance_eval(&block)
173
75
  end
174
76
  end
175
77
  end
@@ -0,0 +1,25 @@
1
+ module Sunspot
2
+ module DSL #:nodoc:
3
+ #
4
+ # This class presents an API for building restrictions in the query DSL. The
5
+ # methods exposed are the snake-cased names of the classes defined in the
6
+ # Restriction module, with the exception of Base and SameAs. All methods
7
+ # take a single argument, which is the value to be applied to the
8
+ # restriction.
9
+ #
10
+ class Restriction #:nodoc:
11
+ def initialize(field_name, query, negative)
12
+ @field_name, @query, @negative = field_name, query, negative
13
+ end
14
+
15
+ Sunspot::Query::Restriction.names.each do |class_name|
16
+ method_name = Util.snake_case(class_name.to_s)
17
+ module_eval(<<-RUBY, __FILE__, __LINE__ + 1)
18
+ def #{method_name}(value)
19
+ @query.add_restriction(@field_name, Sunspot::Query::Restriction::#{class_name}, value, @negative)
20
+ end
21
+ RUBY
22
+ end
23
+ end
24
+ end
25
+ end
@@ -1,24 +1,136 @@
1
1
  module Sunspot
2
- module DSL
2
+ module DSL #:nodoc:
3
3
  #
4
- # This class presents an API for building restrictions in the query DSL. The
5
- # methods exposed are the snake-cased names of the classes defined in the
6
- # Restriction module, with the exception of Base and SameAs. All methods
7
- # take a single argument, which is the value to be applied to the
8
- # restriction.
4
+ # This DSL presents methods for constructing restrictions and other query
5
+ # elements that are specific to fields. As well as being a superclass of
6
+ # Sunspot::DSL::Query, which presents the main query block, this DSL class
7
+ # is also used directly inside the #dynamic() block, which only allows
8
+ # operations on specific fields.
9
9
  #
10
- class Restriction
11
- def initialize(field_name, query, negative)
12
- @field_name, @query, @negative = field_name, query, negative
10
+ class Scope
11
+ NONE = Object.new
12
+
13
+ def initialize(query) #:nodoc:
14
+ @query = query
15
+ end
16
+
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, acts as a shorthand for creating
21
+ # an equality restriction.
22
+ #
23
+ # ==== Parameters
24
+ #
25
+ # field_name<Symbol>:: Name of the field on which to place the restriction
26
+ # value<Symbol>::
27
+ # If passed, creates an equality restriction with this value
28
+ #
29
+ # ==== Returns
30
+ #
31
+ # Sunspot::DSL::Query::Restriction::
32
+ # Restriction DSL object (if only one argument is passed)
33
+ #
34
+ # ==== Examples
35
+ #
36
+ # An equality restriction:
37
+ #
38
+ # Sunspot.search do
39
+ # with(:blog_id, 1)
40
+ # end
41
+ #
42
+ # Other restriction types:
43
+ #
44
+ # Sunspot.search(Post) do
45
+ # with(:average_rating).greater_than(3.0)
46
+ # end
47
+ #
48
+ def with(field_name, value = NONE)
49
+ if value == NONE
50
+ DSL::Restriction.new(field_name.to_sym, @query, false)
51
+ else
52
+ @query.add_restriction(field_name, Sunspot::Query::Restriction::EqualTo, value, false)
53
+ end
13
54
  end
14
55
 
15
- Sunspot::Restriction.names.each do |class_name|
16
- method_name = Util.snake_case(class_name)
17
- module_eval(<<-RUBY, __FILE__, __LINE__ + 1)
18
- def #{method_name}(value)
19
- @query.add_restriction(@field_name, Sunspot::Restriction::#{class_name}, value, @negative)
56
+ #
57
+ # Build a negative restriction (exclusion). This method can take three
58
+ # forms: equality exclusion, exclusion by another restriction, or identity
59
+ # exclusion. The first two forms work the same way as the #with method;
60
+ # the third excludes a specific instance from the search results.
61
+ #
62
+ # ==== Parameters (exclusion by field value)
63
+ #
64
+ # field_name<Symbol>:: Name of the field on which to place the exclusion
65
+ # value<Symbol>::
66
+ # If passed, creates an equality exclusion with this value
67
+ #
68
+ # ==== Parameters (exclusion by identity)
69
+ #
70
+ # args<Object>...::
71
+ # One or more instances that should be excluded from the results
72
+ #
73
+ # ==== Examples
74
+ #
75
+ # An equality exclusion:
76
+ #
77
+ # Sunspot.search(Post) do
78
+ # without(:blog_id, 1)
79
+ # end
80
+ #
81
+ # Other restriction types:
82
+ #
83
+ # Sunspot.search(Post) do
84
+ # without(:average_rating).greater_than(3.0)
85
+ # end
86
+ #
87
+ # Exclusion by identity:
88
+ #
89
+ # Sunspot.search(Post) do
90
+ # without(some_post_instance)
91
+ # end
92
+ #
93
+ def without(*args)
94
+ case args.first
95
+ when String, Symbol
96
+ field_name = args[0]
97
+ value = args.length > 1 ? args[1] : NONE
98
+ if value == NONE
99
+ DSL::Restriction.new(field_name.to_sym, @query, true)
100
+ else
101
+ @query.add_negated_restriction(field_name, Sunspot::Query::Restriction::EqualTo, value)
102
+ end
103
+ else
104
+ instances = args
105
+ for instance in instances.flatten
106
+ @query.exclude_instance(instance)
20
107
  end
21
- RUBY
108
+ end
109
+ end
110
+
111
+ # Specify the order that results should be returned in. This method can
112
+ # be called multiple times; precedence will be in the order given.
113
+ #
114
+ # ==== Parameters
115
+ #
116
+ # field_name<Symbol>:: the field to use for ordering
117
+ # direction<Symbol>:: :asc or :desc (default :asc)
118
+ #
119
+ def order_by(field_name, direction = nil)
120
+ @query.order_by(field_name, direction)
121
+ end
122
+
123
+ # Request facets on the given field names. See Sunspot::Search#facet and
124
+ # Sunspot::Facet for information on what is returned.
125
+ #
126
+ # ==== Parameters
127
+ #
128
+ # field_names...<Symbol>:: fields for which to return field facets
129
+ #
130
+ def facet(*field_names)
131
+ for field_name in field_names
132
+ @query.add_field_facet(field_name)
133
+ end
22
134
  end
23
135
  end
24
136
  end
data/lib/sunspot/dsl.rb CHANGED
@@ -1,3 +1,3 @@
1
- %w(fields query scope).each do |file|
1
+ %w(fields scope query restriction).each do |file|
2
2
  require File.join(File.dirname(__FILE__), 'dsl', file)
3
3
  end