sunspot_rbg 1.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (222) 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/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/adjustable.rb +47 -0
  17. data/lib/sunspot/dsl/field_query.rb +279 -0
  18. data/lib/sunspot/dsl/fields.rb +103 -0
  19. data/lib/sunspot/dsl/fulltext.rb +243 -0
  20. data/lib/sunspot/dsl/function.rb +14 -0
  21. data/lib/sunspot/dsl/functional.rb +44 -0
  22. data/lib/sunspot/dsl/more_like_this_query.rb +56 -0
  23. data/lib/sunspot/dsl/paginatable.rb +28 -0
  24. data/lib/sunspot/dsl/query_facet.rb +36 -0
  25. data/lib/sunspot/dsl/restriction.rb +25 -0
  26. data/lib/sunspot/dsl/restriction_with_near.rb +121 -0
  27. data/lib/sunspot/dsl/scope.rb +217 -0
  28. data/lib/sunspot/dsl/search.rb +30 -0
  29. data/lib/sunspot/dsl/standard_query.rb +121 -0
  30. data/lib/sunspot/dsl.rb +5 -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/library_installer.rb +45 -0
  35. data/lib/sunspot/installer/schema_builder.rb +219 -0
  36. data/lib/sunspot/installer/solrconfig_updater.rb +76 -0
  37. data/lib/sunspot/installer/task_helper.rb +18 -0
  38. data/lib/sunspot/installer.rb +31 -0
  39. data/lib/sunspot/query/abstract_field_facet.rb +52 -0
  40. data/lib/sunspot/query/boost_query.rb +24 -0
  41. data/lib/sunspot/query/common_query.rb +85 -0
  42. data/lib/sunspot/query/composite_fulltext.rb +36 -0
  43. data/lib/sunspot/query/connective.rb +206 -0
  44. data/lib/sunspot/query/date_field_facet.rb +14 -0
  45. data/lib/sunspot/query/dismax.rb +128 -0
  46. data/lib/sunspot/query/field_facet.rb +41 -0
  47. data/lib/sunspot/query/filter.rb +38 -0
  48. data/lib/sunspot/query/function_query.rb +52 -0
  49. data/lib/sunspot/query/geo.rb +53 -0
  50. data/lib/sunspot/query/highlighting.rb +55 -0
  51. data/lib/sunspot/query/more_like_this.rb +61 -0
  52. data/lib/sunspot/query/more_like_this_query.rb +12 -0
  53. data/lib/sunspot/query/pagination.rb +38 -0
  54. data/lib/sunspot/query/query_facet.rb +16 -0
  55. data/lib/sunspot/query/restriction.rb +262 -0
  56. data/lib/sunspot/query/scope.rb +9 -0
  57. data/lib/sunspot/query/sort.rb +95 -0
  58. data/lib/sunspot/query/sort_composite.rb +33 -0
  59. data/lib/sunspot/query/standard_query.rb +16 -0
  60. data/lib/sunspot/query/text_field_boost.rb +17 -0
  61. data/lib/sunspot/query.rb +11 -0
  62. data/lib/sunspot/schema.rb +151 -0
  63. data/lib/sunspot/search/abstract_search.rb +293 -0
  64. data/lib/sunspot/search/date_facet.rb +35 -0
  65. data/lib/sunspot/search/facet_row.rb +27 -0
  66. data/lib/sunspot/search/field_facet.rb +88 -0
  67. data/lib/sunspot/search/highlight.rb +38 -0
  68. data/lib/sunspot/search/hit.rb +136 -0
  69. data/lib/sunspot/search/more_like_this_search.rb +31 -0
  70. data/lib/sunspot/search/paginated_collection.rb +55 -0
  71. data/lib/sunspot/search/query_facet.rb +67 -0
  72. data/lib/sunspot/search/standard_search.rb +21 -0
  73. data/lib/sunspot/search.rb +9 -0
  74. data/lib/sunspot/server.rb +152 -0
  75. data/lib/sunspot/session.rb +260 -0
  76. data/lib/sunspot/session_proxy/abstract_session_proxy.rb +29 -0
  77. data/lib/sunspot/session_proxy/class_sharding_session_proxy.rb +66 -0
  78. data/lib/sunspot/session_proxy/id_sharding_session_proxy.rb +89 -0
  79. data/lib/sunspot/session_proxy/master_slave_session_proxy.rb +43 -0
  80. data/lib/sunspot/session_proxy/sharding_session_proxy.rb +222 -0
  81. data/lib/sunspot/session_proxy/silent_fail_session_proxy.rb +42 -0
  82. data/lib/sunspot/session_proxy/thread_local_session_proxy.rb +37 -0
  83. data/lib/sunspot/session_proxy.rb +87 -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/lib/sunspot.rb +569 -0
  90. data/lib/sunspot_rbg.rb +7 -0
  91. data/log/.gitignore +1 -0
  92. data/pkg/.gitignore +1 -0
  93. data/script/console +10 -0
  94. data/solr/README.txt +42 -0
  95. data/solr/etc/jetty.xml +218 -0
  96. data/solr/etc/webdefault.xml +379 -0
  97. data/solr/lib/jetty-6.1.3.jar +0 -0
  98. data/solr/lib/jetty-util-6.1.3.jar +0 -0
  99. data/solr/lib/jsp-2.1/ant-1.6.5.jar +0 -0
  100. data/solr/lib/jsp-2.1/core-3.1.1.jar +0 -0
  101. data/solr/lib/jsp-2.1/jsp-2.1.jar +0 -0
  102. data/solr/lib/jsp-2.1/jsp-api-2.1.jar +0 -0
  103. data/solr/lib/servlet-api-2.5-6.1.3.jar +0 -0
  104. data/solr/logs/.gitignore +1 -0
  105. data/solr/solr/.gitignore +1 -0
  106. data/solr/solr/README.txt +54 -0
  107. data/solr/solr/conf/admin-extra.html +31 -0
  108. data/solr/solr/conf/elevate.xml +36 -0
  109. data/solr/solr/conf/mapping-ISOLatin1Accent.txt +246 -0
  110. data/solr/solr/conf/protwords.txt +21 -0
  111. data/solr/solr/conf/schema.xml +238 -0
  112. data/solr/solr/conf/scripts.conf +24 -0
  113. data/solr/solr/conf/solrconfig.xml +934 -0
  114. data/solr/solr/conf/spellings.txt +2 -0
  115. data/solr/solr/conf/stopwords.txt +58 -0
  116. data/solr/solr/conf/synonyms.txt +31 -0
  117. data/solr/solr/conf/xslt/example.xsl +132 -0
  118. data/solr/solr/conf/xslt/example_atom.xsl +67 -0
  119. data/solr/solr/conf/xslt/example_rss.xsl +66 -0
  120. data/solr/solr/conf/xslt/luke.xsl +337 -0
  121. data/solr/start.jar +0 -0
  122. data/solr/webapps/solr.war +0 -0
  123. data/solr-1.3/etc/jetty.xml +212 -0
  124. data/solr-1.3/etc/webdefault.xml +379 -0
  125. data/solr-1.3/lib/jetty-6.1.3.jar +0 -0
  126. data/solr-1.3/lib/jetty-util-6.1.3.jar +0 -0
  127. data/solr-1.3/lib/jsp-2.1/ant-1.6.5.jar +0 -0
  128. data/solr-1.3/lib/jsp-2.1/core-3.1.1.jar +0 -0
  129. data/solr-1.3/lib/jsp-2.1/jsp-2.1.jar +0 -0
  130. data/solr-1.3/lib/jsp-2.1/jsp-api-2.1.jar +0 -0
  131. data/solr-1.3/lib/servlet-api-2.5-6.1.3.jar +0 -0
  132. data/solr-1.3/solr/conf/elevate.xml +36 -0
  133. data/solr-1.3/solr/conf/protwords.txt +21 -0
  134. data/solr-1.3/solr/conf/schema.xml +64 -0
  135. data/solr-1.3/solr/conf/solrconfig.xml +725 -0
  136. data/solr-1.3/solr/conf/stopwords.txt +57 -0
  137. data/solr-1.3/solr/conf/synonyms.txt +31 -0
  138. data/solr-1.3/solr/lib/geoapi-nogenerics-2.1-M2.jar +0 -0
  139. data/solr-1.3/solr/lib/gt2-referencing-2.3.1.jar +0 -0
  140. data/solr-1.3/solr/lib/jsr108-0.01.jar +0 -0
  141. data/solr-1.3/solr/lib/locallucene.jar +0 -0
  142. data/solr-1.3/solr/lib/localsolr.jar +0 -0
  143. data/solr-1.3/start.jar +0 -0
  144. data/solr-1.3/webapps/solr.war +0 -0
  145. data/spec/api/adapters_spec.rb +33 -0
  146. data/spec/api/binding_spec.rb +50 -0
  147. data/spec/api/indexer/attributes_spec.rb +149 -0
  148. data/spec/api/indexer/batch_spec.rb +46 -0
  149. data/spec/api/indexer/dynamic_fields_spec.rb +42 -0
  150. data/spec/api/indexer/fixed_fields_spec.rb +57 -0
  151. data/spec/api/indexer/fulltext_spec.rb +43 -0
  152. data/spec/api/indexer/removal_spec.rb +53 -0
  153. data/spec/api/indexer/spec_helper.rb +1 -0
  154. data/spec/api/indexer_spec.rb +14 -0
  155. data/spec/api/query/advanced_manipulation_examples.rb +35 -0
  156. data/spec/api/query/connectives_examples.rb +189 -0
  157. data/spec/api/query/dsl_spec.rb +18 -0
  158. data/spec/api/query/dynamic_fields_examples.rb +165 -0
  159. data/spec/api/query/faceting_examples.rb +397 -0
  160. data/spec/api/query/fulltext_examples.rb +313 -0
  161. data/spec/api/query/function_spec.rb +70 -0
  162. data/spec/api/query/geo_examples.rb +68 -0
  163. data/spec/api/query/highlighting_examples.rb +223 -0
  164. data/spec/api/query/more_like_this_spec.rb +140 -0
  165. data/spec/api/query/ordering_pagination_examples.rb +95 -0
  166. data/spec/api/query/scope_examples.rb +275 -0
  167. data/spec/api/query/spec_helper.rb +1 -0
  168. data/spec/api/query/standard_spec.rb +28 -0
  169. data/spec/api/query/text_field_scoping_examples.rb +30 -0
  170. data/spec/api/query/types_spec.rb +20 -0
  171. data/spec/api/search/dynamic_fields_spec.rb +33 -0
  172. data/spec/api/search/faceting_spec.rb +360 -0
  173. data/spec/api/search/highlighting_spec.rb +69 -0
  174. data/spec/api/search/hits_spec.rb +120 -0
  175. data/spec/api/search/paginated_collection_spec.rb +26 -0
  176. data/spec/api/search/results_spec.rb +66 -0
  177. data/spec/api/search/search_spec.rb +23 -0
  178. data/spec/api/search/spec_helper.rb +1 -0
  179. data/spec/api/server_spec.rb +91 -0
  180. data/spec/api/session_proxy/class_sharding_session_proxy_spec.rb +85 -0
  181. data/spec/api/session_proxy/id_sharding_session_proxy_spec.rb +30 -0
  182. data/spec/api/session_proxy/master_slave_session_proxy_spec.rb +41 -0
  183. data/spec/api/session_proxy/sharding_session_proxy_spec.rb +77 -0
  184. data/spec/api/session_proxy/silent_fail_session_proxy_spec.rb +24 -0
  185. data/spec/api/session_proxy/spec_helper.rb +9 -0
  186. data/spec/api/session_proxy/thread_local_session_proxy_spec.rb +50 -0
  187. data/spec/api/session_spec.rb +220 -0
  188. data/spec/api/spec_helper.rb +3 -0
  189. data/spec/api/sunspot_spec.rb +18 -0
  190. data/spec/ext.rb +11 -0
  191. data/spec/helpers/indexer_helper.rb +29 -0
  192. data/spec/helpers/query_helper.rb +38 -0
  193. data/spec/helpers/search_helper.rb +80 -0
  194. data/spec/integration/dynamic_fields_spec.rb +55 -0
  195. data/spec/integration/faceting_spec.rb +238 -0
  196. data/spec/integration/highlighting_spec.rb +22 -0
  197. data/spec/integration/indexing_spec.rb +33 -0
  198. data/spec/integration/keyword_search_spec.rb +317 -0
  199. data/spec/integration/local_search_spec.rb +64 -0
  200. data/spec/integration/more_like_this_spec.rb +43 -0
  201. data/spec/integration/scoped_search_spec.rb +354 -0
  202. data/spec/integration/spec_helper.rb +7 -0
  203. data/spec/integration/stored_fields_spec.rb +10 -0
  204. data/spec/integration/test_pagination.rb +32 -0
  205. data/spec/mocks/adapters.rb +32 -0
  206. data/spec/mocks/blog.rb +3 -0
  207. data/spec/mocks/comment.rb +21 -0
  208. data/spec/mocks/connection.rb +126 -0
  209. data/spec/mocks/mock_adapter.rb +30 -0
  210. data/spec/mocks/mock_class_sharding_session_proxy.rb +24 -0
  211. data/spec/mocks/mock_record.rb +52 -0
  212. data/spec/mocks/mock_sharding_session_proxy.rb +15 -0
  213. data/spec/mocks/photo.rb +11 -0
  214. data/spec/mocks/post.rb +85 -0
  215. data/spec/mocks/super_class.rb +2 -0
  216. data/spec/mocks/user.rb +13 -0
  217. data/spec/spec_helper.rb +30 -0
  218. data/sunspot.gemspec +40 -0
  219. data/tasks/rdoc.rake +27 -0
  220. data/tasks/schema.rake +19 -0
  221. data/tasks/todo.rake +4 -0
  222. metadata +457 -0
