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.
- data/.gitignore +12 -0
- data/Gemfile +4 -0
- data/History.txt +222 -0
- data/LICENSE +18 -0
- data/Rakefile +17 -0
- data/TODO +13 -0
- data/VERSION.yml +4 -0
- data/bin/sunspot-installer +19 -0
- data/bin/sunspot-solr +74 -0
- data/installer/config/schema.yml +95 -0
- data/lib/light_config.rb +40 -0
- data/lib/sunspot.rb +569 -0
- data/lib/sunspot/adapters.rb +265 -0
- data/lib/sunspot/composite_setup.rb +202 -0
- data/lib/sunspot/configuration.rb +46 -0
- data/lib/sunspot/data_extractor.rb +50 -0
- data/lib/sunspot/dsl.rb +5 -0
- data/lib/sunspot/dsl/adjustable.rb +47 -0
- data/lib/sunspot/dsl/field_query.rb +279 -0
- data/lib/sunspot/dsl/fields.rb +103 -0
- data/lib/sunspot/dsl/fulltext.rb +243 -0
- data/lib/sunspot/dsl/function.rb +14 -0
- data/lib/sunspot/dsl/functional.rb +44 -0
- data/lib/sunspot/dsl/more_like_this_query.rb +56 -0
- data/lib/sunspot/dsl/paginatable.rb +28 -0
- data/lib/sunspot/dsl/query_facet.rb +36 -0
- data/lib/sunspot/dsl/restriction.rb +25 -0
- data/lib/sunspot/dsl/restriction_with_near.rb +121 -0
- data/lib/sunspot/dsl/scope.rb +217 -0
- data/lib/sunspot/dsl/search.rb +30 -0
- data/lib/sunspot/dsl/standard_query.rb +121 -0
- data/lib/sunspot/field.rb +193 -0
- data/lib/sunspot/field_factory.rb +129 -0
- data/lib/sunspot/indexer.rb +131 -0
- data/lib/sunspot/installer.rb +31 -0
- data/lib/sunspot/installer/library_installer.rb +45 -0
- data/lib/sunspot/installer/schema_builder.rb +219 -0
- data/lib/sunspot/installer/solrconfig_updater.rb +76 -0
- data/lib/sunspot/installer/task_helper.rb +18 -0
- data/lib/sunspot/query.rb +11 -0
- data/lib/sunspot/query/abstract_field_facet.rb +52 -0
- data/lib/sunspot/query/boost_query.rb +24 -0
- data/lib/sunspot/query/common_query.rb +85 -0
- data/lib/sunspot/query/composite_fulltext.rb +36 -0
- data/lib/sunspot/query/connective.rb +206 -0
- data/lib/sunspot/query/date_field_facet.rb +14 -0
- data/lib/sunspot/query/dismax.rb +128 -0
- data/lib/sunspot/query/field_facet.rb +41 -0
- data/lib/sunspot/query/filter.rb +38 -0
- data/lib/sunspot/query/function_query.rb +52 -0
- data/lib/sunspot/query/geo.rb +53 -0
- data/lib/sunspot/query/highlighting.rb +67 -0
- data/lib/sunspot/query/more_like_this.rb +61 -0
- data/lib/sunspot/query/more_like_this_query.rb +12 -0
- data/lib/sunspot/query/pagination.rb +38 -0
- data/lib/sunspot/query/query_facet.rb +16 -0
- data/lib/sunspot/query/restriction.rb +262 -0
- data/lib/sunspot/query/scope.rb +9 -0
- data/lib/sunspot/query/sort.rb +95 -0
- data/lib/sunspot/query/sort_composite.rb +33 -0
- data/lib/sunspot/query/standard_query.rb +16 -0
- data/lib/sunspot/query/text_field_boost.rb +17 -0
- data/lib/sunspot/schema.rb +151 -0
- data/lib/sunspot/search.rb +9 -0
- data/lib/sunspot/search/abstract_search.rb +293 -0
- data/lib/sunspot/search/date_facet.rb +35 -0
- data/lib/sunspot/search/facet_row.rb +27 -0
- data/lib/sunspot/search/field_facet.rb +88 -0
- data/lib/sunspot/search/highlight.rb +38 -0
- data/lib/sunspot/search/hit.rb +136 -0
- data/lib/sunspot/search/more_like_this_search.rb +31 -0
- data/lib/sunspot/search/paginated_collection.rb +55 -0
- data/lib/sunspot/search/query_facet.rb +67 -0
- data/lib/sunspot/search/standard_search.rb +21 -0
- data/lib/sunspot/server.rb +152 -0
- data/lib/sunspot/session.rb +260 -0
- data/lib/sunspot/session_proxy.rb +87 -0
- data/lib/sunspot/session_proxy/abstract_session_proxy.rb +29 -0
- data/lib/sunspot/session_proxy/class_sharding_session_proxy.rb +66 -0
- data/lib/sunspot/session_proxy/id_sharding_session_proxy.rb +89 -0
- data/lib/sunspot/session_proxy/master_slave_session_proxy.rb +43 -0
- data/lib/sunspot/session_proxy/sharding_session_proxy.rb +222 -0
- data/lib/sunspot/session_proxy/silent_fail_session_proxy.rb +42 -0
- data/lib/sunspot/session_proxy/thread_local_session_proxy.rb +37 -0
- data/lib/sunspot/setup.rb +350 -0
- data/lib/sunspot/text_field_setup.rb +29 -0
- data/lib/sunspot/type.rb +372 -0
- data/lib/sunspot/util.rb +243 -0
- data/lib/sunspot/version.rb +3 -0
- data/script/console +10 -0
- data/solr-1.3/etc/jetty.xml +212 -0
- data/solr-1.3/etc/webdefault.xml +379 -0
- data/solr-1.3/lib/jetty-6.1.3.jar +0 -0
- data/solr-1.3/lib/jetty-util-6.1.3.jar +0 -0
- data/solr-1.3/lib/jsp-2.1/ant-1.6.5.jar +0 -0
- data/solr-1.3/lib/jsp-2.1/core-3.1.1.jar +0 -0
- data/solr-1.3/lib/jsp-2.1/jsp-2.1.jar +0 -0
- data/solr-1.3/lib/jsp-2.1/jsp-api-2.1.jar +0 -0
- data/solr-1.3/lib/servlet-api-2.5-6.1.3.jar +0 -0
- data/solr-1.3/solr/conf/elevate.xml +36 -0
- data/solr-1.3/solr/conf/protwords.txt +21 -0
- data/solr-1.3/solr/conf/schema.xml +64 -0
- data/solr-1.3/solr/conf/solrconfig.xml +725 -0
- data/solr-1.3/solr/conf/stopwords.txt +57 -0
- data/solr-1.3/solr/conf/synonyms.txt +31 -0
- data/solr-1.3/solr/lib/geoapi-nogenerics-2.1-M2.jar +0 -0
- data/solr-1.3/solr/lib/gt2-referencing-2.3.1.jar +0 -0
- data/solr-1.3/solr/lib/jsr108-0.01.jar +0 -0
- data/solr-1.3/solr/lib/locallucene.jar +0 -0
- data/solr-1.3/solr/lib/localsolr.jar +0 -0
- data/solr-1.3/start.jar +0 -0
- data/solr-1.3/webapps/solr.war +0 -0
- data/solr/README.txt +42 -0
- data/solr/etc/jetty.xml +218 -0
- data/solr/etc/webdefault.xml +379 -0
- data/solr/lib/jetty-6.1.3.jar +0 -0
- data/solr/lib/jetty-util-6.1.3.jar +0 -0
- data/solr/lib/jsp-2.1/ant-1.6.5.jar +0 -0
- data/solr/lib/jsp-2.1/core-3.1.1.jar +0 -0
- data/solr/lib/jsp-2.1/jsp-2.1.jar +0 -0
- data/solr/lib/jsp-2.1/jsp-api-2.1.jar +0 -0
- data/solr/lib/servlet-api-2.5-6.1.3.jar +0 -0
- data/solr/solr/.gitignore +1 -0
- data/solr/solr/README.txt +54 -0
- data/solr/solr/conf/admin-extra.html +31 -0
- data/solr/solr/conf/elevate.xml +36 -0
- data/solr/solr/conf/mapping-ISOLatin1Accent.txt +246 -0
- data/solr/solr/conf/protwords.txt +21 -0
- data/solr/solr/conf/schema.xml +238 -0
- data/solr/solr/conf/scripts.conf +24 -0
- data/solr/solr/conf/solrconfig.xml +934 -0
- data/solr/solr/conf/spellings.txt +2 -0
- data/solr/solr/conf/stopwords.txt +58 -0
- data/solr/solr/conf/synonyms.txt +31 -0
- data/solr/solr/conf/xslt/example.xsl +132 -0
- data/solr/solr/conf/xslt/example_atom.xsl +67 -0
- data/solr/solr/conf/xslt/example_rss.xsl +66 -0
- data/solr/solr/conf/xslt/luke.xsl +337 -0
- data/solr/start.jar +0 -0
- data/solr/webapps/solr.war +0 -0
- data/spec/api/adapters_spec.rb +33 -0
- data/spec/api/binding_spec.rb +50 -0
- data/spec/api/indexer/attributes_spec.rb +149 -0
- data/spec/api/indexer/batch_spec.rb +46 -0
- data/spec/api/indexer/dynamic_fields_spec.rb +42 -0
- data/spec/api/indexer/fixed_fields_spec.rb +57 -0
- data/spec/api/indexer/fulltext_spec.rb +43 -0
- data/spec/api/indexer/removal_spec.rb +53 -0
- data/spec/api/indexer/spec_helper.rb +1 -0
- data/spec/api/indexer_spec.rb +14 -0
- data/spec/api/query/advanced_manipulation_examples.rb +35 -0
- data/spec/api/query/connectives_examples.rb +189 -0
- data/spec/api/query/dsl_spec.rb +18 -0
- data/spec/api/query/dynamic_fields_examples.rb +165 -0
- data/spec/api/query/faceting_examples.rb +397 -0
- data/spec/api/query/fulltext_examples.rb +313 -0
- data/spec/api/query/function_spec.rb +70 -0
- data/spec/api/query/geo_examples.rb +68 -0
- data/spec/api/query/highlighting_examples.rb +223 -0
- data/spec/api/query/more_like_this_spec.rb +140 -0
- data/spec/api/query/ordering_pagination_examples.rb +95 -0
- data/spec/api/query/scope_examples.rb +275 -0
- data/spec/api/query/spec_helper.rb +1 -0
- data/spec/api/query/standard_spec.rb +28 -0
- data/spec/api/query/text_field_scoping_examples.rb +30 -0
- data/spec/api/query/types_spec.rb +20 -0
- data/spec/api/search/dynamic_fields_spec.rb +33 -0
- data/spec/api/search/faceting_spec.rb +360 -0
- data/spec/api/search/highlighting_spec.rb +69 -0
- data/spec/api/search/hits_spec.rb +120 -0
- data/spec/api/search/paginated_collection_spec.rb +26 -0
- data/spec/api/search/results_spec.rb +66 -0
- data/spec/api/search/search_spec.rb +23 -0
- data/spec/api/search/spec_helper.rb +1 -0
- data/spec/api/server_spec.rb +91 -0
- data/spec/api/session_proxy/class_sharding_session_proxy_spec.rb +85 -0
- data/spec/api/session_proxy/id_sharding_session_proxy_spec.rb +30 -0
- data/spec/api/session_proxy/master_slave_session_proxy_spec.rb +41 -0
- data/spec/api/session_proxy/sharding_session_proxy_spec.rb +77 -0
- data/spec/api/session_proxy/silent_fail_session_proxy_spec.rb +24 -0
- data/spec/api/session_proxy/spec_helper.rb +9 -0
- data/spec/api/session_proxy/thread_local_session_proxy_spec.rb +50 -0
- data/spec/api/session_spec.rb +220 -0
- data/spec/api/spec_helper.rb +3 -0
- data/spec/api/sunspot_spec.rb +18 -0
- data/spec/ext.rb +11 -0
- data/spec/helpers/indexer_helper.rb +29 -0
- data/spec/helpers/query_helper.rb +38 -0
- data/spec/helpers/search_helper.rb +80 -0
- data/spec/integration/dynamic_fields_spec.rb +55 -0
- data/spec/integration/faceting_spec.rb +238 -0
- data/spec/integration/highlighting_spec.rb +22 -0
- data/spec/integration/indexing_spec.rb +33 -0
- data/spec/integration/keyword_search_spec.rb +317 -0
- data/spec/integration/local_search_spec.rb +64 -0
- data/spec/integration/more_like_this_spec.rb +43 -0
- data/spec/integration/scoped_search_spec.rb +354 -0
- data/spec/integration/spec_helper.rb +7 -0
- data/spec/integration/stored_fields_spec.rb +10 -0
- data/spec/integration/test_pagination.rb +32 -0
- data/spec/mocks/adapters.rb +32 -0
- data/spec/mocks/blog.rb +3 -0
- data/spec/mocks/comment.rb +21 -0
- data/spec/mocks/connection.rb +126 -0
- data/spec/mocks/mock_adapter.rb +30 -0
- data/spec/mocks/mock_class_sharding_session_proxy.rb +24 -0
- data/spec/mocks/mock_record.rb +52 -0
- data/spec/mocks/mock_sharding_session_proxy.rb +15 -0
- data/spec/mocks/photo.rb +11 -0
- data/spec/mocks/post.rb +85 -0
- data/spec/mocks/super_class.rb +2 -0
- data/spec/mocks/user.rb +13 -0
- data/spec/spec_helper.rb +30 -0
- data/tasks/rdoc.rake +27 -0
- data/tasks/schema.rake +19 -0
- data/tasks/todo.rake +4 -0
- 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
|
data/lib/sunspot/dsl.rb
ADDED
|
@@ -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
|