sunspot 0.9.7

Sign up to get free protection for your applications and to get access to all the features.
Files changed (101) hide show
  1. data/History.txt +83 -0
  2. data/LICENSE +18 -0
  3. data/README.rdoc +154 -0
  4. data/Rakefile +9 -0
  5. data/TODO +9 -0
  6. data/VERSION.yml +4 -0
  7. data/bin/sunspot-configure-solr +46 -0
  8. data/bin/sunspot-solr +62 -0
  9. data/lib/light_config.rb +40 -0
  10. data/lib/sunspot.rb +469 -0
  11. data/lib/sunspot/adapters.rb +265 -0
  12. data/lib/sunspot/composite_setup.rb +186 -0
  13. data/lib/sunspot/configuration.rb +38 -0
  14. data/lib/sunspot/data_extractor.rb +47 -0
  15. data/lib/sunspot/dsl.rb +3 -0
  16. data/lib/sunspot/dsl/field_query.rb +72 -0
  17. data/lib/sunspot/dsl/fields.rb +86 -0
  18. data/lib/sunspot/dsl/query.rb +59 -0
  19. data/lib/sunspot/dsl/query_facet.rb +31 -0
  20. data/lib/sunspot/dsl/restriction.rb +25 -0
  21. data/lib/sunspot/dsl/scope.rb +193 -0
  22. data/lib/sunspot/dsl/search.rb +30 -0
  23. data/lib/sunspot/facet.rb +16 -0
  24. data/lib/sunspot/facet_data.rb +120 -0
  25. data/lib/sunspot/facet_row.rb +10 -0
  26. data/lib/sunspot/field.rb +157 -0
  27. data/lib/sunspot/field_factory.rb +126 -0
  28. data/lib/sunspot/indexer.rb +123 -0
  29. data/lib/sunspot/instantiated_facet.rb +42 -0
  30. data/lib/sunspot/instantiated_facet_row.rb +22 -0
  31. data/lib/sunspot/query.rb +191 -0
  32. data/lib/sunspot/query/base_query.rb +90 -0
  33. data/lib/sunspot/query/connective.rb +126 -0
  34. data/lib/sunspot/query/dynamic_query.rb +69 -0
  35. data/lib/sunspot/query/field_facet.rb +151 -0
  36. data/lib/sunspot/query/field_query.rb +63 -0
  37. data/lib/sunspot/query/pagination.rb +39 -0
  38. data/lib/sunspot/query/query_facet.rb +73 -0
  39. data/lib/sunspot/query/query_facet_row.rb +19 -0
  40. data/lib/sunspot/query/query_field_facet.rb +13 -0
  41. data/lib/sunspot/query/restriction.rb +233 -0
  42. data/lib/sunspot/query/scope.rb +165 -0
  43. data/lib/sunspot/query/sort.rb +36 -0
  44. data/lib/sunspot/query/sort_composite.rb +33 -0
  45. data/lib/sunspot/schema.rb +165 -0
  46. data/lib/sunspot/search.rb +219 -0
  47. data/lib/sunspot/search/hit.rb +66 -0
  48. data/lib/sunspot/session.rb +201 -0
  49. data/lib/sunspot/setup.rb +271 -0
  50. data/lib/sunspot/type.rb +200 -0
  51. data/lib/sunspot/util.rb +164 -0
  52. data/solr/etc/jetty.xml +212 -0
  53. data/solr/etc/webdefault.xml +379 -0
  54. data/solr/lib/jetty-6.1.3.jar +0 -0
  55. data/solr/lib/jetty-util-6.1.3.jar +0 -0
  56. data/solr/lib/jsp-2.1/ant-1.6.5.jar +0 -0
  57. data/solr/lib/jsp-2.1/core-3.1.1.jar +0 -0
  58. data/solr/lib/jsp-2.1/jsp-2.1.jar +0 -0
  59. data/solr/lib/jsp-2.1/jsp-api-2.1.jar +0 -0
  60. data/solr/lib/servlet-api-2.5-6.1.3.jar +0 -0
  61. data/solr/solr/conf/elevate.xml +36 -0
  62. data/solr/solr/conf/protwords.txt +21 -0
  63. data/solr/solr/conf/schema.xml +50 -0
  64. data/solr/solr/conf/solrconfig.xml +696 -0
  65. data/solr/solr/conf/stopwords.txt +57 -0
  66. data/solr/solr/conf/synonyms.txt +31 -0
  67. data/solr/start.jar +0 -0
  68. data/solr/webapps/solr.war +0 -0
  69. data/spec/api/adapters_spec.rb +33 -0
  70. data/spec/api/build_search_spec.rb +1039 -0
  71. data/spec/api/indexer_spec.rb +311 -0
  72. data/spec/api/query_spec.rb +153 -0
  73. data/spec/api/search_retrieval_spec.rb +362 -0
  74. data/spec/api/session_spec.rb +157 -0
  75. data/spec/api/spec_helper.rb +1 -0
  76. data/spec/api/sunspot_spec.rb +18 -0
  77. data/spec/integration/dynamic_fields_spec.rb +55 -0
  78. data/spec/integration/faceting_spec.rb +169 -0
  79. data/spec/integration/keyword_search_spec.rb +83 -0
  80. data/spec/integration/scoped_search_spec.rb +289 -0
  81. data/spec/integration/spec_helper.rb +1 -0
  82. data/spec/integration/stored_fields_spec.rb +10 -0
  83. data/spec/integration/test_pagination.rb +32 -0
  84. data/spec/mocks/adapters.rb +32 -0
  85. data/spec/mocks/blog.rb +3 -0
  86. data/spec/mocks/comment.rb +19 -0
  87. data/spec/mocks/connection.rb +84 -0
  88. data/spec/mocks/mock_adapter.rb +30 -0
  89. data/spec/mocks/mock_record.rb +48 -0
  90. data/spec/mocks/photo.rb +8 -0
  91. data/spec/mocks/post.rb +73 -0
  92. data/spec/mocks/user.rb +8 -0
  93. data/spec/spec_helper.rb +47 -0
  94. data/tasks/gemspec.rake +25 -0
  95. data/tasks/rcov.rake +28 -0
  96. data/tasks/rdoc.rake +22 -0
  97. data/tasks/schema.rake +19 -0
  98. data/tasks/spec.rake +24 -0
  99. data/tasks/todo.rake +4 -0
  100. data/templates/schema.xml.haml +24 -0
  101. 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
@@ -0,0 +1,3 @@
1
+ %w(fields scope field_query query query_facet restriction search).each do |file|
2
+ require File.join(File.dirname(__FILE__), 'dsl', file)
3
+ end
@@ -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