lisausa-sunspot 1.2.1

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.
Files changed (217) hide show
  1. data/.gitignore +12 -0
  2. data/Gemfile +4 -0
  3. data/History.txt +222 -0
  4. data/LICENSE +18 -0
  5. data/Rakefile +17 -0
  6. data/TODO +13 -0
  7. data/VERSION.yml +4 -0
  8. data/bin/sunspot-installer +19 -0
  9. data/bin/sunspot-solr +74 -0
  10. data/installer/config/schema.yml +95 -0
  11. data/lib/light_config.rb +40 -0
  12. data/lib/sunspot.rb +569 -0
  13. data/lib/sunspot/adapters.rb +265 -0
  14. data/lib/sunspot/composite_setup.rb +202 -0
  15. data/lib/sunspot/configuration.rb +46 -0
  16. data/lib/sunspot/data_extractor.rb +50 -0
  17. data/lib/sunspot/dsl.rb +5 -0
  18. data/lib/sunspot/dsl/adjustable.rb +47 -0
  19. data/lib/sunspot/dsl/field_query.rb +279 -0
  20. data/lib/sunspot/dsl/fields.rb +103 -0
  21. data/lib/sunspot/dsl/fulltext.rb +243 -0
  22. data/lib/sunspot/dsl/function.rb +14 -0
  23. data/lib/sunspot/dsl/functional.rb +44 -0
  24. data/lib/sunspot/dsl/more_like_this_query.rb +56 -0
  25. data/lib/sunspot/dsl/paginatable.rb +28 -0
  26. data/lib/sunspot/dsl/query_facet.rb +36 -0
  27. data/lib/sunspot/dsl/restriction.rb +25 -0
  28. data/lib/sunspot/dsl/restriction_with_near.rb +121 -0
  29. data/lib/sunspot/dsl/scope.rb +217 -0
  30. data/lib/sunspot/dsl/search.rb +30 -0
  31. data/lib/sunspot/dsl/standard_query.rb +121 -0
  32. data/lib/sunspot/field.rb +193 -0
  33. data/lib/sunspot/field_factory.rb +129 -0
  34. data/lib/sunspot/indexer.rb +131 -0
  35. data/lib/sunspot/installer.rb +31 -0
  36. data/lib/sunspot/installer/library_installer.rb +45 -0
  37. data/lib/sunspot/installer/schema_builder.rb +219 -0
  38. data/lib/sunspot/installer/solrconfig_updater.rb +76 -0
  39. data/lib/sunspot/installer/task_helper.rb +18 -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 +67 -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 +293 -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 +136 -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/server.rb +152 -0
  76. data/lib/sunspot/session.rb +260 -0
  77. data/lib/sunspot/session_proxy.rb +87 -0
  78. data/lib/sunspot/session_proxy/abstract_session_proxy.rb +29 -0
  79. data/lib/sunspot/session_proxy/class_sharding_session_proxy.rb +66 -0
  80. data/lib/sunspot/session_proxy/id_sharding_session_proxy.rb +89 -0
  81. data/lib/sunspot/session_proxy/master_slave_session_proxy.rb +43 -0
  82. data/lib/sunspot/session_proxy/sharding_session_proxy.rb +222 -0
  83. data/lib/sunspot/session_proxy/silent_fail_session_proxy.rb +42 -0
  84. data/lib/sunspot/session_proxy/thread_local_session_proxy.rb +37 -0
  85. data/lib/sunspot/setup.rb +350 -0
  86. data/lib/sunspot/text_field_setup.rb +29 -0
  87. data/lib/sunspot/type.rb +372 -0
  88. data/lib/sunspot/util.rb +243 -0
  89. data/lib/sunspot/version.rb +3 -0
  90. data/script/console +10 -0
  91. data/solr-1.3/etc/jetty.xml +212 -0
  92. data/solr-1.3/etc/webdefault.xml +379 -0
  93. data/solr-1.3/lib/jetty-6.1.3.jar +0 -0
  94. data/solr-1.3/lib/jetty-util-6.1.3.jar +0 -0
  95. data/solr-1.3/lib/jsp-2.1/ant-1.6.5.jar +0 -0
  96. data/solr-1.3/lib/jsp-2.1/core-3.1.1.jar +0 -0
  97. data/solr-1.3/lib/jsp-2.1/jsp-2.1.jar +0 -0
  98. data/solr-1.3/lib/jsp-2.1/jsp-api-2.1.jar +0 -0
  99. data/solr-1.3/lib/servlet-api-2.5-6.1.3.jar +0 -0
  100. data/solr-1.3/solr/conf/elevate.xml +36 -0
  101. data/solr-1.3/solr/conf/protwords.txt +21 -0
  102. data/solr-1.3/solr/conf/schema.xml +64 -0
  103. data/solr-1.3/solr/conf/solrconfig.xml +725 -0
  104. data/solr-1.3/solr/conf/stopwords.txt +57 -0
  105. data/solr-1.3/solr/conf/synonyms.txt +31 -0
  106. data/solr-1.3/solr/lib/geoapi-nogenerics-2.1-M2.jar +0 -0
  107. data/solr-1.3/solr/lib/gt2-referencing-2.3.1.jar +0 -0
  108. data/solr-1.3/solr/lib/jsr108-0.01.jar +0 -0
  109. data/solr-1.3/solr/lib/locallucene.jar +0 -0
  110. data/solr-1.3/solr/lib/localsolr.jar +0 -0
  111. data/solr-1.3/start.jar +0 -0
  112. data/solr-1.3/webapps/solr.war +0 -0
  113. data/solr/README.txt +42 -0
  114. data/solr/etc/jetty.xml +218 -0
  115. data/solr/etc/webdefault.xml +379 -0
  116. data/solr/lib/jetty-6.1.3.jar +0 -0
  117. data/solr/lib/jetty-util-6.1.3.jar +0 -0
  118. data/solr/lib/jsp-2.1/ant-1.6.5.jar +0 -0
  119. data/solr/lib/jsp-2.1/core-3.1.1.jar +0 -0
  120. data/solr/lib/jsp-2.1/jsp-2.1.jar +0 -0
  121. data/solr/lib/jsp-2.1/jsp-api-2.1.jar +0 -0
  122. data/solr/lib/servlet-api-2.5-6.1.3.jar +0 -0
  123. data/solr/solr/.gitignore +1 -0
  124. data/solr/solr/README.txt +54 -0
  125. data/solr/solr/conf/admin-extra.html +31 -0
  126. data/solr/solr/conf/elevate.xml +36 -0
  127. data/solr/solr/conf/mapping-ISOLatin1Accent.txt +246 -0
  128. data/solr/solr/conf/protwords.txt +21 -0
  129. data/solr/solr/conf/schema.xml +238 -0
  130. data/solr/solr/conf/scripts.conf +24 -0
  131. data/solr/solr/conf/solrconfig.xml +934 -0
  132. data/solr/solr/conf/spellings.txt +2 -0
  133. data/solr/solr/conf/stopwords.txt +58 -0
  134. data/solr/solr/conf/synonyms.txt +31 -0
  135. data/solr/solr/conf/xslt/example.xsl +132 -0
  136. data/solr/solr/conf/xslt/example_atom.xsl +67 -0
  137. data/solr/solr/conf/xslt/example_rss.xsl +66 -0
  138. data/solr/solr/conf/xslt/luke.xsl +337 -0
  139. data/solr/start.jar +0 -0
  140. data/solr/webapps/solr.war +0 -0
  141. data/spec/api/adapters_spec.rb +33 -0
  142. data/spec/api/binding_spec.rb +50 -0
  143. data/spec/api/indexer/attributes_spec.rb +149 -0
  144. data/spec/api/indexer/batch_spec.rb +46 -0
  145. data/spec/api/indexer/dynamic_fields_spec.rb +42 -0
  146. data/spec/api/indexer/fixed_fields_spec.rb +57 -0
  147. data/spec/api/indexer/fulltext_spec.rb +43 -0
  148. data/spec/api/indexer/removal_spec.rb +53 -0
  149. data/spec/api/indexer/spec_helper.rb +1 -0
  150. data/spec/api/indexer_spec.rb +14 -0
  151. data/spec/api/query/advanced_manipulation_examples.rb +35 -0
  152. data/spec/api/query/connectives_examples.rb +189 -0
  153. data/spec/api/query/dsl_spec.rb +18 -0
  154. data/spec/api/query/dynamic_fields_examples.rb +165 -0
  155. data/spec/api/query/faceting_examples.rb +397 -0
  156. data/spec/api/query/fulltext_examples.rb +313 -0
  157. data/spec/api/query/function_spec.rb +70 -0
  158. data/spec/api/query/geo_examples.rb +68 -0
  159. data/spec/api/query/highlighting_examples.rb +223 -0
  160. data/spec/api/query/more_like_this_spec.rb +140 -0
  161. data/spec/api/query/ordering_pagination_examples.rb +95 -0
  162. data/spec/api/query/scope_examples.rb +275 -0
  163. data/spec/api/query/spec_helper.rb +1 -0
  164. data/spec/api/query/standard_spec.rb +28 -0
  165. data/spec/api/query/text_field_scoping_examples.rb +30 -0
  166. data/spec/api/query/types_spec.rb +20 -0
  167. data/spec/api/search/dynamic_fields_spec.rb +33 -0
  168. data/spec/api/search/faceting_spec.rb +360 -0
  169. data/spec/api/search/highlighting_spec.rb +69 -0
  170. data/spec/api/search/hits_spec.rb +120 -0
  171. data/spec/api/search/paginated_collection_spec.rb +26 -0
  172. data/spec/api/search/results_spec.rb +66 -0
  173. data/spec/api/search/search_spec.rb +23 -0
  174. data/spec/api/search/spec_helper.rb +1 -0
  175. data/spec/api/server_spec.rb +91 -0
  176. data/spec/api/session_proxy/class_sharding_session_proxy_spec.rb +85 -0
  177. data/spec/api/session_proxy/id_sharding_session_proxy_spec.rb +30 -0
  178. data/spec/api/session_proxy/master_slave_session_proxy_spec.rb +41 -0
  179. data/spec/api/session_proxy/sharding_session_proxy_spec.rb +77 -0
  180. data/spec/api/session_proxy/silent_fail_session_proxy_spec.rb +24 -0
  181. data/spec/api/session_proxy/spec_helper.rb +9 -0
  182. data/spec/api/session_proxy/thread_local_session_proxy_spec.rb +50 -0
  183. data/spec/api/session_spec.rb +220 -0
  184. data/spec/api/spec_helper.rb +3 -0
  185. data/spec/api/sunspot_spec.rb +18 -0
  186. data/spec/ext.rb +11 -0
  187. data/spec/helpers/indexer_helper.rb +29 -0
  188. data/spec/helpers/query_helper.rb +38 -0
  189. data/spec/helpers/search_helper.rb +80 -0
  190. data/spec/integration/dynamic_fields_spec.rb +55 -0
  191. data/spec/integration/faceting_spec.rb +238 -0
  192. data/spec/integration/highlighting_spec.rb +22 -0
  193. data/spec/integration/indexing_spec.rb +33 -0
  194. data/spec/integration/keyword_search_spec.rb +317 -0
  195. data/spec/integration/local_search_spec.rb +64 -0
  196. data/spec/integration/more_like_this_spec.rb +43 -0
  197. data/spec/integration/scoped_search_spec.rb +354 -0
  198. data/spec/integration/spec_helper.rb +7 -0
  199. data/spec/integration/stored_fields_spec.rb +10 -0
  200. data/spec/integration/test_pagination.rb +32 -0
  201. data/spec/mocks/adapters.rb +32 -0
  202. data/spec/mocks/blog.rb +3 -0
  203. data/spec/mocks/comment.rb +21 -0
  204. data/spec/mocks/connection.rb +126 -0
  205. data/spec/mocks/mock_adapter.rb +30 -0
  206. data/spec/mocks/mock_class_sharding_session_proxy.rb +24 -0
  207. data/spec/mocks/mock_record.rb +52 -0
  208. data/spec/mocks/mock_sharding_session_proxy.rb +15 -0
  209. data/spec/mocks/photo.rb +11 -0
  210. data/spec/mocks/post.rb +85 -0
  211. data/spec/mocks/super_class.rb +2 -0
  212. data/spec/mocks/user.rb +13 -0
  213. data/spec/spec_helper.rb +30 -0
  214. data/tasks/rdoc.rake +27 -0
  215. data/tasks/schema.rake +19 -0
  216. data/tasks/todo.rake +4 -0
  217. metadata +457 -0
