outoftime-sunspot 0.7.3 → 0.8.0

Sign up to get free protection for your applications and to get access to all the features.
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