pallan-sunspot 0.8.3

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 (73) hide show
  1. data/History.txt +39 -0
  2. data/LICENSE +18 -0
  3. data/README.rdoc +158 -0
  4. data/Rakefile +11 -0
  5. data/TODO +6 -0
  6. data/VERSION.yml +4 -0
  7. data/bin/sunspot-solr +46 -0
  8. data/lib/light_config.rb +40 -0
  9. data/lib/sunspot.rb +404 -0
  10. data/lib/sunspot/adapters.rb +253 -0
  11. data/lib/sunspot/configuration.rb +32 -0
  12. data/lib/sunspot/data_extractor.rb +37 -0
  13. data/lib/sunspot/dsl.rb +3 -0
  14. data/lib/sunspot/dsl/fields.rb +59 -0
  15. data/lib/sunspot/dsl/query.rb +78 -0
  16. data/lib/sunspot/dsl/restriction.rb +25 -0
  17. data/lib/sunspot/dsl/scope.rb +137 -0
  18. data/lib/sunspot/facet.rb +37 -0
  19. data/lib/sunspot/facet_row.rb +34 -0
  20. data/lib/sunspot/field.rb +234 -0
  21. data/lib/sunspot/indexer.rb +81 -0
  22. data/lib/sunspot/query.rb +372 -0
  23. data/lib/sunspot/query/dynamic_query.rb +86 -0
  24. data/lib/sunspot/query/field_facet.rb +21 -0
  25. data/lib/sunspot/query/pagination.rb +39 -0
  26. data/lib/sunspot/query/restriction.rb +223 -0
  27. data/lib/sunspot/query/sort.rb +33 -0
  28. data/lib/sunspot/search.rb +159 -0
  29. data/lib/sunspot/session.rb +159 -0
  30. data/lib/sunspot/setup.rb +200 -0
  31. data/lib/sunspot/type.rb +154 -0
  32. data/lib/sunspot/util.rb +164 -0
  33. data/solr/etc/jetty.xml +212 -0
  34. data/solr/etc/webdefault.xml +379 -0
  35. data/solr/lib/jetty-6.1.3.jar +0 -0
  36. data/solr/lib/jetty-util-6.1.3.jar +0 -0
  37. data/solr/lib/jsp-2.1/ant-1.6.5.jar +0 -0
  38. data/solr/lib/jsp-2.1/core-3.1.1.jar +0 -0
  39. data/solr/lib/jsp-2.1/jsp-2.1.jar +0 -0
  40. data/solr/lib/jsp-2.1/jsp-api-2.1.jar +0 -0
  41. data/solr/lib/servlet-api-2.5-6.1.3.jar +0 -0
  42. data/solr/solr/conf/elevate.xml +36 -0
  43. data/solr/solr/conf/protwords.txt +21 -0
  44. data/solr/solr/conf/schema.xml +231 -0
  45. data/solr/solr/conf/solrconfig.xml +696 -0
  46. data/solr/solr/conf/stopwords.txt +57 -0
  47. data/solr/solr/conf/synonyms.txt +31 -0
  48. data/solr/start.jar +0 -0
  49. data/solr/webapps/solr.war +0 -0
  50. data/spec/api/build_search_spec.rb +419 -0
  51. data/spec/api/indexer_spec.rb +214 -0
  52. data/spec/api/query_spec.rb +129 -0
  53. data/spec/api/search_retrieval_spec.rb +148 -0
  54. data/spec/api/session_spec.rb +102 -0
  55. data/spec/api/spec_helper.rb +1 -0
  56. data/spec/integration/dynamic_fields_spec.rb +55 -0
  57. data/spec/integration/faceting_spec.rb +39 -0
  58. data/spec/integration/keyword_search_spec.rb +32 -0
  59. data/spec/integration/scoped_search_spec.rb +175 -0
  60. data/spec/integration/spec_helper.rb +1 -0
  61. data/spec/integration/test_pagination.rb +32 -0
  62. data/spec/mocks/base_class.rb +2 -0
  63. data/spec/mocks/comment.rb +28 -0
  64. data/spec/mocks/mock_adapter.rb +22 -0
  65. data/spec/mocks/post.rb +75 -0
  66. data/spec/mocks/user.rb +8 -0
  67. data/spec/spec_helper.rb +32 -0
  68. data/tasks/gemspec.rake +22 -0
  69. data/tasks/rcov.rake +28 -0
  70. data/tasks/rdoc.rake +21 -0
  71. data/tasks/spec.rake +24 -0
  72. data/tasks/todo.rake +4 -0
  73. metadata +175 -0
