pduey-sunspot 1.2.1.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (166) hide show
  1. data/.gitignore +12 -0
  2. data/Gemfile +5 -0
  3. data/History.txt +225 -0
  4. data/LICENSE +18 -0
  5. data/Rakefile +15 -0
  6. data/TODO +13 -0
  7. data/VERSION.yml +4 -0
  8. data/bin/sunspot-installer +19 -0
  9. data/installer/config/schema.yml +95 -0
  10. data/lib/light_config.rb +40 -0
  11. data/lib/sunspot.rb +568 -0
  12. data/lib/sunspot/adapters.rb +265 -0
  13. data/lib/sunspot/composite_setup.rb +202 -0
  14. data/lib/sunspot/configuration.rb +46 -0
  15. data/lib/sunspot/data_extractor.rb +50 -0
  16. data/lib/sunspot/dsl.rb +5 -0
  17. data/lib/sunspot/dsl/adjustable.rb +47 -0
  18. data/lib/sunspot/dsl/field_query.rb +279 -0
  19. data/lib/sunspot/dsl/fields.rb +103 -0
  20. data/lib/sunspot/dsl/fulltext.rb +243 -0
  21. data/lib/sunspot/dsl/function.rb +14 -0
  22. data/lib/sunspot/dsl/functional.rb +44 -0
  23. data/lib/sunspot/dsl/more_like_this_query.rb +56 -0
  24. data/lib/sunspot/dsl/paginatable.rb +28 -0
  25. data/lib/sunspot/dsl/query_facet.rb +36 -0
  26. data/lib/sunspot/dsl/restriction.rb +25 -0
  27. data/lib/sunspot/dsl/restriction_with_near.rb +121 -0
  28. data/lib/sunspot/dsl/scope.rb +217 -0
  29. data/lib/sunspot/dsl/search.rb +30 -0
  30. data/lib/sunspot/dsl/standard_query.rb +121 -0
  31. data/lib/sunspot/field.rb +193 -0
  32. data/lib/sunspot/field_factory.rb +129 -0
  33. data/lib/sunspot/indexer.rb +131 -0
  34. data/lib/sunspot/installer.rb +31 -0
  35. data/lib/sunspot/installer/library_installer.rb +45 -0
  36. data/lib/sunspot/installer/schema_builder.rb +219 -0
  37. data/lib/sunspot/installer/solrconfig_updater.rb +76 -0
  38. data/lib/sunspot/installer/task_helper.rb +18 -0
  39. data/lib/sunspot/java.rb +8 -0
  40. data/lib/sunspot/query.rb +11 -0
  41. data/lib/sunspot/query/abstract_field_facet.rb +52 -0
  42. data/lib/sunspot/query/boost_query.rb +24 -0
  43. data/lib/sunspot/query/common_query.rb +85 -0
  44. data/lib/sunspot/query/composite_fulltext.rb +36 -0
  45. data/lib/sunspot/query/connective.rb +206 -0
  46. data/lib/sunspot/query/date_field_facet.rb +14 -0
  47. data/lib/sunspot/query/dismax.rb +128 -0
  48. data/lib/sunspot/query/field_facet.rb +41 -0
  49. data/lib/sunspot/query/filter.rb +38 -0
  50. data/lib/sunspot/query/function_query.rb +52 -0
  51. data/lib/sunspot/query/geo.rb +53 -0
  52. data/lib/sunspot/query/highlighting.rb +55 -0
  53. data/lib/sunspot/query/more_like_this.rb +61 -0
  54. data/lib/sunspot/query/more_like_this_query.rb +12 -0
  55. data/lib/sunspot/query/pagination.rb +38 -0
  56. data/lib/sunspot/query/query_facet.rb +16 -0
  57. data/lib/sunspot/query/restriction.rb +262 -0
  58. data/lib/sunspot/query/scope.rb +9 -0
  59. data/lib/sunspot/query/sort.rb +95 -0
  60. data/lib/sunspot/query/sort_composite.rb +33 -0
  61. data/lib/sunspot/query/standard_query.rb +16 -0
  62. data/lib/sunspot/query/text_field_boost.rb +17 -0
  63. data/lib/sunspot/schema.rb +151 -0
  64. data/lib/sunspot/search.rb +9 -0
  65. data/lib/sunspot/search/abstract_search.rb +335 -0
  66. data/lib/sunspot/search/date_facet.rb +35 -0
  67. data/lib/sunspot/search/facet_row.rb +27 -0
  68. data/lib/sunspot/search/field_facet.rb +88 -0
  69. data/lib/sunspot/search/highlight.rb +38 -0
  70. data/lib/sunspot/search/hit.rb +150 -0
  71. data/lib/sunspot/search/more_like_this_search.rb +31 -0
  72. data/lib/sunspot/search/paginated_collection.rb +55 -0
  73. data/lib/sunspot/search/query_facet.rb +67 -0
  74. data/lib/sunspot/search/standard_search.rb +21 -0
  75. data/lib/sunspot/session.rb +260 -0
  76. data/lib/sunspot/session_proxy.rb +87 -0
  77. data/lib/sunspot/session_proxy/abstract_session_proxy.rb +29 -0
  78. data/lib/sunspot/session_proxy/class_sharding_session_proxy.rb +66 -0
  79. data/lib/sunspot/session_proxy/id_sharding_session_proxy.rb +89 -0
  80. data/lib/sunspot/session_proxy/master_slave_session_proxy.rb +43 -0
  81. data/lib/sunspot/session_proxy/sharding_session_proxy.rb +222 -0
  82. data/lib/sunspot/session_proxy/silent_fail_session_proxy.rb +42 -0
  83. data/lib/sunspot/session_proxy/thread_local_session_proxy.rb +37 -0
  84. data/lib/sunspot/setup.rb +350 -0
  85. data/lib/sunspot/text_field_setup.rb +29 -0
  86. data/lib/sunspot/type.rb +372 -0
  87. data/lib/sunspot/util.rb +243 -0
  88. data/lib/sunspot/version.rb +3 -0
  89. data/pduey-sunspot.gemspec +38 -0
  90. data/script/console +10 -0
  91. data/spec/api/adapters_spec.rb +33 -0
  92. data/spec/api/binding_spec.rb +50 -0
  93. data/spec/api/indexer/attributes_spec.rb +149 -0
  94. data/spec/api/indexer/batch_spec.rb +46 -0
  95. data/spec/api/indexer/dynamic_fields_spec.rb +42 -0
  96. data/spec/api/indexer/fixed_fields_spec.rb +57 -0
  97. data/spec/api/indexer/fulltext_spec.rb +43 -0
  98. data/spec/api/indexer/removal_spec.rb +53 -0
  99. data/spec/api/indexer/spec_helper.rb +1 -0
  100. data/spec/api/indexer_spec.rb +14 -0
  101. data/spec/api/query/advanced_manipulation_examples.rb +35 -0
  102. data/spec/api/query/connectives_examples.rb +189 -0
  103. data/spec/api/query/dsl_spec.rb +18 -0
  104. data/spec/api/query/dynamic_fields_examples.rb +165 -0
  105. data/spec/api/query/faceting_examples.rb +397 -0
  106. data/spec/api/query/fulltext_examples.rb +313 -0
  107. data/spec/api/query/function_spec.rb +70 -0
  108. data/spec/api/query/geo_examples.rb +68 -0
  109. data/spec/api/query/highlighting_examples.rb +223 -0
  110. data/spec/api/query/more_like_this_spec.rb +140 -0
  111. data/spec/api/query/ordering_pagination_examples.rb +95 -0
  112. data/spec/api/query/scope_examples.rb +275 -0
  113. data/spec/api/query/spec_helper.rb +1 -0
  114. data/spec/api/query/standard_spec.rb +28 -0
  115. data/spec/api/query/text_field_scoping_examples.rb +30 -0
  116. data/spec/api/query/types_spec.rb +20 -0
  117. data/spec/api/search/dynamic_fields_spec.rb +33 -0
  118. data/spec/api/search/faceting_spec.rb +360 -0
  119. data/spec/api/search/highlighting_spec.rb +69 -0
  120. data/spec/api/search/hits_spec.rb +131 -0
  121. data/spec/api/search/paginated_collection_spec.rb +26 -0
  122. data/spec/api/search/results_spec.rb +66 -0
  123. data/spec/api/search/search_spec.rb +23 -0
  124. data/spec/api/search/spec_helper.rb +1 -0
  125. data/spec/api/session_proxy/class_sharding_session_proxy_spec.rb +85 -0
  126. data/spec/api/session_proxy/id_sharding_session_proxy_spec.rb +30 -0
  127. data/spec/api/session_proxy/master_slave_session_proxy_spec.rb +41 -0
  128. data/spec/api/session_proxy/sharding_session_proxy_spec.rb +77 -0
  129. data/spec/api/session_proxy/silent_fail_session_proxy_spec.rb +24 -0
  130. data/spec/api/session_proxy/spec_helper.rb +9 -0
  131. data/spec/api/session_proxy/thread_local_session_proxy_spec.rb +39 -0
  132. data/spec/api/session_spec.rb +220 -0
  133. data/spec/api/spec_helper.rb +3 -0
  134. data/spec/api/sunspot_spec.rb +18 -0
  135. data/spec/ext.rb +11 -0
  136. data/spec/helpers/indexer_helper.rb +29 -0
  137. data/spec/helpers/query_helper.rb +38 -0
  138. data/spec/helpers/search_helper.rb +80 -0
  139. data/spec/integration/dynamic_fields_spec.rb +57 -0
  140. data/spec/integration/faceting_spec.rb +238 -0
  141. data/spec/integration/highlighting_spec.rb +24 -0
  142. data/spec/integration/indexing_spec.rb +33 -0
  143. data/spec/integration/keyword_search_spec.rb +317 -0
  144. data/spec/integration/local_search_spec.rb +64 -0
  145. data/spec/integration/more_like_this_spec.rb +43 -0
  146. data/spec/integration/scoped_search_spec.rb +354 -0
  147. data/spec/integration/spec_helper.rb +7 -0
  148. data/spec/integration/stored_fields_spec.rb +12 -0
  149. data/spec/integration/test_pagination.rb +32 -0
  150. data/spec/mocks/adapters.rb +32 -0
  151. data/spec/mocks/blog.rb +3 -0
  152. data/spec/mocks/comment.rb +21 -0
  153. data/spec/mocks/connection.rb +126 -0
  154. data/spec/mocks/mock_adapter.rb +30 -0
  155. data/spec/mocks/mock_class_sharding_session_proxy.rb +24 -0
  156. data/spec/mocks/mock_record.rb +52 -0
  157. data/spec/mocks/mock_sharding_session_proxy.rb +15 -0
  158. data/spec/mocks/photo.rb +11 -0
  159. data/spec/mocks/post.rb +85 -0
  160. data/spec/mocks/super_class.rb +2 -0
  161. data/spec/mocks/user.rb +13 -0
  162. data/spec/spec_helper.rb +28 -0
  163. data/tasks/rdoc.rake +27 -0
  164. data/tasks/schema.rake +19 -0
  165. data/tasks/todo.rake +4 -0
  166. metadata +369 -0
