sunspot 0.9.7
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 +83 -0
- data/LICENSE +18 -0
- data/README.rdoc +154 -0
- data/Rakefile +9 -0
- data/TODO +9 -0
- data/VERSION.yml +4 -0
- data/bin/sunspot-configure-solr +46 -0
- data/bin/sunspot-solr +62 -0
- data/lib/light_config.rb +40 -0
- data/lib/sunspot.rb +469 -0
- data/lib/sunspot/adapters.rb +265 -0
- data/lib/sunspot/composite_setup.rb +186 -0
- data/lib/sunspot/configuration.rb +38 -0
- data/lib/sunspot/data_extractor.rb +47 -0
- data/lib/sunspot/dsl.rb +3 -0
- data/lib/sunspot/dsl/field_query.rb +72 -0
- data/lib/sunspot/dsl/fields.rb +86 -0
- data/lib/sunspot/dsl/query.rb +59 -0
- data/lib/sunspot/dsl/query_facet.rb +31 -0
- data/lib/sunspot/dsl/restriction.rb +25 -0
- data/lib/sunspot/dsl/scope.rb +193 -0
- data/lib/sunspot/dsl/search.rb +30 -0
- data/lib/sunspot/facet.rb +16 -0
- data/lib/sunspot/facet_data.rb +120 -0
- data/lib/sunspot/facet_row.rb +10 -0
- data/lib/sunspot/field.rb +157 -0
- data/lib/sunspot/field_factory.rb +126 -0
- data/lib/sunspot/indexer.rb +123 -0
- data/lib/sunspot/instantiated_facet.rb +42 -0
- data/lib/sunspot/instantiated_facet_row.rb +22 -0
- data/lib/sunspot/query.rb +191 -0
- data/lib/sunspot/query/base_query.rb +90 -0
- data/lib/sunspot/query/connective.rb +126 -0
- data/lib/sunspot/query/dynamic_query.rb +69 -0
- data/lib/sunspot/query/field_facet.rb +151 -0
- data/lib/sunspot/query/field_query.rb +63 -0
- data/lib/sunspot/query/pagination.rb +39 -0
- data/lib/sunspot/query/query_facet.rb +73 -0
- data/lib/sunspot/query/query_facet_row.rb +19 -0
- data/lib/sunspot/query/query_field_facet.rb +13 -0
- data/lib/sunspot/query/restriction.rb +233 -0
- data/lib/sunspot/query/scope.rb +165 -0
- data/lib/sunspot/query/sort.rb +36 -0
- data/lib/sunspot/query/sort_composite.rb +33 -0
- data/lib/sunspot/schema.rb +165 -0
- data/lib/sunspot/search.rb +219 -0
- data/lib/sunspot/search/hit.rb +66 -0
- data/lib/sunspot/session.rb +201 -0
- data/lib/sunspot/setup.rb +271 -0
- data/lib/sunspot/type.rb +200 -0
- data/lib/sunspot/util.rb +164 -0
- data/solr/etc/jetty.xml +212 -0
- data/solr/etc/webdefault.xml +379 -0
- data/solr/lib/jetty-6.1.3.jar +0 -0
- data/solr/lib/jetty-util-6.1.3.jar +0 -0
- data/solr/lib/jsp-2.1/ant-1.6.5.jar +0 -0
- data/solr/lib/jsp-2.1/core-3.1.1.jar +0 -0
- data/solr/lib/jsp-2.1/jsp-2.1.jar +0 -0
- data/solr/lib/jsp-2.1/jsp-api-2.1.jar +0 -0
- data/solr/lib/servlet-api-2.5-6.1.3.jar +0 -0
- data/solr/solr/conf/elevate.xml +36 -0
- data/solr/solr/conf/protwords.txt +21 -0
- data/solr/solr/conf/schema.xml +50 -0
- data/solr/solr/conf/solrconfig.xml +696 -0
- data/solr/solr/conf/stopwords.txt +57 -0
- data/solr/solr/conf/synonyms.txt +31 -0
- data/solr/start.jar +0 -0
- data/solr/webapps/solr.war +0 -0
- data/spec/api/adapters_spec.rb +33 -0
- data/spec/api/build_search_spec.rb +1039 -0
- data/spec/api/indexer_spec.rb +311 -0
- data/spec/api/query_spec.rb +153 -0
- data/spec/api/search_retrieval_spec.rb +362 -0
- data/spec/api/session_spec.rb +157 -0
- data/spec/api/spec_helper.rb +1 -0
- data/spec/api/sunspot_spec.rb +18 -0
- data/spec/integration/dynamic_fields_spec.rb +55 -0
- data/spec/integration/faceting_spec.rb +169 -0
- data/spec/integration/keyword_search_spec.rb +83 -0
- data/spec/integration/scoped_search_spec.rb +289 -0
- data/spec/integration/spec_helper.rb +1 -0
- data/spec/integration/stored_fields_spec.rb +10 -0
- data/spec/integration/test_pagination.rb +32 -0
- data/spec/mocks/adapters.rb +32 -0
- data/spec/mocks/blog.rb +3 -0
- data/spec/mocks/comment.rb +19 -0
- data/spec/mocks/connection.rb +84 -0
- data/spec/mocks/mock_adapter.rb +30 -0
- data/spec/mocks/mock_record.rb +48 -0
- data/spec/mocks/photo.rb +8 -0
- data/spec/mocks/post.rb +73 -0
- data/spec/mocks/user.rb +8 -0
- data/spec/spec_helper.rb +47 -0
- data/tasks/gemspec.rake +25 -0
- data/tasks/rcov.rake +28 -0
- data/tasks/rdoc.rake +22 -0
- data/tasks/schema.rake +19 -0
- data/tasks/spec.rake +24 -0
- data/tasks/todo.rake +4 -0
- data/templates/schema.xml.haml +24 -0
- metadata +246 -0
@@ -0,0 +1,38 @@
|
|
1
|
+
module Sunspot
|
2
|
+
# The Sunspot::Configuration module provides a factory method for Sunspot
|
3
|
+
# configuration objects. Available properties are:
|
4
|
+
#
|
5
|
+
# Sunspot.config.http_client::
|
6
|
+
# The client to use for HTTP communication with Solr. Available options are
|
7
|
+
# :net_http, which is the default and uses Ruby's built-in pure-Ruby HTTP
|
8
|
+
# library; and :curb, which uses Ruby's libcurl bindings and requires
|
9
|
+
# installation of the 'curb' gem.
|
10
|
+
# Sunspot.config.solr.url::
|
11
|
+
# The URL at which to connect to Solr
|
12
|
+
# (default: 'http://localhost:8983/solr')
|
13
|
+
# Sunspot.config.pagination.default_per_page::
|
14
|
+
# Solr always paginates its results. This sets Sunspot's default result
|
15
|
+
# count per page if it is not explicitly specified in the query.
|
16
|
+
#
|
17
|
+
module Configuration
|
18
|
+
class <<self
|
19
|
+
# Factory method to build configuration instances.
|
20
|
+
#
|
21
|
+
# ==== Returns
|
22
|
+
#
|
23
|
+
# LightConfig::Configuration:: new configuration instance with defaults
|
24
|
+
#
|
25
|
+
def build #:nodoc:
|
26
|
+
LightConfig.build do
|
27
|
+
http_client :net_http
|
28
|
+
solr do
|
29
|
+
url 'http://127.0.0.1:8983/solr'
|
30
|
+
end
|
31
|
+
pagination do
|
32
|
+
default_per_page 30
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,47 @@
|
|
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
|
+
|
37
|
+
class Constant
|
38
|
+
def initialize(value)
|
39
|
+
@value = value
|
40
|
+
end
|
41
|
+
|
42
|
+
def value_for(object)
|
43
|
+
@value
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
data/lib/sunspot/dsl.rb
ADDED
@@ -0,0 +1,72 @@
|
|
1
|
+
module Sunspot
|
2
|
+
module DSL
|
3
|
+
#
|
4
|
+
# Provides an API for areas of the query DSL that operate on specific
|
5
|
+
# fields. This functionality is provided by the query DSL and the dynamic
|
6
|
+
# query DSL.
|
7
|
+
#
|
8
|
+
class FieldQuery < Scope
|
9
|
+
# Specify the order that results should be returned in. This method can
|
10
|
+
# be called multiple times; precedence will be in the order given.
|
11
|
+
#
|
12
|
+
# ==== Parameters
|
13
|
+
#
|
14
|
+
# field_name<Symbol>:: the field to use for ordering
|
15
|
+
# direction<Symbol>:: :asc or :desc (default :asc)
|
16
|
+
#
|
17
|
+
def order_by(field_name, direction = nil)
|
18
|
+
@query.order_by(field_name, direction)
|
19
|
+
end
|
20
|
+
|
21
|
+
#
|
22
|
+
# Order results randomly. This will (generally) return the results in a
|
23
|
+
# different order each time a search is called.
|
24
|
+
#
|
25
|
+
def order_by_random
|
26
|
+
@query.order_by_random
|
27
|
+
end
|
28
|
+
|
29
|
+
# Request facets on the given field names. If the last argument is a hash,
|
30
|
+
# the given options will be applied to all specified fields. See
|
31
|
+
# Sunspot::Search#facet and Sunspot::Facet for information on what is
|
32
|
+
# returned.
|
33
|
+
#
|
34
|
+
# ==== Parameters
|
35
|
+
#
|
36
|
+
# field_names...<Symbol>:: fields for which to return field facets
|
37
|
+
#
|
38
|
+
# ==== Options
|
39
|
+
#
|
40
|
+
# :sort<Symbol>::
|
41
|
+
# Either :count (values matching the most terms first) or :index (lexical)
|
42
|
+
# :limit<Integer>::
|
43
|
+
# The maximum number of facet rows to return
|
44
|
+
# :minimum_count<Integer>::
|
45
|
+
# The minimum count a facet row must have to be returned
|
46
|
+
# :zeros<Boolean>::
|
47
|
+
# Return facet rows for which there are no matches (equivalent to
|
48
|
+
# :minimum_count => 0). Default is false.
|
49
|
+
#
|
50
|
+
def facet(*field_names, &block)
|
51
|
+
if block
|
52
|
+
if field_names.length != 1
|
53
|
+
raise(
|
54
|
+
ArgumentError,
|
55
|
+
"wrong number of arguments (#{field_names.length} for 1)"
|
56
|
+
)
|
57
|
+
end
|
58
|
+
name = field_names.first
|
59
|
+
DSL::QueryFacet.new(@query.add_query_facet(name)).instance_eval(&block)
|
60
|
+
else
|
61
|
+
options =
|
62
|
+
if field_names.last.is_a?(Hash)
|
63
|
+
field_names.pop
|
64
|
+
end
|
65
|
+
for field_name in field_names
|
66
|
+
@query.add_field_facet(field_name, options)
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
@@ -0,0 +1,86 @@
|
|
1
|
+
module Sunspot
|
2
|
+
module DSL #:nodoc:
|
3
|
+
# The Fields class provides a DSL for specifying field definitions in the
|
4
|
+
# Sunspot.setup block. As well as the #text method, which creates fulltext
|
5
|
+
# fields, uses #method_missing to allow definition of typed fields. The
|
6
|
+
# available methods are determined by the constants defined in
|
7
|
+
# Sunspot::Type - in theory (though this is untested), plugin developers
|
8
|
+
# should be able to add support for new types simply by creating new
|
9
|
+
# implementations in Sunspot::Type
|
10
|
+
#
|
11
|
+
class Fields
|
12
|
+
def initialize(setup) #:nodoc:
|
13
|
+
@setup = setup
|
14
|
+
end
|
15
|
+
|
16
|
+
# Add a text field. Text fields are tokenized before indexing and are
|
17
|
+
# the only fields searched in fulltext searches. If a block is passed,
|
18
|
+
# create a virtual field; otherwise create an attribute field.
|
19
|
+
#
|
20
|
+
# If options are passed, they will be applied to all the given fields.
|
21
|
+
#
|
22
|
+
# ==== Parameters
|
23
|
+
#
|
24
|
+
# names...<Symbol>:: One or more field names
|
25
|
+
#
|
26
|
+
# ==== Options
|
27
|
+
#
|
28
|
+
# :boost<Float>::
|
29
|
+
# Boost that should be applied to this field for keyword search
|
30
|
+
#
|
31
|
+
def text(*names, &block)
|
32
|
+
options = names.pop if names.last.is_a?(Hash)
|
33
|
+
for name in names
|
34
|
+
@setup.add_text_field_factory(
|
35
|
+
name,
|
36
|
+
options || {},
|
37
|
+
&block
|
38
|
+
)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
#
|
43
|
+
# Specify a document-level boost. As with fields, you have the option of
|
44
|
+
# passing an attribute name which will be called on each model, or a block
|
45
|
+
# to be evaluated in the model's context. As well as these two options,
|
46
|
+
# this method can also take a constant number, meaning that all indexed
|
47
|
+
# documents of this class will have the specified boost.
|
48
|
+
#
|
49
|
+
# ==== Parameters
|
50
|
+
#
|
51
|
+
# attr_name<Symbol,~.to_f>:: Attribute name to call or a numeric constant
|
52
|
+
#
|
53
|
+
def boost(attr_name = nil, &block)
|
54
|
+
@setup.add_document_boost(attr_name, &block)
|
55
|
+
end
|
56
|
+
|
57
|
+
# method_missing is used to provide access to typed fields, because
|
58
|
+
# developers should be able to add new Sunspot::Type implementations
|
59
|
+
# dynamically and have them recognized inside the Fields DSL. Like #text,
|
60
|
+
# these methods will create a VirtualField if a block is passed, or an
|
61
|
+
# AttributeField if not.
|
62
|
+
#
|
63
|
+
# ==== Example
|
64
|
+
#
|
65
|
+
# Sunspot.setup(File) do
|
66
|
+
# time :mtime
|
67
|
+
# end
|
68
|
+
#
|
69
|
+
# The call to +time+ will create a field of type Sunspot::Types::TimeType
|
70
|
+
#
|
71
|
+
def method_missing(method, *args, &block)
|
72
|
+
begin
|
73
|
+
type = Type.const_get("#{Util.camel_case(method.to_s.sub(/^dynamic_/, ''))}Type")
|
74
|
+
rescue(NameError)
|
75
|
+
super(method.to_sym, *args, &block) and return
|
76
|
+
end
|
77
|
+
name = args.shift
|
78
|
+
if method.to_s =~ /^dynamic_/
|
79
|
+
@setup.add_dynamic_field_factory(name, type, *args, &block)
|
80
|
+
else
|
81
|
+
@setup.add_field_factory(name, type, *args, &block)
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
module Sunspot
|
2
|
+
module DSL #:nodoc:
|
3
|
+
#
|
4
|
+
# This class presents a DSL for constructing queries using the
|
5
|
+
# Sunspot.search method. Methods of this class are available inside the
|
6
|
+
# search block. Much of the DSL's functionality is implemented by this
|
7
|
+
# class's superclasses, Sunspot::DSL::FieldQuery and Sunspot::DSL::Scope
|
8
|
+
#
|
9
|
+
# See Sunspot.search for usage examples
|
10
|
+
#
|
11
|
+
class Query < FieldQuery
|
12
|
+
# Specify a phrase that should be searched as fulltext. Only +text+
|
13
|
+
# fields are searched - see DSL::Fields.text
|
14
|
+
#
|
15
|
+
# Keyword search is executed using Solr's dismax handler, which strikes
|
16
|
+
# a good balance between powerful and foolproof. In particular,
|
17
|
+
# well-matched quotation marks can be used to group phrases, and the
|
18
|
+
# + and - modifiers work as expected. All other special Solr boolean
|
19
|
+
# syntax is escaped, and mismatched quotes are ignored entirely.
|
20
|
+
#
|
21
|
+
# ==== Parameters
|
22
|
+
#
|
23
|
+
# keywords<String>:: phrase to perform fulltext search on
|
24
|
+
#
|
25
|
+
# ==== Options
|
26
|
+
#
|
27
|
+
# :fields<Array>::
|
28
|
+
# List of fields that should be searched for keywords. Defaults to all
|
29
|
+
# fields configured for the types under search.
|
30
|
+
#
|
31
|
+
def keywords(keywords, options = {})
|
32
|
+
@query.set_keywords(keywords, options)
|
33
|
+
end
|
34
|
+
|
35
|
+
# Paginate your search. This works the same way as WillPaginate's
|
36
|
+
# paginate().
|
37
|
+
#
|
38
|
+
# Note that Solr searches are _always_ paginated. Not calling #paginate is
|
39
|
+
# the equivalent of calling:
|
40
|
+
#
|
41
|
+
# paginate(:page => 1, :per_page => Sunspot.config.pagination.default_per_page)
|
42
|
+
#
|
43
|
+
# ==== Options (options)
|
44
|
+
#
|
45
|
+
# :page<Integer>:: The requested page (required)
|
46
|
+
#
|
47
|
+
# :per_page<Integer>::
|
48
|
+
# How many results to return per page. The default is the value in
|
49
|
+
# +Sunspot.config.pagination.default_per_page+
|
50
|
+
#
|
51
|
+
def paginate(options = {})
|
52
|
+
page = options.delete(:page) || raise(ArgumentError, "paginate requires a :page argument")
|
53
|
+
per_page = options.delete(:per_page)
|
54
|
+
raise ArgumentError, "unknown argument #{options.keys.first.inspect} passed to paginate" unless options.empty?
|
55
|
+
@query.paginate(page, per_page)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
module Sunspot
|
2
|
+
module DSL
|
3
|
+
#
|
4
|
+
# This tiny DSL class implements the DSL for the FieldQuery.facet
|
5
|
+
# method.
|
6
|
+
#
|
7
|
+
class QueryFacet
|
8
|
+
def initialize(query_facet) #:nodoc:
|
9
|
+
@query_facet = query_facet
|
10
|
+
end
|
11
|
+
|
12
|
+
#
|
13
|
+
# Add a row to this query facet. The label argument can be anything; it's
|
14
|
+
# simply the value that's passed into the Sunspot::QueryFacetRow object
|
15
|
+
# corresponding to the row that's created. Use whatever seems most
|
16
|
+
# intuitive.
|
17
|
+
#
|
18
|
+
# The block is evaluated in the context of a Sunspot::DSL::Scope, meaning
|
19
|
+
# any restrictions can be placed on the documents matching this facet row.
|
20
|
+
#
|
21
|
+
# ==== Parameters
|
22
|
+
#
|
23
|
+
# label<Object>::
|
24
|
+
# An object used to identify this facet row in the results.
|
25
|
+
#
|
26
|
+
def row(label, &block)
|
27
|
+
Scope.new(@query_facet.add_row(label)).instance_eval(&block)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
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
|
@@ -0,0 +1,193 @@
|
|
1
|
+
module Sunspot
|
2
|
+
module DSL #:nodoc:
|
3
|
+
#
|
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
|
+
#
|
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, 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
|
26
|
+
#
|
27
|
+
# field_name<Symbol>:: Name of the field on which to place the restriction
|
28
|
+
# value<Object,Range,Array>::
|
29
|
+
# If passed, creates an equality, range, or any-of restriction based on
|
30
|
+
# the type of value passed.
|
31
|
+
#
|
32
|
+
# ==== Returns
|
33
|
+
#
|
34
|
+
# Sunspot::DSL::Query::Restriction::
|
35
|
+
# Restriction DSL object (if only one argument is passed)
|
36
|
+
#
|
37
|
+
# ==== Examples
|
38
|
+
#
|
39
|
+
# An equality restriction:
|
40
|
+
#
|
41
|
+
# Sunspot.search do
|
42
|
+
# with(:blog_id, 1)
|
43
|
+
# end
|
44
|
+
#
|
45
|
+
# Restrict by range:
|
46
|
+
#
|
47
|
+
# Sunspot.search do
|
48
|
+
# with(:average_rating, 3.0..5.0)
|
49
|
+
# end
|
50
|
+
#
|
51
|
+
# Restrict by a set of allowed values:
|
52
|
+
#
|
53
|
+
# Sunspot.search do
|
54
|
+
# with(:category_ids, [1, 5, 9])
|
55
|
+
# end
|
56
|
+
#
|
57
|
+
# Other restriction types:
|
58
|
+
#
|
59
|
+
# Sunspot.search(Post) do
|
60
|
+
# with(:average_rating).greater_than(3.0)
|
61
|
+
# end
|
62
|
+
#
|
63
|
+
def with(field_name, value = NONE)
|
64
|
+
if value == NONE
|
65
|
+
DSL::Restriction.new(field_name.to_sym, @query, false)
|
66
|
+
else
|
67
|
+
@query.add_shorthand_restriction(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:
|
97
|
+
#
|
98
|
+
# Sunspot.search(Post) do
|
99
|
+
# without(:average_rating).greater_than(3.0)
|
100
|
+
# end
|
101
|
+
#
|
102
|
+
# Exclusion by identity:
|
103
|
+
#
|
104
|
+
# Sunspot.search(Post) do
|
105
|
+
# without(some_post_instance)
|
106
|
+
# end
|
107
|
+
#
|
108
|
+
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(field_name.to_sym, @query, true)
|
115
|
+
else
|
116
|
+
@query.add_negated_shorthand_restriction(field_name, value)
|
117
|
+
end
|
118
|
+
else
|
119
|
+
instances = args
|
120
|
+
for instance in instances.flatten
|
121
|
+
@query.exclude_instance(instance)
|
122
|
+
end
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
#
|
127
|
+
# Create a disjunction, scoping the results to documents that match any
|
128
|
+
# of the enclosed restrictions.
|
129
|
+
#
|
130
|
+
# ==== Example
|
131
|
+
#
|
132
|
+
# Sunspot.search(Post) do
|
133
|
+
# any_of do
|
134
|
+
# with(:expired_at).greater_than Time.now
|
135
|
+
# with :expired_at, nil
|
136
|
+
# end
|
137
|
+
# end
|
138
|
+
#
|
139
|
+
# This will return all documents who either have an expiration time in the
|
140
|
+
# future, or who do not have any expiration time at all.
|
141
|
+
#
|
142
|
+
def any_of(&block)
|
143
|
+
Util.instance_eval_or_call(Scope.new(@query.add_disjunction), &block)
|
144
|
+
end
|
145
|
+
|
146
|
+
#
|
147
|
+
# Create a conjunction, scoping the results to documents that match all of
|
148
|
+
# the enclosed restrictions. When called from the top level of a search
|
149
|
+
# block, this has no effect, but can be useful for grouping a conjunction
|
150
|
+
# inside a disjunction.
|
151
|
+
#
|
152
|
+
# ==== Example
|
153
|
+
#
|
154
|
+
# Sunspot.search(Post) do
|
155
|
+
# any_of do
|
156
|
+
# with(:blog_id, 1)
|
157
|
+
# all_of do
|
158
|
+
# with(:blog_id, 2)
|
159
|
+
# with(:category_ids, 3)
|
160
|
+
# end
|
161
|
+
# end
|
162
|
+
# end
|
163
|
+
#
|
164
|
+
def all_of(&block)
|
165
|
+
Util.instance_eval_or_call(Scope.new(@query.add_conjunction), &block)
|
166
|
+
end
|
167
|
+
|
168
|
+
#
|
169
|
+
# Apply restrictions, facets, and ordering to dynamic field instances.
|
170
|
+
# The block API is implemented by Sunspot::DSL::FieldQuery, which is a
|
171
|
+
# superclass of the Query DSL (thus providing a subset of the API, in
|
172
|
+
# particular only methods that refer to particular fields).
|
173
|
+
#
|
174
|
+
# ==== Parameters
|
175
|
+
#
|
176
|
+
# base_name<Symbol>:: The base name for the dynamic field definition
|
177
|
+
#
|
178
|
+
# ==== Example
|
179
|
+
#
|
180
|
+
# Sunspot.search Post do
|
181
|
+
# dynamic :custom do
|
182
|
+
# with :cuisine, 'Pizza'
|
183
|
+
# facet :atmosphere
|
184
|
+
# order_by :chef_name
|
185
|
+
# end
|
186
|
+
# end
|
187
|
+
#
|
188
|
+
def dynamic(base_name, &block)
|
189
|
+
FieldQuery.new(@query.dynamic_query(base_name)).instance_eval(&block)
|
190
|
+
end
|
191
|
+
end
|
192
|
+
end
|
193
|
+
end
|