@@ -0,0 +1,39 @@
1
+ == 0.8.0 2009-05-22
2
+ * Access query API directly; instantiate search without running it
3
+ * Dynamic fields
4
+ * Search blocks can be evaluated in calling context
5
+
6
+ == 0.7.3 2009-05-06
7
+ * Better exception handling when class doesn't have adapter/setup
8
+
9
+ == 0.7.2 2009-04-29
10
+ * Dirty sessions
11
+
12
+ == 0.7.1 2009-04-29
13
+ * Removed extlib dependency from gemspec
14
+
15
+ == 0.7.0 2009-04-28
16
+ * Less magic in the DSL
17
+ * Restrict by empty values
18
+ * Negative scoping using without() method
19
+ * Exclusion by object identity using without(instance)
20
+ * Support for faceting
21
+ * Explicit commits
22
+ * Boolean field type
23
+ * Attribute field flexibility
24
+ * Virtual field blocks can be evaluated in calling context
25
+ * Order available by multiple fields
26
+ * New adapter API
27
+ * Got rid of builder API
28
+ * Full documentation
29
+
30
+ == 0.0.2 2009-02-14
31
+ * Run sunspot's built-in Solr instance using
32
+ sunspot-solr executable
33
+ * Search hash interpretation delegated to
34
+ Builder object
35
+
36
+ == 0.0.1 2008-12-11
37
+ * Initial release
38
+ * Define indexing for any class using DSL
39
+ * Search indexed classes using DSL
data/LICENSE ADDED
@@ -0,0 +1,18 @@
1
+ Permission is hereby granted, free of charge, to any person obtaining
2
+ a copy of this software and associated documentation files (the
3
+ 'Software'), to deal in the Software without restriction, including
4
+ without limitation the rights to use, copy, modify, merge, publish,
5
+ distribute, sublicense, and/or sell copies of the Software, and to
6
+ permit persons to whom the Software is furnished to do so, subject to
7
+ the following conditions:
8
+
9
+ The above copyright notice and this permission notice shall be
10
+ included in all copies or substantial portions of the Software.
11
+
12
+ THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
13
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
14
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
15
+ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
16
+ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
17
+ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
18
+ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,158 @@
1
+ = Sunspot
2
+
3
+ http://outoftime.github.com/sunspot
4
+
5
+ Sunspot is a Ruby library for expressive, powerful interaction with the Solr search engine.
6
+ Sunspot is built on top of the solr-ruby gem, which provides a low-level interface for Solr
7
+ interaction; Sunspot provides a simple, intuitive, expressive DSL backed by powerful
8
+ features for indexing objects and searching for them.
9
+
10
+ Sunspot is designed to be easily plugged in to any ORM, or even non-database-backed
11
+ objects such as the filesystem.
12
+
13
+ === Features:
14
+
15
+ * Define indexing strategy for each searchable class using intuitive block-based API
16
+ * Clean separation between keyword-searchable fields and fields for scoping/ordering
17
+ * Define fields based on existing attributes or "virtual fields" for custom indexing
18
+ * Indexes each object's entire superclass hierarchy, for easy searching for all objects inheriting from a parent class
19
+ * Intuitive DSL for scoping searches, with all the usual boolean operators available
20
+ * Intuitive interface for requesting facets on indexed fields
21
+ * Extensible adapter architecture for easy integration of other ORMs or non-model classes
22
+ * Full compatibility with will_paginate
23
+ * Ordering
24
+
25
+ == Installation
26
+
27
+ gem sources -a http://gems.github.com
28
+ gem install outoftime-sunspot
29
+
30
+ In order to start the packaged Solr installation, run:
31
+
32
+ sunspot-solr start -- [-d /path/to/data/directory] [-p port] [-s path/to/solr/home]
33
+
34
+ If you don't specify a data directory, your Solr index will be stored in your operating system's temporary directory.
35
+
36
+ If you specify a solr home, the directory must contain a <code>conf</code>
37
+ directory, which should contain at least <code>schema.xml</code> and
38
+ <code>solrconfig.xml</code>. Be sure to copy the <code>schema.xml</code> out of
39
+ the Sunspot gem's <code>solr/solr/conf</code> directory. Sunspot relies on the
40
+ field name patterns defined in the packaged <code>schema.xml</code>, so those
41
+ cannot be modified.
42
+
43
+ You can also run your own instance of Solr wherever you'd like; just copy the solr/config/schema.xml file out of the gem's solr into your installation.
44
+ You can change the URL at which Sunspot accesses Solr with:
45
+
46
+ Sunspot.config.solr.url = 'http://solr.my.host:9818/solr'
47
+
48
+ == Rails Integration
49
+
50
+ The {Sunspot::Rails}[http://github.com/outoftime/sunspot_rails] plugin makes
51
+ integrating Sunspot into Rails drop-in easy.
52
+
53
+ == Using Sunspot
54
+
55
+ === Define an index:
56
+
57
+ class Post
58
+ #...
59
+ end
60
+
61
+ Sunspot.setup(Post) do
62
+ text :title, :body
63
+ string :author_name
64
+ integer :blog_id
65
+ integer :category_ids
66
+ float :average_rating, :using => :ratings_average
67
+ time :published_at
68
+ string :sort_title do
69
+ title.downcase.sub(/^(an?|the)\W+/, ''/) if title = self.title
70
+ end
71
+ end
72
+
73
+ See Sunspot.setup for more information.
74
+
75
+ Note that in order for a class to be searchable, it must have an adapter
76
+ registered for itself or one of its subclasses. Adapters allow Sunspot to load
77
+ objects out of persistent storage, and to determine their primary key for
78
+ indexing. {Sunspot::Rails}[http://github.com/outoftime/sunspot_rails] comes with
79
+ an adapter for ActiveRecord objects, but for other types of models you will need
80
+ to define your own. See Sunspot::Adapters for more information.
81
+
82
+ === Search for objects:
83
+
84
+ search = Sunspot.search Post do
85
+ keywords 'great pizza'
86
+ with :author_name, 'Mark Twain'
87
+ with(:blog_id).any_of [2, 14]
88
+ with(:category_ids).all_of [4, 10]
89
+ with(:published_at).less_than Time.now
90
+ without :title, 'Bad Title'
91
+ without bad_instance # specifically exclude this instance from results
92
+
93
+ paginate :page => 3, :per_page => 15
94
+ order_by :average_rating, :desc
95
+
96
+ facet :blog_id
97
+ end
98
+
99
+ See Sunspot.search for more information.
100
+
101
+ === Get data from search:
102
+
103
+ search.results
104
+ search.total
105
+ search.page
106
+ search.per_page
107
+ search.facet(:blog_id)
108
+
109
+ === Building searches manually:
110
+
111
+ The search DSL is great for building searches from fairly static parameters,
112
+ but a highly dynamic search might want to leverage an intermediate approach
113
+ (such as an application of the Builder pattern). For these cases, Sunspot
114
+ exposes direct access to the Query object:
115
+
116
+ search = Sunspot.new_search(Post)
117
+ search.query.keywords = 'great pizza'
118
+ search.query.add_restriction(:author_name, :equal_to, 'Mark Twain')
119
+ search.query.add_restriction(:title, :equal_to, 'Bad Title', true) # negate the restriction
120
+ search.query.exclude_instance(bad_instance)
121
+ search.query.paginate(3, 15)
122
+ search.query.order_by(:average_rating, :desc)
123
+ search.query.add_field_facet(:blog_id)
124
+ search.execute!
125
+
126
+ == About the API documentation
127
+
128
+ All of the methods documented in the RDoc are considered part of Sunspot's
129
+ public API. Methods that are not part of the public API are documented in the
130
+ code, but excluded from the RDoc. If you find yourself needing to access methods
131
+ that are not part of the public API in order to do what you need, please contact
132
+ me so I can rectify the situation!
133
+
134
+ == Dependencies
135
+
136
+ 1. solr-ruby
137
+ 2. Java
138
+
139
+ Sunspot has been tested with MRI 1.8.6, YARV 1.9.1, and JRuby 1.2.0
140
+
141
+ == Bugs
142
+
143
+ Please submit bug reports to
144
+ http://outoftime.lighthouseapp.com/projects/20339-sunspot
145
+
146
+ == Further Reading
147
+
148
+ * Sunspot Discussion: http://groups.google.com/group/ruby-sunspot
149
+ * Posts about Sunspot from my tumblog: http://outofti.me/tagged/sunspot
150
+ * Read about it on Linux Magazine: http://www.linux-mag.com/id/7341
151
+
152
+ == Contributors
153
+
154
+ Mat Brown (mat@patch.com)
155
+
156
+ == License
157
+
158
+ Sunspot is distributed under the MIT License, copyright (c) 2008-2009 Mat Brown
@@ -0,0 +1,11 @@
1
+ require 'rubygems'
2
+
3
+ ENV['RUBYOPT'] = '-W1'
4
+
5
+ task :environment do
6
+ require File.dirname(__FILE__) + '/lib/sunspot'
7
+ end
8
+
9
+ Dir['tasks/**/*.rake'].each { |t| load t }
10
+
11
+ task :default => 'spec:api'
data/TODO ADDED
@@ -0,0 +1,6 @@
1
+ === 0.9 ===
2
+ * Instantiated facets!
3
+ * Direct access to adapter
4
+ * Facet by type (?)
5
+ * Switch to RSolr
6
+ * Query-based faceting (?)
@@ -0,0 +1,4 @@
1
+ ---
2
+ :major: 0
3
+ :minor: 8
4
+ :patch: 3
@@ -0,0 +1,46 @@
1
+ #!/usr/bin/env ruby
2
+ require 'rubygems'
3
+ gem 'daemons', '~> 1.0'
4
+ gem 'optiflag', '~> 0.6.5'
5
+ require 'fileutils'
6
+ require 'tmpdir'
7
+ require 'daemons'
8
+ require 'optiflag'
9
+
10
+ working_directory = FileUtils.pwd
11
+ solr_home = File.join(File.dirname(__FILE__), '..', 'solr')
12
+
13
+ module SolrFlags extend OptiFlagSet
14
+ optional_flag 'p' do
15
+ description 'Port on which to run Solr (default 8983)'
16
+ long_form 'port'
17
+ end
18
+
19
+ optional_flag 'd' do
20
+ description 'Solr data directory'
21
+ end
22
+
23
+ optional_flag 's' do
24
+ description 'Solr home (should contain conf/ directory)'
25
+ end
26
+
27
+ and_process!
28
+ end
29
+
30
+ port = ARGV.flags.p || '8983'
31
+ data_dir = File.expand_path(ARGV.flags.d || File.join(Dir.tmpdir, 'solr_data'))
32
+ home = File.expand_path(ARGV.flags.s) if ARGV.flags.s
33
+
34
+ Daemons.run_proc('sunspot-solr') do
35
+ FileUtils.cd(working_directory) do
36
+ FileUtils.cd(solr_home) do
37
+ args = ['java']
38
+ args << "-Djetty.port=#{port}" if port
39
+ args << "-Dsolr.data.dir=#{data_dir}" if data_dir
40
+ args << "-Dsolr.solr.home=#{home}" if home
41
+ args << '-jar' << 'start.jar'
42
+ STDERR.puts(args * ' ')
43
+ Kernel.exec(*args)
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,40 @@
1
+ module LightConfig
2
+ class Configuration
3
+ def initialize(&block)
4
+ @properties = {}
5
+ ::LightConfig::Builder.new(self).instance_eval(&block)
6
+ singleton = (class <<self; self; end)
7
+ @properties.keys.each do |property|
8
+ singleton.module_eval do
9
+ define_method property do
10
+ @properties[property]
11
+ end
12
+
13
+ define_method "#{property}=" do |value|
14
+ @properties[property] = value
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
20
+
21
+ class Builder
22
+ def initialize(configuration)
23
+ @configuration = configuration
24
+ end
25
+
26
+ def method_missing(method, *args, &block)
27
+ raise ArgumentError("wrong number of arguments(#{args.length} for 1)") unless args.length < 2
28
+ value = if block then ::LightConfig::Configuration.new(&block)
29
+ else args.first
30
+ end
31
+ @configuration.instance_variable_get(:@properties)[method] = value
32
+ end
33
+ end
34
+
35
+ class <<self
36
+ def build(&block)
37
+ LightConfig::Configuration.new(&block)
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,404 @@
1
+ gem 'solr-ruby'
2
+ require 'solr'
3
+ require File.join(File.dirname(__FILE__), 'light_config')
4
+
5
+ %w(adapters configuration setup field data_extractor indexer
6
+ query search facet facet_row session type util dsl).each do |filename|
7
+ require File.join(File.dirname(__FILE__), 'sunspot', filename)
8
+ end
9
+
10
+ #
11
+ # The Sunspot module provides class-method entry points to most of the
12
+ # functionality provided by the Sunspot library. Internally, the Sunspot
13
+ # singleton class contains a (non-thread-safe!) instance of Sunspot::Session,
14
+ # to which it delegates most of the class methods it exposes. In the method
15
+ # documentation below, this instance is referred to as the "singleton session".
16
+ #
17
+ # Though the singleton session provides a convenient entry point to Sunspot,
18
+ # it is by no means required to use the Sunspot class methods. Multiple sessions
19
+ # may be instantiated and used (if you need to connect to multiple Solr
20
+ # instances, for example.)
21
+ #
22
+ # Note that the configuration of classes for index/search (the +setup+
23
+ # method) is _not_ session-specific, but rather global.
24
+ #
25
+ module Sunspot
26
+ UnrecognizedFieldError = Class.new(Exception)
27
+ UnrecognizedRestrictionError = Class.new(Exception)
28
+ NoAdapterError = Class.new(Exception)
29
+ NoSetupError = Class.new(Exception)
30
+
31
+ class <<self
32
+ # Configures indexing and search for a given class.
33
+ #
34
+ # ==== Parameters
35
+ #
36
+ # clazz<Class>:: class to configure
37
+ #
38
+ # ==== Example
39
+ #
40
+ # Sunspot.setup(Post) do
41
+ # text :title, :body
42
+ # string :author_name
43
+ # integer :blog_id
44
+ # integer :category_ids
45
+ # float :average_rating, :using => :ratings_average
46
+ # time :published_at
47
+ # string :sort_title do
48
+ # title.downcase.sub(/^(an?|the)\W+/, ''/) if title = self.title
49
+ # end
50
+ # end
51
+ #
52
+ # ====== Attribute Fields vs. Virtual Fields
53
+ #
54
+ # Attribute fields call a method on the indexed object and index the
55
+ # return value. All of the fields defined above except for the last one are
56
+ # attribute fields. By default, the field name will also be the attribute
57
+ # used; this can be overriden with the +:using+ option, as in
58
+ # +:average_rating+ above. In that case, the attribute +:ratings_average+
59
+ # will be indexed with the field name +:average_rating+.
60
+ #
61
+ # +:sort_title+ is a virtual field, which evaluates the block inside the
62
+ # context of the instance being indexed, and indexes the value returned
63
+ # by the block. If the block you pass takes an argument, it will be passed
64
+ # the instance rather than being evaluated inside of it; so, the following
65
+ # example is equivalent to the one above (assuming #title is public):
66
+ #
67
+ # Sunspot.setup(Post) do
68
+ # string :sort_title do |post|
69
+ # post.title.downcase.sub(/^(an?|the)\W+/, ''/) if title = self.title
70
+ # end
71
+ # end
72
+ #
73
+ # ===== Field Types
74
+ #
75
+ # The available types are:
76
+ #
77
+ # * +text+
78
+ # * +string+
79
+ # * +integer+
80
+ # * +float+
81
+ # * +time+
82
+ # * +boolean+
83
+ #
84
+ # Note that the +text+ type behaves quite differently from the others -
85
+ # this is the type that is indexed as fulltext, and is searched using the
86
+ # +keywords+ method inside the search DSL. Text fields cannot have
87
+ # restrictions set on them, nor can they be used in order statements or
88
+ # for facets. All other types are indexed literally, and thus can be used
89
+ # for all of those operations. They will not, however, be searched in
90
+ # fulltext. In this way, Sunspot provides a complete barrier between
91
+ # fulltext fields and value fields.
92
+ #
93
+ # It is fine to specify a field both as a text field and a string field;
94
+ # internally, the fields will have different names so there is no danger
95
+ # of conflict.
96
+ #
97
+ # ===== Dynamic Fields
98
+ #
99
+ # For use cases which have highly dynamic data models (for instance, an
100
+ # open set of key-value pairs attached to a model), it may be useful to
101
+ # defer definition of fields until indexing time. Sunspot exposes dynamic
102
+ # fields, which define a data accessor (either attribute or virtual, see
103
+ # above), which accepts a hash of field names to values. Note that the field
104
+ # names in the hash are internally scoped to the base name of the dynamic
105
+ # field, so any time they are referred to, they are referred to using both
106
+ # the base name and the dynamic (runtime-specified) name.
107
+ #
108
+ # Dynamic fields are speficied in the setup block using the type name
109
+ # prefixed by +dynamic_+. For example:
110
+ #
111
+ # Sunspot.setup(Post) do
112
+ # dynamic_string :custom_values do
113
+ # key_value_pairs.inject({}) do |hash, key_value_pair|
114
+ # hash[key_value_pair.key.to_sym] = key_value_pair.value
115
+ # end
116
+ # end
117
+ # end
118
+ #
119
+ # If you later wanted to facet all of the values for the key "cuisine",
120
+ # you could issue:
121
+ #
122
+ # Sunspot.search(Post) do
123
+ # dynamic :custom_values do
124
+ # facet :cuisine
125
+ # end
126
+ # end
127
+ #
128
+ # In the documentation, +:custom_values+ is referred to as the "base name" -
129
+ # that is, the one specified statically - and +:cuisine+ is referred to as
130
+ # the dynamic name, which is the part that is specified at indexing time.
131
+ #
132
+ def setup(clazz, &block)
133
+ Setup.setup(clazz, &block)
134
+ end
135
+
136
+ # Indexes objects on the singleton session.
137
+ #
138
+ # ==== Parameters
139
+ #
140
+ # objects...<Object>:: objects to index (may pass an array or varargs)
141
+ #
142
+ # ==== Example
143
+ #
144
+ # post1, post2 = Array(2) { Post.create }
145
+ # Sunspot.index(post1, post2)
146
+ #
147
+ # Note that indexed objects won't be reflected in search until a commit is
148
+ # sent - see Sunspot.index! and Sunspot.commit
149
+ #
150
+ def index(*objects)
151
+ session.index(*objects)
152
+ end
153
+
154
+ # Indexes objects on the singleton session and commits immediately.
155
+ #
156
+ # See: Sunspot.index and Sunspot.commit
157
+ #
158
+ # ==== Parameters
159
+ #
160
+ # objects...<Object>:: objects to index (may pass an array or varargs)
161
+ #
162
+ def index!(*objects)
163
+ session.index!(*objects)
164
+ end
165
+
166
+ # Commits the singleton session
167
+ #
168
+ # When documents are added to or removed from Solr, the changes are
169
+ # initially stored in memory, and are not reflected in Solr's existing
170
+ # searcher instance. When a commit message is sent, the changes are written
171
+ # to disk, and a new searcher is spawned. Commits are thus fairly
172
+ # expensive, so if your application needs to index several documents as part
173
+ # of a single operation, it is advisable to index them all and then call
174
+ # commit at the end of the operation.
175
+ #
176
+ # Note that Solr can also be configured to automatically perform a commit
177
+ # after either a specified interval after the last change, or after a
178
+ # specified number of documents are added. See
179
+ # http://wiki.apache.org/solr/SolrConfigXml
180
+ #
181
+ def commit
182
+ session.commit
183
+ end
184
+
185
+ #
186
+ # Create a new Search instance, but do not execute it immediately. Generally
187
+ # you will want to use the #search method to execute searches using the
188
+ # DSL; however, if you are building searches dynamically (using the Builder
189
+ # pattern, for instance), it may be easier to access the Query API directly.
190
+ #
191
+ # ==== Parameters
192
+ #
193
+ # types<Class>...::
194
+ # Zero, one, or more types to search for. If no types are passed, all
195
+ # configured types will be searched for.
196
+ #
197
+ # ==== Returns
198
+ #
199
+ # Sunspot::Search::
200
+ # Search object, not yet executed. Query parameters can be added manually;
201
+ # then #execute! should be called.
202
+ #
203
+ def new_search(*types)
204
+ session.new_search(*types)
205
+ end
206
+
207
+
208
+ # Search for objects in the index.
209
+ #
210
+ # ==== Parameters
211
+ #
212
+ # types<Class>...::
213
+ # Zero, one, or more types to search for. If no types are passed, all
214
+ # configured types will be searched.
215
+ #
216
+ # ==== Options (last argument, optional)
217
+ #
218
+ # :keywords<String>:: Fulltext search string
219
+ # :conditions<Hash>::
220
+ # Hash of key-value pairs to be used as restrictions. Keys are field
221
+ # names. Scalar values are used as equality restrictions; arrays are used
222
+ # as "any of" restrictions; and Ranges are used as range restrictions.
223
+ # :order<String>:: order field and direction (e.g., 'updated_at desc')
224
+ # :page<Integer>:: Page to start on for pagination
225
+ # :per_page<Integer>::
226
+ # Number of results to use per page. Ignored if :page is not specified.
227
+ #
228
+ # ==== Returns
229
+ #
230
+ # Sunspot::Search:: Object containing results, facets, count, etc.
231
+ #
232
+ # The fields available for restriction, ordering, etc. are those that meet
233
+ # the following criteria:
234
+ #
235
+ # * They are not of type +text+.
236
+ # * They are defined for all of the classes being searched
237
+ # * They have the same data type for all of the classes being searched
238
+ # * They have the same multiple flag for all of the classes being searched.
239
+ #
240
+ # The restrictions available are the constants defined in the
241
+ # Sunspot::Restriction class. The standard restrictions are:
242
+ #
243
+ # with(:field_name).equal_to(value)
244
+ # with(:field_name, value) # shorthand for above
245
+ # with(:field_name).less_than(value)
246
+ # with(:field_name).greater_than(value)
247
+ # with(:field_name).between(value1..value2)
248
+ # with(:field_name).any_of([value1, value2, value3])
249
+ # with(:field_name).all_of([value1, value2, value3])
250
+ # without(some_instance) # exclude that particular instance
251
+ #
252
+ # +without+ can be substituted for +with+, causing the restriction to be
253
+ # negated. In the last example above, only +without+ works, as it does not
254
+ # make sense to search only for an instance you already have.
255
+ #
256
+ # Equality restrictions can take +nil+ as a value, which restricts the
257
+ # results to documents that have no value for the given field. Passing +nil+
258
+ # as a value to other restriction types is illegal. Thus:
259
+ #
260
+ # with(:field_name, nil) # ok
261
+ # with(:field_name).equal_to(nil) # ok
262
+ # with(:field_name).less_than(nil) # bad
263
+ #
264
+ # ==== Example
265
+ #
266
+ # Sunspot.search(Post) do
267
+ # keywords 'great pizza'
268
+ # with(:published_at).less_than Time.now
269
+ # with :blog_id, 1
270
+ # without current_post
271
+ # facet :category_ids
272
+ # order_by :published_at, :desc
273
+ # paginate 2, 15
274
+ # end
275
+ #
276
+ # If the block passed to #search takes an argument, that argument will
277
+ # present the DSL, and the block will be evaluated in the calling context.
278
+ # This will come in handy for building searches using instance data or
279
+ # methods, e.g.:
280
+ #
281
+ # Sunspot.search(Post) do |query|
282
+ # query.with(:blog_id, @current_blog.id)
283
+ # end
284
+ #
285
+ # See Sunspot::DSL::Scope and Sunspot::DSL::Query for the full API presented
286
+ # inside the block.
287
+ #
288
+ def search(*types, &block)
289
+ session.search(*types, &block)
290
+ end
291
+
292
+ # Remove objects from the index. Any time an object is destroyed, it must
293
+ # be removed from the index; otherwise, the index will contain broken
294
+ # references to objects that do not exist, which will cause errors when
295
+ # those objects are matched in search results.
296
+ #
297
+ # ==== Parameters
298
+ #
299
+ # objects...<Object>::
300
+ # Objects to remove from the index (may pass an array or varargs)
301
+ #
302
+ # ==== Example
303
+ #
304
+ # post.destroy
305
+ # Sunspot.remove(post)
306
+ #
307
+ def remove(*objects)
308
+ session.remove(*objects)
309
+ end
310
+
311
+ #
312
+ # Remove objects from the index and immediately commit. See Sunspot.remove
313
+ #
314
+ # ==== Parameters
315
+ #
316
+ # objects...<Object>:: Objects to remove from the index
317
+ #
318
+ def remove!
319
+ session.remove!(*objects)
320
+ end
321
+
322
+ # Remove all objects of the given classes from the index. There isn't much
323
+ # use for this in general operations but it can be useful for maintenance,
324
+ # testing, etc. If no arguments are passed, remove everything from the
325
+ # index.
326
+ #
327
+ # ==== Parameters
328
+ #
329
+ # classes...<Class>::
330
+ # classes for which to remove all instances from the index (may pass an
331
+ # array or varargs)
332
+ #
333
+ # ==== Example
334
+ #
335
+ # Sunspot.remove_all(Post, Blog)
336
+ #
337
+ def remove_all(*classes)
338
+ session.remove_all(*classes)
339
+ end
340
+
341
+ #
342
+ # Remove all objects of the given classes from the index and immediately
343
+ # commit. See Sunspot.remove_all
344
+ #
345
+ # ==== Parameters
346
+ #
347
+ # classes...<Class>::
348
+ # classes for which to remove all instances from the index
349
+ def remove_all!(*classes)
350
+ session.remove_all(*classes)
351
+ end
352
+
353
+ #
354
+ # True if documents have been added, updated, or removed since the last
355
+ # commit.
356
+ #
357
+ # ==== Returns
358
+ #
359
+ # Boolean:: Whether there have been any updates since the last commit
360
+ #
361
+ def dirty?
362
+ session.dirty?
363
+ end
364
+
365
+ #
366
+ # Sends a commit if the session is dirty (see #dirty?).
367
+ #
368
+ def commit_if_dirty
369
+ session.commit_if_dirty
370
+ end
371
+
372
+ # Returns the configuration associated with the singleton session. See
373
+ # Sunspot::Configuration for details.
374
+ #
375
+ # ==== Returns
376
+ #
377
+ # LightConfig::Configuration:: configuration for singleton session
378
+ #
379
+ def config
380
+ session.config
381
+ end
382
+
383
+ #
384
+ # Resets the singleton session. This is useful for clearing out all
385
+ # static data between tests, but probably nowhere else.
386
+ #
387
+ def reset!
388
+ @session = nil
389
+ end
390
+
391
+ private
392
+
393
+ #
394
+ # Get the singleton session, creating it if none yet exists.
395
+ #
396
+ # ==== Returns
397
+ #
398
+ # Sunspot::Session:: the singleton session
399
+ #
400
+ def session #:nodoc:
401
+ @session ||= Session.new
402
+ end
403
+ end
404
+ end