@@ -0,0 +1,121 @@
1
+ module Sunspot
2
+ module DSL
3
+ class RestrictionWithNear < Restriction
4
+ def initialize(field, scope, query, negated)
5
+ super(field, scope, negated)
6
+ @query = query
7
+ end
8
+
9
+ #
10
+ # Perform a Geohash-based location restriction for the given `location`
11
+ # field. Though this uses the same API as other attribute-field
12
+ # restrictions, there are several differences between this and other
13
+ # scoping methods:
14
+ #
15
+ # * It can only be called from the top-level query; it cannot be nested
16
+ # in a `dynamic`, `any_of`, or `all_of` block. This is because geohash
17
+ # queries are not sent to Solr as filter queries like other scopes, but
18
+ # rather are part of the fulltext query sent to Solr.
19
+ # * Because it is included with the fulltext query (if any), location
20
+ # restrictions can be given boost. By default, an "exact"
21
+ # (maximum-precision) match will give the result a boost of 1.0; each
22
+ # lower level of precision gives a boost of 1/2 the next highest
23
+ # precision. See below for options to modify this behavior.
24
+ #
25
+ # ==== What is a Geohash?
26
+ #
27
+ # Geohash is a clever algorithm that creates a decodable digest of a
28
+ # geographical point. It does this by dividing the globe into
29
+ # quadrants, encoding the quadrant in which the point sits in the hash,
30
+ # dividing the quadrant into smaller quadrants, and repeating an arbitrary
31
+ # number of times (the "precision"). Because of the way Geohash are
32
+ # built, the shared Geohash prefix length of two locations will
33
+ # <em>usually</em> increase as the distance between the points decreases.
34
+ # Put another way, the geohashes of two nearby points will
35
+ # <em>usually</em> have a longer shared prefix than two points which are
36
+ # distant from one another.
37
+ #
38
+ # Read more about Geohashes on
39
+ # {Wikipedia}[http://en.wikipedia.org/wiki/Geohash] or play around with
40
+ # generating your own at {geohash.org}[http://geohash.org/].
41
+ #
42
+ # In Sunspot, GeoHashes can have a precision between 3 and 12; this is the
43
+ # number of characters in the hash. The precisions have the following
44
+ # maximum bounding box sizes, in miles:
45
+ #
46
+ # <dt>3</dt>
47
+ # <dd>389.07812</dd>
48
+ # <dt>4</dt>
49
+ # <dd>97.26953</dd>
50
+ # <dt>5</dt>
51
+ # <dd>24.31738</dd>
52
+ # <dt>6</dt>
53
+ # <dd>6.07935</dd>
54
+ # <dt>7</dt>
55
+ # <dd>1.51984
56
+ # <dt>8</dt>
57
+ # <dd>0.37996</dd>
58
+ # <dt>9</dt>
59
+ # <dd>0.09499</dd>
60
+ # <dt>10</dt>
61
+ # <dd>0.02375</dd>
62
+ # <dt>11</dt>
63
+ # <dd>0.00594</dd>
64
+ # <dt>12</dt>
65
+ # <dd>0.00148</dd>
66
+ #
67
+ # ==== Score, boost, and sorting with location search
68
+ #
69
+ # The concept of relevance scoring is a familiar one from fulltext search;
70
+ # Solr (or Lucene, actually) gives each result document a score based on
71
+ # how relevant the document's text is to the search phrase. Sunspot's
72
+ # location search also uses scoring to determine geographical relevance;
73
+ # using boosts, longer prefix matches (which are, in general,
74
+ # geographically closer to the search origin) are assigned higher
75
+ # relevance. This means that the results of a pure location search are
76
+ # <em>roughly</em> in order of geographical distance, as long as no other
77
+ # sort is specified explicitly.
78
+ #
79
+ # This geographical relevance plays on the same field as fulltext scoring;
80
+ # if you use both fulltext and geographical components in a single search,
81
+ # both types of relevance will be taken into account when scoring the
82
+ # matches. Thus, a very close fulltext match that's further away from the
83
+ # geographical origin will be scored similarly to a less precise fulltext
84
+ # match that is very close to the geographical origin. That's likely to be
85
+ # consistent with the way most users would expect a fulltext geographical
86
+ # search to work.
87
+ #
88
+ # ==== Options
89
+ #
90
+ # <dt><code>:precision</code></dt>
91
+ # <dd>The minimum precision at which locations should match. See the table
92
+ # of precisions and bounding-box sizes above; the proximity value will
93
+ # ensure that all matching documents share a bounding box of the
94
+ # corresponding maximum size with your origin point. The default value
95
+ # is 7, meaning all results will share a bounding box with edges of
96
+ # about one and a half miles with the origin.</dd>
97
+ # <dt><code>:boost</code></dt>
98
+ # <dd>The boost to apply to maximum-precision matches. Default is 1.0. You
99
+ # can use this option to adjust the weight given to geographic
100
+ # proximity versus fulltext matching, if you are doing both in a
101
+ # search.</dd>
102
+ # <dt><code>:precision_factor</code></dt>
103
+ # <dd>This option determines how much boost is applied to matches at lower
104
+ # precisions. The default value, 16.0, means that a match at precision
105
+ # N is 1/16 as relevant as a match at precision N+1 (this is consistent
106
+ # with the fact that each precision's bounding box is about sixteen
107
+ # times the size of the next highest precision.)</dd>
108
+ #
109
+ # ==== Example
110
+ #
111
+ # Sunspot.search(Post) do
112
+ # fulltext('pizza')
113
+ # with(:location).near(-40.0, -70.0, :boost => 2, :precision => 6)
114
+ # end
115
+ #
116
+ def near(lat, lng, options = {})
117
+ @query.fulltext.add_location(@field, lat, lng, options)
118
+ end
119
+ end
120
+ end
121
+ end
@@ -0,0 +1,217 @@
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(scope, setup) #:nodoc:
14
+ @scope, @setup = scope, setup
15
+ end
16
+
17
+ #
18
+ # Build a positive restriction. This method can take three forms: equality
19
+ # restriction, restriction by another restriction, or identity
20
+ # restriction.
21
+ # In the first two forms, the first argument is a field name. If only a
22
+ # field name is specified, this method returns another DSL object which
23
+ # presents methods for attaching various restriction types.
24
+ # With two arguments, this creates a shorthand restriction: if the second
25
+ # argument is a scalar, an equality restriction is created; if it is a
26
+ # Range, a between restriction will be created; and if it is an Array, an
27
+ # any_of restriction will be created.
28
+ # The third from restricts the search results to a specific instance.
29
+ #
30
+ # ==== Parameters (restriction by field value)
31
+ #
32
+ # field_name<Symbol>:: Name of the field on which to place the restriction
33
+ # value<Object,Range,Array>::
34
+ # If passed, creates an equality, range, or any-of restriction based on
35
+ # the type of value passed.
36
+ #
37
+ # ==== Parameters (restriction by identity)
38
+ #
39
+ # args<Object>...::
40
+ # One or more instances that should be included in the results
41
+ #
42
+ # ==== Returns
43
+ #
44
+ # Sunspot::DSL::Query::Restriction::
45
+ # Restriction DSL object (if only one argument is passed which is a
46
+ # field name)
47
+ #
48
+ # ==== Examples
49
+ #
50
+ # An equality restriction:
51
+ #
52
+ # Sunspot.search do
53
+ # with(:blog_id, 1)
54
+ # end
55
+ #
56
+ # Restrict by range:
57
+ #
58
+ # Sunspot.search do
59
+ # with(:average_rating, 3.0..5.0)
60
+ # end
61
+ #
62
+ # Restrict by a set of allowed values:
63
+ #
64
+ # Sunspot.search do
65
+ # with(:category_ids, [1, 5, 9])
66
+ # end
67
+ #
68
+ # Other restriction types:
69
+ #
70
+ # Sunspot.search(Post) do
71
+ # with(:average_rating).greater_than(3.0)
72
+ # end
73
+ #
74
+ # Restriction by identity:
75
+ #
76
+ # Sunspot.search(Post) do
77
+ # with(some_post_instance)
78
+ # end
79
+ #
80
+ def with(*args)
81
+ add_restriction(false, *args)
82
+ end
83
+
84
+ #
85
+ # Build a negative restriction (exclusion). This method works the same way
86
+ # asthe #with method.
87
+ #
88
+ def without(*args)
89
+ add_restriction(true, *args)
90
+ end
91
+
92
+ #
93
+ # Create a disjunction, scoping the results to documents that match any
94
+ # of the enclosed restrictions.
95
+ #
96
+ # ==== Example
97
+ #
98
+ # Sunspot.search(Post) do
99
+ # any_of do
100
+ # with(:expired_at).greater_than Time.now
101
+ # with :expired_at, nil
102
+ # end
103
+ # end
104
+ #
105
+ # This will return all documents who either have an expiration time in the
106
+ # future, or who do not have any expiration time at all.
107
+ #
108
+ def any_of(&block)
109
+ disjunction = @scope.add_disjunction
110
+ Util.instance_eval_or_call(Scope.new(disjunction, @setup), &block)
111
+ disjunction
112
+ end
113
+
114
+ #
115
+ # Create a conjunction, scoping the results to documents that match all of
116
+ # the enclosed restrictions. When called from the top level of a search
117
+ # block, this has no effect, but can be useful for grouping a conjunction
118
+ # inside a disjunction.
119
+ #
120
+ # ==== Example
121
+ #
122
+ # Sunspot.search(Post) do
123
+ # any_of do
124
+ # with(:blog_id, 1)
125
+ # all_of do
126
+ # with(:blog_id, 2)
127
+ # with(:category_ids, 3)
128
+ # end
129
+ # end
130
+ # end
131
+ #
132
+ def all_of(&block)
133
+ conjunction = @scope.add_conjunction
134
+ Util.instance_eval_or_call(Scope.new(conjunction, @setup), &block)
135
+ conjunction
136
+ end
137
+
138
+ #
139
+ # Apply restrictions, facets, and ordering to dynamic field instances.
140
+ # The block API is implemented by Sunspot::DSL::FieldQuery, which is a
141
+ # superclass of the Query DSL (thus providing a subset of the API, in
142
+ # particular only methods that refer to particular fields).
143
+ #
144
+ # ==== Parameters
145
+ #
146
+ # base_name<Symbol>:: The base name for the dynamic field definition
147
+ #
148
+ # ==== Example
149
+ #
150
+ # Sunspot.search Post do
151
+ # dynamic :custom do
152
+ # with :cuisine, 'Pizza'
153
+ # facet :atmosphere
154
+ # order_by :chef_name
155
+ # end
156
+ # end
157
+ #
158
+ def dynamic(base_name, &block)
159
+ Sunspot::Util.instance_eval_or_call(
160
+ Scope.new(@scope, @setup.dynamic_field_factory(base_name)),
161
+ &block
162
+ )
163
+ end
164
+
165
+ #
166
+ # Apply scope-type restrictions on fulltext fields. In certain situations,
167
+ # it may be desirable to place logical restrictions on text fields.
168
+ # Remember that text fields are tokenized; your mileage may very.
169
+ #
170
+ # The block works exactly like a normal scope, except that the field names
171
+ # refer to text fields instead of attribute fields.
172
+ #
173
+ # === Example
174
+ #
175
+ # Sunspot.search(Post) do
176
+ # text_fields do
177
+ # with :body, nil
178
+ # end
179
+ # end
180
+ #
181
+ # This will return all documents that do not have a body.
182
+ #
183
+ def text_fields(&block)
184
+ Sunspot::Util.instance_eval_or_call(
185
+ Scope.new(@scope, TextFieldSetup.new(@setup)),
186
+ &block
187
+ )
188
+ end
189
+
190
+ private
191
+
192
+ def add_restriction(negated, *args)
193
+ case args.first
194
+ when String, Symbol
195
+ raise ArgumentError if args.length > 2
196
+ field_name = args[0]
197
+ value = args.length > 1 ? args[1] : NONE
198
+ if value == NONE
199
+ DSL::Restriction.new(@setup.field(field_name.to_sym), @scope, negated)
200
+ else
201
+ @scope.add_shorthand_restriction(negated, @setup.field(field_name.to_sym), value)
202
+ end
203
+ else
204
+ instances = args.flatten
205
+ @scope.add_restriction(
206
+ negated,
207
+ IdField.instance,
208
+ Sunspot::Query::Restriction::AnyOf,
209
+ instances.flatten.map { |instance|
210
+ Sunspot::Adapters::InstanceAdapter.adapt(instance).index_id }
211
+ )
212
+ end
213
+ end
214
+
215
+ end
216
+ end
217
+ end
@@ -0,0 +1,30 @@
1
+ module Sunspot
2
+ module DSL
3
+ #
4
+ # This top-level DSL class is the context in which the block passed to
5
+ # Sunspot.query. See Sunspot::DSL::Query, Sunspot::DSL::FieldQuery, and
6
+ # Sunspot::DSL::Scope for the full API presented.
7
+ #
8
+ class Search < StandardQuery
9
+ def initialize(search, setup) #:nodoc:
10
+ @search = search
11
+ super(search, search.query, setup)
12
+ end
13
+
14
+ #
15
+ # Retrieve the data accessor used to load instances of the given class
16
+ # out of persistent storage. Data accessors are free to implement any
17
+ # extra methods that may be useful in this context.
18
+ #
19
+ # ==== Example
20
+ #
21
+ # Sunspot.search Post do
22
+ # data_acccessor_for(Post).includes = [:blog, :comments]
23
+ # end
24
+ #
25
+ def data_accessor_for(clazz)
26
+ @search.data_accessor_for(clazz)
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,121 @@
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 StandardQuery < FieldQuery
12
+ include Paginatable, Adjustable
13
+
14
+ # Specify a phrase that should be searched as fulltext. Only +text+
15
+ # fields are searched - see DSL::Fields.text
16
+ #
17
+ # Keyword search is executed using Solr's dismax handler, which strikes
18
+ # a good balance between powerful and foolproof. In particular,
19
+ # well-matched quotation marks can be used to group phrases, and the
20
+ # + and - modifiers work as expected. All other special Solr boolean
21
+ # syntax is escaped, and mismatched quotes are ignored entirely.
22
+ #
23
+ # This method can optionally take a block, which is evaluated by the
24
+ # Fulltext DSL class, and exposes several powerful dismax features.
25
+ #
26
+ # ==== Parameters
27
+ #
28
+ # keywords<String>:: phrase to perform fulltext search on
29
+ #
30
+ # ==== Options
31
+ #
32
+ # :fields<Array>::
33
+ # List of fields that should be searched for keywords. Defaults to all
34
+ # fields configured for the types under search.
35
+ # :highlight<Boolean,Array>::
36
+ # If true, perform keyword highlighting on all searched fields. If an
37
+ # array of field names, perform highlighting on the specified fields.
38
+ # This can also be called from within the fulltext block.
39
+ # :minimum_match<Integer>::
40
+ # The minimum number of search terms that a result must match. By
41
+ # default, all search terms must match; if the number of search terms
42
+ # is less than this number, the default behavior applies.
43
+ # :tie<Float>::
44
+ # A tiebreaker coefficient for scores derived from subqueries that are
45
+ # lower-scoring than the maximum score subquery. Typically a near-zero
46
+ # value is useful. See
47
+ # http://wiki.apache.org/solr/DisMaxRequestHandler#tie_.28Tie_breaker.29
48
+ # for more information.
49
+ # :query_phrase_slop<Integer>::
50
+ # The number of words that can appear between the words in a
51
+ # user-entered phrase (i.e., keywords in quotes) and still match. For
52
+ # instance, in a search for "\"great pizza\"" with a phrase slop of 1,
53
+ # "great pizza" and "great big pizza" will match, but "great monster of
54
+ # a pizza" will not. Default behavior is a query phrase slop of zero.
55
+ #
56
+ def fulltext(keywords, options = {}, &block)
57
+ if keywords && !(keywords.to_s =~ /^\s*$/)
58
+ fulltext_query = @query.add_fulltext(keywords)
59
+ if field_names = options.delete(:fields)
60
+ Util.Array(field_names).each do |field_name|
61
+ @setup.text_fields(field_name).each do |field|
62
+ fulltext_query.add_fulltext_field(field, field.default_boost)
63
+ end
64
+ end
65
+ end
66
+ if minimum_match = options.delete(:minimum_match)
67
+ fulltext_query.minimum_match = minimum_match.to_i
68
+ end
69
+ if tie = options.delete(:tie)
70
+ fulltext_query.tie = tie.to_f
71
+ end
72
+ if query_phrase_slop = options.delete(:query_phrase_slop)
73
+ fulltext_query.query_phrase_slop = query_phrase_slop.to_i
74
+ end
75
+ if highlight_field_names = options.delete(:highlight)
76
+ if highlight_field_names == true
77
+ fulltext_query.add_highlight
78
+ else
79
+ highlight_fields = []
80
+ Util.Array(highlight_field_names).each do |field_name|
81
+ highlight_fields.concat(@setup.text_fields(field_name))
82
+ end
83
+ fulltext_query.add_highlight(highlight_fields)
84
+ end
85
+ end
86
+ if block && fulltext_query
87
+ fulltext_dsl = Fulltext.new(fulltext_query, @setup)
88
+ Util.instance_eval_or_call(
89
+ fulltext_dsl,
90
+ &block
91
+ )
92
+ end
93
+ if !field_names && (!fulltext_dsl || !fulltext_dsl.fields_added?)
94
+ @setup.all_text_fields.each do |field|
95
+ unless fulltext_query.has_fulltext_field?(field)
96
+ unless fulltext_dsl && fulltext_dsl.exclude_fields.include?(field.name)
97
+ fulltext_query.add_fulltext_field(field, field.default_boost)
98
+ end
99
+ end
100
+ end
101
+ end
102
+ end
103
+ end
104
+ alias_method :keywords, :fulltext
105
+
106
+ def with(*args)
107
+ case args.first
108
+ when String, Symbol
109
+ field_name = args[0]
110
+ value = args.length > 1 ? args[1] : Scope::NONE
111
+ if value == Scope::NONE
112
+ return DSL::RestrictionWithNear.new(@setup.field(field_name.to_sym), @scope, @query, false)
113
+ end
114
+ end
115
+
116
+ # else
117
+ super
118
+ end
119
+ end
120
+ end
121
+ end