@@ -0,0 +1,46 @@
1
+ module Sunspot
2
+ # The Sunspot::Configuration module provides a factory method for Sunspot
3
+ # configuration objects. Available properties are:
4
+ #
5
+ # Sunspot.config.solr.url::
6
+ # The URL at which to connect to Solr
7
+ # (default: 'http://localhost:8983/solr')
8
+ # Sunspot.config.pagination.default_per_page::
9
+ # Solr always paginates its results. This sets Sunspot's default result
10
+ # count per page if it is not explicitly specified in the query.
11
+ #
12
+ module Configuration
13
+ class <<self
14
+ # Factory method to build configuration instances.
15
+ #
16
+ # ==== Returns
17
+ #
18
+ # LightConfig::Configuration:: new configuration instance with defaults
19
+ #
20
+ def build #:nodoc:
21
+ LightConfig.build do
22
+ solr do
23
+ url 'http://127.0.0.1:8983/solr'
24
+ end
25
+ master_solr do
26
+ url nil
27
+ end
28
+ pagination do
29
+ default_per_page 30
30
+ end
31
+ end
32
+ end
33
+
34
+ # Location for the default solr configuration files,
35
+ # required for bootstrapping a new solr installation
36
+ #
37
+ # ==== Returns
38
+ #
39
+ # String:: Directory with default solr config files
40
+ #
41
+ def solr_default_configuration_location
42
+ File.join( File.dirname(__FILE__), '../../solr/solr/conf' )
43
+ end
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,50 @@
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
+ #
38
+ # Constant data extractors simply return the same value for every object.
39
+ #
40
+ class Constant
41
+ def initialize(value)
42
+ @value = value
43
+ end
44
+
45
+ def value_for(object)
46
+ @value
47
+ end
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,5 @@
1
+ %w(fields scope paginatable adjustable field_query standard_query query_facet
2
+ functional fulltext restriction restriction_with_near search
3
+ more_like_this_query function).each do |file|
4
+ require File.join(File.dirname(__FILE__), 'dsl', file)
5
+ end
@@ -0,0 +1,47 @@
1
+ module Sunspot
2
+ module DSL #:nodoc:
3
+ module Adjustable #:nodoc
4
+ # <strong>Expert:</strong> Adjust or reset the parameters passed to Solr.
5
+ # The adjustment will take place just before sending the params to solr,
6
+ # after Sunspot builds the Solr params based on the methods called in the
7
+ # DSL.
8
+ #
9
+ # Under normal circumstances, using this method should not be necessary;
10
+ # if you find that it is, please consider submitting a feature request.
11
+ # Using this method requires knowledge of Sunspot's internal Solr schema
12
+ # and Solr query representations, which are not part of Sunspot's public
13
+ # API; they could change at any time. <strong>This method is unsupported
14
+ # and your mileage may vary.</strong>
15
+ #
16
+ # ==== Examples
17
+ #
18
+ # Sunspot.search(Post) do
19
+ # adjust_solr_params do |params|
20
+ # params[:q] += ' AND something_s:more'
21
+ # end
22
+ # end
23
+ #
24
+ # Sunspot.more_like_this(my_post) do
25
+ # adjust_solr_params do |params|
26
+ # params["mlt.match.include"] = true
27
+ # end
28
+ # end
29
+ #
30
+ def adjust_solr_params( &block )
31
+ @query.solr_parameter_adjustment = block
32
+ end
33
+
34
+ #
35
+ # <strong>Expert:</strong> Use a custom request handler for this search.
36
+ # The general use case for this would be a request handler configuration
37
+ # you've defined in solrconfig that has different search components,
38
+ # defaults, etc. Using this to point at an entirely different type of
39
+ # request handler that Sunspot doesn't support probably won't get you very
40
+ # far.
41
+ #
42
+ def request_handler(request_handler)
43
+ @search.request_handler = request_handler
44
+ end
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,279 @@
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
+ def initialize(search, query, setup) #:nodoc:
10
+ @search, @query = search, query
11
+ super(query.scope, setup)
12
+ end
13
+
14
+ # Specify the order that results should be returned in. This method can
15
+ # be called multiple times; precedence will be in the order given.
16
+ #
17
+ # ==== Parameters
18
+ #
19
+ # field_name<Symbol>:: the field to use for ordering
20
+ # direction<Symbol>:: :asc or :desc (default :asc)
21
+ #
22
+ def order_by(field_name, direction = nil)
23
+ sort =
24
+ if special = Sunspot::Query::Sort.special(field_name)
25
+ special.new(direction)
26
+ else
27
+ Sunspot::Query::Sort::FieldSort.new(
28
+ @setup.field(field_name), direction
29
+ )
30
+ end
31
+ @query.add_sort(sort)
32
+ end
33
+
34
+ #
35
+ # DEPRECATED Use <code>order_by(:random)</code>
36
+ #
37
+ def order_by_random
38
+ order_by(:random)
39
+ end
40
+
41
+ #
42
+ # Request a facet on the search query. A facet is a feature of Solr that
43
+ # determines the number of documents that match the existing search *and*
44
+ # an additional criterion. This allows you to build powerful drill-down
45
+ # interfaces for search, at each step presenting the searcher with a set
46
+ # of refinements that are known to return results.
47
+ #
48
+ # In Sunspot, each facet returns zero or more rows, each of which
49
+ # represents a particular criterion conjoined with the actual query being
50
+ # performed. For _field_ _facets_, each row represents a particular value
51
+ # for a given field. For _query_ _facets_, each row represents an
52
+ # arbitrary scope; the facet itself is just a means of logically grouping
53
+ # the scopes.
54
+ #
55
+ # === Examples
56
+ #
57
+ # ==== Field Facets
58
+ #
59
+ # A field facet is specified by passing one or more Symbol arguments to
60
+ # this method:
61
+ #
62
+ # Sunspot.search(Post) do
63
+ # with(:blog_id, 1)
64
+ # facet(:category_id)
65
+ # end
66
+ #
67
+ # The facet specified above will have a row for each category_id that is
68
+ # present in a document which also has a blog_id of 1.
69
+ #
70
+ # ==== Multiselect Facets
71
+ #
72
+ # In certain circumstances, it is beneficial to exclude certain query
73
+ # scopes from a facet; the most common example is multi-select faceting,
74
+ # where the user has selected a certain value, but the facet should still
75
+ # show all options that would be available if they had not:
76
+ #
77
+ # Sunspot.search(Post) do
78
+ # with(:blog_id, 1)
79
+ # category_filter = with(:category_id, 2)
80
+ # facet(:category_id, :exclude => category_filter)
81
+ # end
82
+ #
83
+ # Although the results of the above search will be restricted to those
84
+ # with a category_id of 2, the category_id facet will operate as if a
85
+ # category had not been selected, allowing the user to select additional
86
+ # categories (which will presumably be ORed together).
87
+ #
88
+ # It possible to exclude multiple filters by passing an array:
89
+ #
90
+ # Sunspot.search(Post) do
91
+ # with(:blog_id, 1)
92
+ # category_filter = with(:category_id, 2)
93
+ # author_filter = with(:author_id, 3)
94
+ # facet(:category_id,
95
+ # :exclude => [category_filter, author_filter].compact)
96
+ # end
97
+ #
98
+ # You should consider using +.compact+ to ensure that the array does not
99
+ # contain any nil values.
100
+ #
101
+ # <strong>As far as I can tell, Solr only supports multi-select with
102
+ # field facets; if +:exclude+ is passed to a query facet, this method will
103
+ # raise an error. Also, the +:only+ and +:extra+ options use query
104
+ # faceting under the hood, so these can't be used with +:extra+ either.
105
+ # </strong>
106
+ #
107
+ # ==== Query Facets
108
+ #
109
+ # A query facet is a collection of arbitrary scopes, each of which
110
+ # represents a row. This is specified by passing a block into the #facet
111
+ # method; the block then contains one or more +row+ blocks, each of which
112
+ # creates a query facet row. The +row+ blocks follow the usual Sunspot
113
+ # scope DSL.
114
+ #
115
+ # For example, a query facet can be used to facet over a set of ranges:
116
+ #
117
+ # Sunspot.search(Post) do
118
+ # facet(:average_rating) do
119
+ # row(1.0..2.0) do
120
+ # with(:average_rating, 1.0..2.0)
121
+ # end
122
+ # row(2.0..3.0) do
123
+ # with(:average_rating, 2.0..3.0)
124
+ # end
125
+ # row(3.0..4.0) do
126
+ # with(:average_rating, 3.0..4.0)
127
+ # end
128
+ # row(4.0..5.0) do
129
+ # with(:average_rating, 4.0..5.0)
130
+ # end
131
+ # end
132
+ # end
133
+ #
134
+ # Note that the arguments to the +facet+ and +row+ methods simply provide
135
+ # labels for the facet and its rows, so that they can be retrieved and
136
+ # identified from the Search object. They are not passed to Solr and no
137
+ # semantic meaning is attached to them. The label for +facet+ should be
138
+ # a symbol; the label for +row+ can be whatever you'd like.
139
+ #
140
+ # ==== Parameters
141
+ #
142
+ # field_names...<Symbol>:: fields for which to return field facets
143
+ #
144
+ # ==== Options
145
+ #
146
+ # :sort<Symbol>::
147
+ # Either :count (values matching the most terms first) or :index (lexical)
148
+ # :limit<Integer>::
149
+ # The maximum number of facet rows to return
150
+ # :minimum_count<Integer>::
151
+ # The minimum count a facet row must have to be returned
152
+ # :zeros<Boolean>::
153
+ # Return facet rows for which there are no matches (equivalent to
154
+ # :minimum_count => 0). Default is false.
155
+ # :exclude<Object,Array>::
156
+ # Exclude one or more filters when performing the faceting (see
157
+ # Multiselect Faceting above). The object given for this argument should
158
+ # be the return value(s) of a scoping method (+with+, +any_of+,
159
+ # +all_of+, etc.). <strong>Only can be used for field facets that do not
160
+ # use the +:extra+ or +:only+ options.</strong>
161
+ # :name<Symbol>::
162
+ # Give a custom name to a field facet. The main use case for this option
163
+ # is for requesting the same field facet multiple times, using different
164
+ # filter exclusions (see Multiselect Faceting above). If you pass this
165
+ # option, it is also the argument that should be passed to Search#facet
166
+ # when retrieving the facet result.
167
+ # :only<Array>::
168
+ # Only return facet rows for the given values. Useful if you are only
169
+ # interested in faceting on a subset of values for a given field.
170
+ # <strong>Only applies to field facets.</strong>
171
+ # :extra<Symbol,Array>::
172
+ # One or more of :any and :none. :any returns a facet row with a count
173
+ # of all matching documents that have some value for this field. :none
174
+ # returns a facet row with a count of all matching documents that have
175
+ # no value for this field. The facet row(s) corresponding to the extras
176
+ # have a value of the symbol passed. <strong>Only applies to field
177
+ # facets.</strong>
178
+ #
179
+ def facet(*field_names, &block)
180
+ options = Sunspot::Util.extract_options_from(field_names)
181
+
182
+ if block
183
+ if field_names.length != 1
184
+ raise(
185
+ ArgumentError,
186
+ "wrong number of arguments (#{field_names.length} for 1)"
187
+ )
188
+ end
189
+ if options.has_key?(:exclude)
190
+ raise(
191
+ ArgumentError,
192
+ "can't use :exclude with query facets"
193
+ )
194
+ end
195
+ search_facet = @search.add_query_facet(field_names.first, options)
196
+ Sunspot::Util.instance_eval_or_call(
197
+ QueryFacet.new(@query, @setup, search_facet),
198
+ &block
199
+ )
200
+ elsif options[:only]
201
+ if options.has_key?(:exclude)
202
+ raise(
203
+ ArgumentError,
204
+ "can't use :exclude with :only (see documentation)"
205
+ )
206
+ end
207
+ field_names.each do |field_name|
208
+ field = @setup.field(field_name)
209
+ search_facet = @search.add_field_facet(field, options)
210
+ Util.Array(options[:only]).each do |value|
211
+ facet = Sunspot::Query::QueryFacet.new
212
+ facet.add_positive_restriction(field, Sunspot::Query::Restriction::EqualTo, value)
213
+ @query.add_query_facet(facet)
214
+ search_facet.add_row(value, facet.to_boolean_phrase)
215
+ end
216
+ end
217
+ else
218
+ field_names.each do |field_name|
219
+ search_facet = nil
220
+ field = @setup.field(field_name)
221
+ facet =
222
+ if options[:time_range]
223
+ unless field.type.is_a?(Sunspot::Type::TimeType)
224
+ raise(
225
+ ArgumentError,
226
+ ':time_range can only be specified for Date or Time fields'
227
+ )
228
+ end
229
+ search_facet = @search.add_date_facet(field, options)
230
+ Sunspot::Query::DateFieldFacet.new(field, options)
231
+ else
232
+ search_facet = @search.add_field_facet(field, options)
233
+ Sunspot::Query::FieldFacet.new(field, options)
234
+ end
235
+ @query.add_field_facet(facet)
236
+ Util.Array(options[:extra]).each do |extra|
237
+ if options.has_key?(:exclude)
238
+ raise(
239
+ ArgumentError,
240
+ "can't use :exclude with :extra (see documentation)"
241
+ )
242
+ end
243
+ extra_facet = Sunspot::Query::QueryFacet.new
244
+ case extra
245
+ when :any
246
+ extra_facet.add_negated_restriction(
247
+ field,
248
+ Sunspot::Query::Restriction::EqualTo,
249
+ nil
250
+ )
251
+ when :none
252
+ extra_facet.add_positive_restriction(
253
+ field,
254
+ Sunspot::Query::Restriction::EqualTo,
255
+ nil
256
+ )
257
+ else
258
+ raise(
259
+ ArgumentError,
260
+ "Allowed values for :extra are :any and :none"
261
+ )
262
+ end
263
+ search_facet.add_row(extra, extra_facet.to_boolean_phrase)
264
+ @query.add_query_facet(extra_facet)
265
+ end
266
+ end
267
+ end
268
+ end
269
+
270
+ def dynamic(base_name, &block)
271
+ dynamic_field_factory = @setup.dynamic_field_factory(base_name)
272
+ Sunspot::Util.instance_eval_or_call(
273
+ FieldQuery.new(@search, @query, dynamic_field_factory),
274
+ &block
275
+ )
276
+ end
277
+ end
278
+ end
279
+ end
@@ -0,0 +1,103 @@
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
+ # Index-time boost that should be applied to this field for keyword search
30
+ # :default_boost<Float>::
31
+ # Default search-time boost to apply to this field during keyword
32
+ # search. Can be overriden with DSL::Fulltext#fields or
33
+ # DSL::Fulltext#boost_fields method.
34
+ #
35
+ def text(*names, &block)
36
+ options = names.pop if names.last.is_a?(Hash)
37
+ names.each do |name|
38
+ @setup.add_text_field_factory(
39
+ name,
40
+ options || {},
41
+ &block
42
+ )
43
+ end
44
+ end
45
+
46
+ #
47
+ # Specify a document-level boost. As with fields, you have the option of
48
+ # passing an attribute name which will be called on each model, or a block
49
+ # to be evaluated in the model's context. As well as these two options,
50
+ # this method can also take a constant number, meaning that all indexed
51
+ # documents of this class will have the specified boost.
52
+ #
53
+ # ==== Parameters
54
+ #
55
+ # attr_name<Symbol,~.to_f>:: Attribute name to call or a numeric constant
56
+ #
57
+ def boost(attr_name = nil, &block)
58
+ @setup.add_document_boost(attr_name, &block)
59
+ end
60
+
61
+ # method_missing is used to provide access to typed fields, because
62
+ # developers should be able to add new Sunspot::Type implementations
63
+ # dynamically and have them recognized inside the Fields DSL. Like #text,
64
+ # these methods will create a VirtualField if a block is passed, or an
65
+ # AttributeField if not.
66
+ #
67
+ # ==== Example
68
+ #
69
+ # Sunspot.setup(File) do
70
+ # time :mtime
71
+ # end
72
+ #
73
+ # The call to +time+ will create a field of type Sunspot::Types::TimeType
74
+ #
75
+ def method_missing(method, *args, &block)
76
+ options = Util.extract_options_from(args)
77
+ type_const_name = "#{Util.camel_case(method.to_s.sub(/^dynamic_/, ''))}Type"
78
+ trie = options.delete(:trie)
79
+ type_const_name = "Trie#{type_const_name}" if trie
80
+ begin
81
+ type_class = Type.const_get(type_const_name)
82
+ rescue(NameError)
83
+ if trie
84
+ raise ArgumentError, "Trie fields are only valid for numeric and time types"
85
+ else
86
+ super(method, *args, &block)
87
+ end
88
+ end
89
+ type = type_class.instance
90
+ name = args.shift
91
+ if method.to_s =~ /^dynamic_/
92
+ if type.accepts_dynamic?
93
+ @setup.add_dynamic_field_factory(name, type, options, &block)
94
+ else
95
+ super(method, *args, &block)
96
+ end
97
+ else
98
+ @setup.add_field_factory(name, type, options, &block)
99
+ end
100
+ end
101
+ end
102
+ end
103
+ end