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 +26 -1
- data/LICENSE +18 -0
- data/README.rdoc +31 -25
- data/Rakefile +1 -1
- data/TODO +1 -5
- data/VERSION.yml +2 -2
- data/lib/sunspot/adapters.rb +28 -11
- data/lib/sunspot/data_extractor.rb +37 -0
- data/lib/sunspot/dsl/fields.rb +6 -17
- data/lib/sunspot/dsl/query.rb +22 -120
- data/lib/sunspot/dsl/restriction.rb +25 -0
- data/lib/sunspot/dsl/scope.rb +127 -15
- data/lib/sunspot/dsl.rb +1 -1
- data/lib/sunspot/field.rb +151 -81
- data/lib/sunspot/indexer.rb +1 -1
- data/lib/sunspot/query/dynamic_query.rb +86 -0
- data/lib/sunspot/{facets.rb → query/field_facet.rb} +1 -1
- data/lib/sunspot/query/pagination.rb +39 -0
- data/lib/sunspot/query/restriction.rb +223 -0
- data/lib/sunspot/query/sort.rb +33 -0
- data/lib/sunspot/query.rb +217 -117
- data/lib/sunspot/search.rb +43 -5
- data/lib/sunspot/session.rb +20 -5
- data/lib/sunspot/setup.rb +39 -10
- data/lib/sunspot/type.rb +15 -7
- data/lib/sunspot/util.rb +17 -0
- data/lib/sunspot.rb +83 -2
- data/spec/api/build_search_spec.rb +120 -2
- data/spec/api/indexer_spec.rb +44 -0
- data/spec/api/query_spec.rb +129 -0
- data/spec/api/search_retrieval_spec.rb +6 -0
- data/spec/integration/dynamic_fields_spec.rb +55 -0
- data/spec/mocks/post.rb +27 -1
- data/spec/spec_helper.rb +10 -3
- data/tasks/gemspec.rake +6 -1
- data/tasks/rdoc.rake +14 -1
- metadata +79 -56
- data/lib/sunspot/restriction.rb +0 -216
data/History.txt
CHANGED
|
@@ -1,6 +1,31 @@
|
|
|
1
|
-
== 0.0
|
|
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
|
-
=
|
|
1
|
+
= Sunspot
|
|
2
2
|
|
|
3
|
-
http://github.com/
|
|
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
|
|
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
|
-
|
|
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
data/TODO
CHANGED
data/VERSION.yml
CHANGED
data/lib/sunspot/adapters.rb
CHANGED
|
@@ -1,15 +1,32 @@
|
|
|
1
1
|
module Sunspot
|
|
2
|
-
#
|
|
3
|
-
#
|
|
4
|
-
#
|
|
5
|
-
#
|
|
6
|
-
#
|
|
7
|
-
#
|
|
8
|
-
#
|
|
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
|
-
#
|
|
11
|
-
#
|
|
12
|
-
#
|
|
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
|
data/lib/sunspot/dsl/fields.rb
CHANGED
|
@@ -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(
|
|
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
|
-
|
|
52
|
-
|
|
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::
|
|
54
|
+
@setup.add_fields(Field::StaticField.build(name, type, *args, &block))
|
|
66
55
|
end
|
|
67
56
|
end
|
|
68
57
|
end
|
data/lib/sunspot/dsl/query.rb
CHANGED
|
@@ -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
|
-
#
|
|
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
|
-
#
|
|
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
|
-
|
|
169
|
-
|
|
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
|
data/lib/sunspot/dsl/scope.rb
CHANGED
|
@@ -1,24 +1,136 @@
|
|
|
1
1
|
module Sunspot
|
|
2
|
-
module DSL
|
|
2
|
+
module DSL #:nodoc:
|
|
3
3
|
#
|
|
4
|
-
# This
|
|
5
|
-
#
|
|
6
|
-
#
|
|
7
|
-
#
|
|
8
|
-
#
|
|
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
|
|
11
|
-
|
|
12
|
-
|
|
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
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
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
|
-
|
|
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