@@ -0,0 +1,41 @@
1
+ module Sunspot
2
+ module Query
3
+ class FieldFacet < AbstractFieldFacet
4
+ def initialize(field, options)
5
+ if exclude_filters = options[:exclude]
6
+ @exclude_tag = Util.Array(exclude_filters).map do |filter|
7
+ filter.tag
8
+ end.join(',')
9
+ end
10
+ super
11
+ end
12
+
13
+ def to_params
14
+ super.merge(:"facet.field" => [field_name_with_local_params])
15
+ end
16
+
17
+ private
18
+
19
+ def local_params
20
+ @local_params ||=
21
+ begin
22
+ local_params = {}
23
+ local_params[:ex] = @exclude_tag if @exclude_tag
24
+ local_params[:key] = @options[:name] if @options[:name]
25
+ local_params
26
+ end
27
+ end
28
+
29
+ def field_name_with_local_params
30
+ if local_params.empty?
31
+ @field.indexed_name
32
+ else
33
+ pairs = local_params.map do |key, value|
34
+ "#{key}=#{value}"
35
+ end
36
+ "{!#{pairs.join(' ')}}#{@field.indexed_name}"
37
+ end
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,38 @@
1
+ module Sunspot
2
+ module Query
3
+ module Filter
4
+
5
+ #
6
+ # Express this filter as an :fq parameter; i.e., the boolean phrase,
7
+ # maybe prefixed by local params.
8
+ #
9
+ def to_filter_query
10
+ if tagged? then "{!tag=#{tag}}#{to_boolean_phrase}"
11
+ else to_boolean_phrase
12
+ end
13
+ end
14
+
15
+ #
16
+ # Generate and return a tag that can be attached to this restriction,
17
+ # for use with multiselect faceting. This needs to be unique, but doesn't
18
+ # really need to be human-readable, so just generate a string based on the
19
+ # hash of the boolean phrase.
20
+ #
21
+ def tag
22
+ @tag ||= to_boolean_phrase.hash.abs.to_s(36)
23
+ end
24
+
25
+ private
26
+
27
+ #
28
+ # True if a tag has been generated for this filter (e.g., if it's been
29
+ # excluded from a given facet). If a tag has not been generated at the
30
+ # time that the filter query param is requested, then it is not necessary
31
+ # to include a tag in the local params.
32
+ #
33
+ def tagged?
34
+ !!@tag
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,52 @@
1
+ module Sunspot
2
+ module Query
3
+ #
4
+ # Abstract class for function queries.
5
+ #
6
+ class FunctionQuery
7
+ include RSolr::Char
8
+ end
9
+
10
+ #
11
+ # Function query which represents a constant.
12
+ #
13
+ class ConstantFunctionQuery < FunctionQuery
14
+ def initialize(constant)
15
+ @constant = constant
16
+ end
17
+
18
+ def to_s
19
+ Type.to_literal(@constant)
20
+ end
21
+ end
22
+
23
+ #
24
+ # Function query which represents a field.
25
+ #
26
+ class FieldFunctionQuery < FunctionQuery
27
+ def initialize(field)
28
+ @field = field
29
+ end
30
+
31
+ def to_s
32
+ "#{escape(@field.indexed_name)}"
33
+ end
34
+ end
35
+
36
+ #
37
+ # Function query which represents an actual function invocation.
38
+ # Takes a function name and arguments as parameters.
39
+ # Arguments are in turn FunctionQuery objects.
40
+ #
41
+ class FunctionalFunctionQuery < FunctionQuery
42
+ def initialize(function_name, function_args)
43
+ @function_name, @function_args = function_name, function_args
44
+ end
45
+
46
+ def to_s
47
+ params = @function_args.map { |arg| arg.to_s }.join(",")
48
+ "#{@function_name}(#{params})"
49
+ end
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,53 @@
1
+ begin
2
+ require 'geohash'
3
+ rescue LoadError => e
4
+ require 'pr_geohash'
5
+ end
6
+
7
+ module Sunspot
8
+ module Query
9
+ class Geo
10
+ MAX_PRECISION = 12
11
+ DEFAULT_PRECISION = 7
12
+ DEFAULT_PRECISION_FACTOR = 16.0
13
+
14
+ def initialize(field, lat, lng, options)
15
+ @field, @options = field, options
16
+ @geohash = GeoHash.encode(lat.to_f, lng.to_f, MAX_PRECISION)
17
+ end
18
+
19
+ def to_params
20
+ { :q => to_boolean_query }
21
+ end
22
+
23
+ def to_subquery
24
+ "(#{to_boolean_query})"
25
+ end
26
+
27
+ private
28
+
29
+ def to_boolean_query
30
+ queries = []
31
+ MAX_PRECISION.downto(precision) do |i|
32
+ star = i == MAX_PRECISION ? '' : '*'
33
+ precision_boost = Util.format_float(
34
+ boost * precision_factor ** (i-MAX_PRECISION).to_f, 3)
35
+ queries << "#{@field.indexed_name}:#{@geohash[0, i]}#{star}^#{precision_boost}"
36
+ end
37
+ queries.join(' OR ')
38
+ end
39
+
40
+ def precision
41
+ @options[:precision] || DEFAULT_PRECISION
42
+ end
43
+
44
+ def precision_factor
45
+ @options[:precision_factor] || DEFAULT_PRECISION_FACTOR
46
+ end
47
+
48
+ def boost
49
+ @options[:boost] || 1.0
50
+ end
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,55 @@
1
+ module Sunspot
2
+ module Query
3
+ #
4
+ # A query component that builds parameters for requesting highlights
5
+ #
6
+ class Highlighting #:nodoc:
7
+ def initialize(fields=[], options={})
8
+ @fields = fields
9
+ @options = options
10
+ end
11
+
12
+ #
13
+ # Return Solr highlighting params
14
+ #
15
+ def to_params
16
+ params = {
17
+ :hl => 'on',
18
+ :"hl.simple.pre" => '@@@hl@@@',
19
+ :"hl.simple.post" => '@@@endhl@@@'
20
+ }
21
+ unless @fields.empty?
22
+ params[:"hl.fl"] = @fields.map { |field| field.indexed_name }
23
+ end
24
+ if max_snippets = @options[:max_snippets]
25
+ params.merge!(make_params('snippets', max_snippets))
26
+ end
27
+ if fragment_size = @options[:fragment_size]
28
+ params.merge!(make_params('fragsize', fragment_size))
29
+ end
30
+ if @options[:merge_contiguous_fragments]
31
+ params.merge!(make_params('mergeContiguous', 'true'))
32
+ end
33
+ if @options[:phrase_highlighter]
34
+ params.merge!(make_params('usePhraseHighlighter', 'true'))
35
+ if @options[:require_field_match]
36
+ params.merge!(make_params('requireFieldMatch', 'true'))
37
+ end
38
+ end
39
+ params
40
+ end
41
+
42
+ private
43
+
44
+ def make_params(name, value)
45
+ if @fields.empty?
46
+ { :"hl.#{name}" => value }
47
+ else
48
+ @fields.inject({}) do |hash, field|
49
+ hash.merge!(:"f.#{field.indexed_name}.hl.#{name}" => value)
50
+ end
51
+ end
52
+ end
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,61 @@
1
+ module Sunspot
2
+ module Query
3
+ class MoreLikeThis
4
+ attr_reader :fields
5
+
6
+ def initialize(document)
7
+ @document_scope = Restriction::EqualTo.new(
8
+ false,
9
+ IdField.instance,
10
+ Adapters::InstanceAdapter.adapt(document).index_id
11
+ )
12
+ @fields = {}
13
+ @params = {}
14
+ end
15
+
16
+ def add_field(field, boost = nil)
17
+ raise(ArgumentError, "Field #{field.name} is not set up for more_like_this") unless field.more_like_this?
18
+ @fields[field.indexed_name] = TextFieldBoost.new(field, boost)
19
+ end
20
+
21
+ def minimum_term_frequency=(mintf)
22
+ @params[:"mlt.mintf"] = mintf
23
+ end
24
+
25
+ def minimum_document_frequency=(mindf)
26
+ @params[:"mlt.mindf"] = mindf
27
+ end
28
+
29
+ def minimum_word_length=(minwl)
30
+ @params[:"mlt.minwl"] = minwl
31
+ end
32
+
33
+ def maximum_word_length=(maxwl)
34
+ @params[:"mlt.maxwl"] = maxwl
35
+ end
36
+
37
+ def maximum_query_terms=(maxqt)
38
+ @params[:"mlt.maxqt"] = maxqt
39
+ end
40
+
41
+ def boost_by_relevance=(should_boost)
42
+ @params[:"mlt.boost"] = should_boost
43
+ end
44
+
45
+ def to_params
46
+ params = Sunspot::Util.deep_merge(
47
+ @params,
48
+ :q => @document_scope.to_boolean_phrase
49
+ )
50
+ params[:"mlt.fl"] = @fields.keys.join(",")
51
+ boosted_fields = @fields.values.select { |field| field.boost }
52
+ unless boosted_fields.empty?
53
+ params[:qf] = boosted_fields.map do |field|
54
+ field.to_boosted_field
55
+ end.join(' ')
56
+ end
57
+ params
58
+ end
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,12 @@
1
+ module Sunspot
2
+ module Query
3
+ class MoreLikeThisQuery < CommonQuery
4
+ attr_accessor :scope, :more_like_this
5
+
6
+ def initialize(document, types)
7
+ super(types)
8
+ @components << @more_like_this = MoreLikeThis.new(document)
9
+ end
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,38 @@
1
+ module Sunspot
2
+ module Query
3
+ #
4
+ # A query component that holds information about pagination. Unlike other
5
+ # query components, this one is mutable, because the query itself holds a
6
+ # reference to it and updates it if pagination is changed.
7
+ #
8
+ class Pagination #:nodoc:
9
+ attr_reader :page, :per_page
10
+
11
+ def initialize(page = nil, per_page = nil)
12
+ self.page, self.per_page = page, per_page
13
+ end
14
+
15
+ def to_params
16
+ { :start => start, :rows => rows }
17
+ end
18
+
19
+ def page=(page)
20
+ @page = page.to_i if page
21
+ end
22
+
23
+ def per_page=(per_page)
24
+ @per_page = per_page.to_i if per_page
25
+ end
26
+
27
+ private
28
+
29
+ def start
30
+ (@page - 1) * @per_page
31
+ end
32
+
33
+ def rows
34
+ @per_page
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,16 @@
1
+ module Sunspot
2
+ module Query
3
+ class QueryFacet < Connective::Conjunction
4
+ def to_params
5
+ if @components.empty?
6
+ {}
7
+ else
8
+ {
9
+ :facet => 'true',
10
+ :"facet.query" => to_boolean_phrase
11
+ }
12
+ end
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,262 @@
1
+ module Sunspot
2
+ module Query
3
+ module Restriction #:nodoc:
4
+ class <<self
5
+ #
6
+ # Return the names of all of the restriction classes that should be made
7
+ # available to the DSL.
8
+ #
9
+ # ==== Returns
10
+ #
11
+ # Array:: Collection of restriction class names
12
+ #
13
+ def names
14
+ constants - %w(Base) #XXX this seems ugly
15
+ end
16
+
17
+ #
18
+ # Convenience method to access a restriction class by an underscored
19
+ # symbol or string
20
+ #
21
+ def [](restriction_name)
22
+ @types ||= {}
23
+ @types[restriction_name.to_sym] ||= const_get(Sunspot::Util.camel_case(restriction_name.to_s))
24
+ end
25
+ end
26
+
27
+ #
28
+ # Subclasses of this class represent restrictions that can be applied to
29
+ # a Sunspot query. The Sunspot::DSL::Restriction class presents a builder
30
+ # API for instances of this class.
31
+ #
32
+ # Implementations of this class must respond to #to_params and
33
+ # #to_negated_params. Instead of implementing those methods, they may
34
+ # choose to implement any of:
35
+ #
36
+ # * #to_positive_boolean_phrase, and optionally #to_negated_boolean_phrase
37
+ # * #to_solr_conditional
38
+ #
39
+ class Base #:nodoc:
40
+ include Filter
41
+ include RSolr::Char
42
+
43
+ RESERVED_WORDS = Set['AND', 'OR', 'NOT']
44
+
45
+ def initialize(negated, field, value)
46
+ raise ArgumentError.new("RFCTR") unless [true, false].include?(negated)
47
+ @negated, @field, @value = negated, field, value
48
+ end
49
+
50
+ #
51
+ # A hash representing this restriction in solr-ruby's parameter format.
52
+ # All restriction implementations must respond to this method; however,
53
+ # the base implementation delegates to the #to_positive_boolean_phrase method, so
54
+ # subclasses may (and probably should) choose to implement that method
55
+ # instead.
56
+ #
57
+ # ==== Returns
58
+ #
59
+ # Hash:: Representation of this restriction as solr-ruby parameters
60
+ #
61
+ def to_params
62
+ { :fq => [to_filter_query] }
63
+ end
64
+
65
+ #
66
+ # Return the boolean phrase associated with this restriction object.
67
+ # Differentiates between positive and negated boolean phrases depending
68
+ # on whether this restriction is negated.
69
+ #
70
+ def to_boolean_phrase
71
+ unless negated?
72
+ to_positive_boolean_phrase
73
+ else
74
+ to_negated_boolean_phrase
75
+ end
76
+ end
77
+
78
+ #
79
+ # Boolean phrase representing this restriction in the positive. Subclasses
80
+ # may choose to implement this method rather than #to_params; however,
81
+ # this method delegates to the abstract #to_solr_conditional method, which
82
+ # in most cases will be what subclasses will want to implement.
83
+ # #to_solr_conditional contains the boolean phrase representing the
84
+ # condition but leaves out the field name (see built-in implementations
85
+ # for examples)
86
+ #
87
+ # ==== Returns
88
+ #
89
+ # String:: Boolean phrase for restriction in the positive
90
+ #
91
+ def to_positive_boolean_phrase
92
+ "#{escape(@field.indexed_name)}:#{to_solr_conditional}"
93
+ end
94
+
95
+ #
96
+ # Boolean phrase representing this restriction in the negated. Subclasses
97
+ # may choose to implement this method, but it is not necessary, as the
98
+ # base implementation delegates to #to_positive_boolean_phrase.
99
+ #
100
+ # ==== Returns
101
+ #
102
+ # String:: Boolean phrase for restriction in the negated
103
+ #
104
+ def to_negated_boolean_phrase
105
+ "-#{to_positive_boolean_phrase}"
106
+ end
107
+
108
+ #
109
+ # Whether this restriction should be negated from its original meaning
110
+ #
111
+ def negated? #:nodoc:
112
+ !!@negated
113
+ end
114
+
115
+ #
116
+ # Return a new restriction that is the negated version of this one. It
117
+ # is used by disjunction denormalization.
118
+ #
119
+ def negate
120
+ self.class.new(!@negated, @field, @value)
121
+ end
122
+
123
+ protected
124
+
125
+ #
126
+ # Return escaped Solr API representation of given value
127
+ #
128
+ # ==== Parameters
129
+ #
130
+ # value<Object>::
131
+ # value to convert to Solr representation (default: @value)
132
+ #
133
+ # ==== Returns
134
+ #
135
+ # String:: Solr API representation of given value
136
+ #
137
+ def solr_value(value = @value)
138
+ solr_value = escape(@field.to_indexed(value))
139
+ if RESERVED_WORDS.include?(solr_value)
140
+ %Q("#{solr_value}")
141
+ else
142
+ solr_value
143
+ end
144
+ end
145
+ end
146
+
147
+ #
148
+ # Results must have field with value equal to given value. If the value
149
+ # is nil, results must have no value for the given field.
150
+ #
151
+ class EqualTo < Base
152
+ def to_positive_boolean_phrase
153
+ unless @value.nil?
154
+ super
155
+ else
156
+ "#{escape(@field.indexed_name)}:[* TO *]"
157
+ end
158
+ end
159
+
160
+ def negated?
161
+ if @value.nil?
162
+ !super
163
+ else
164
+ super
165
+ end
166
+ end
167
+
168
+ private
169
+
170
+ def to_solr_conditional
171
+ "#{solr_value}"
172
+ end
173
+ end
174
+
175
+ #
176
+ # Results must have field with value less than given value
177
+ #
178
+ class LessThan < Base
179
+ private
180
+
181
+ def solr_value(value = @value)
182
+ solr_value = super
183
+ solr_value = "\"#{solr_value}\"" if solr_value.index(' ')
184
+ solr_value
185
+ end
186
+
187
+ def to_solr_conditional
188
+ "[* TO #{solr_value}]"
189
+ end
190
+ end
191
+
192
+ #
193
+ # Results must have field with value greater than given value
194
+ #
195
+ class GreaterThan < Base
196
+ private
197
+
198
+ def solr_value(value = @value)
199
+ solr_value = super
200
+ solr_value = "\"#{solr_value}\"" if solr_value.index(' ')
201
+ solr_value
202
+ end
203
+
204
+ def to_solr_conditional
205
+ "[#{solr_value} TO *]"
206
+ end
207
+ end
208
+
209
+ #
210
+ # Results must have field with value in given range
211
+ #
212
+ class Between < Base
213
+ private
214
+
215
+ def solr_value(value = @value)
216
+ solr_value = super
217
+ solr_value = "\"#{solr_value}\"" if solr_value.index(' ')
218
+ solr_value
219
+ end
220
+
221
+ def to_solr_conditional
222
+ "[#{solr_value(@value.first)} TO #{solr_value(@value.last)}]"
223
+ end
224
+ end
225
+
226
+ #
227
+ # Results must have field with value included in given collection
228
+ #
229
+ class AnyOf < Base
230
+ private
231
+
232
+ def to_solr_conditional
233
+ "(#{@value.map { |v| solr_value v } * ' OR '})"
234
+ end
235
+ end
236
+
237
+ #
238
+ # Results must have field with values matching all values in given
239
+ # collection (only makes sense for fields with multiple values)
240
+ #
241
+ class AllOf < Base
242
+ private
243
+
244
+ def to_solr_conditional
245
+ "(#{@value.map { |v| solr_value v } * ' AND '})"
246
+ end
247
+ end
248
+
249
+ #
250
+ # Results must have a field with a value that begins with the argument.
251
+ # Most useful for strings, but in theory will work with anything.
252
+ #
253
+ class StartingWith < Base
254
+ private
255
+
256
+ def to_solr_conditional
257
+ "#{solr_value(@value)}*"
258
+ end
259
+ end
260
+ end
261
+ end
262
+ end
@@ -0,0 +1,9 @@
1
+ module Sunspot
2
+ module Query
3
+ class Scope < Connective::Conjunction
4
+ def to_params
5
+ { :fq => @components.map { |component| component.to_filter_query }}
6
+ end
7
+ end
8
+ end
9
+ end