Module: Sunspot

Defined in:
lib/sunspot.rb,
lib/sunspot/type.rb,
lib/sunspot/util.rb,
lib/sunspot/facet.rb,
lib/sunspot/field.rb,
lib/sunspot/query.rb,
lib/sunspot/setup.rb,
lib/sunspot/search.rb,
lib/sunspot/indexer.rb,
lib/sunspot/session.rb,
lib/sunspot/adapters.rb,
lib/sunspot/dsl/query.rb,
lib/sunspot/dsl/scope.rb,
lib/sunspot/facet_row.rb,
lib/sunspot/dsl/fields.rb,
lib/sunspot/query/sort.rb,
lib/sunspot/configuration.rb,
lib/sunspot/data_extractor.rb,
lib/sunspot/dsl/restriction.rb,
lib/sunspot/query/pagination.rb,
lib/sunspot/query/field_facet.rb,
lib/sunspot/query/restriction.rb,
lib/sunspot/query/dynamic_query.rb

Overview

The Sunspot module provides class-method entry points to most of the functionality provided by the Sunspot library. Internally, the Sunspot singleton class contains a (non-thread-safe!) instance of Sunspot::Session, to which it delegates most of the class methods it exposes. In the method documentation below, this instance is referred to as the “singleton session”.

Though the singleton session provides a convenient entry point to Sunspot, it is by no means required to use the Sunspot class methods. Multiple sessions may be instantiated and used (if you need to connect to multiple Solr instances, for example.)

Note that the configuration of classes for index/search (the setup method) is not session-specific, but rather global.

Defined Under Namespace

Modules: Adapters, Configuration, DSL, DataExtractor, Field, Type, Util Classes: Facet, FacetRow, Indexer, Query, Search, Session, Setup

Constant Summary collapse

UnrecognizedFieldError =
Class.new(Exception)
UnrecognizedRestrictionError =
Class.new(Exception)
NoAdapterError =
Class.new(Exception)
NoSetupError =
Class.new(Exception)

Class Method Summary collapse

Class Method Details

.commitObject

Commits the singleton session

When documents are added to or removed from Solr, the changes are initially stored in memory, and are not reflected in Solr’s existing searcher instance. When a commit message is sent, the changes are written to disk, and a new searcher is spawned. Commits are thus fairly expensive, so if your application needs to index several documents as part of a single operation, it is advisable to index them all and then call commit at the end of the operation.

Note that Solr can also be configured to automatically perform a commit after either a specified interval after the last change, or after a specified number of documents are added. See wiki.apache.org/solr/SolrConfigXml



181
182
183
# File 'lib/sunspot.rb', line 181

def commit
  session.commit
end

.commit_if_dirtyObject

Sends a commit if the session is dirty (see #dirty?).



368
369
370
# File 'lib/sunspot.rb', line 368

def commit_if_dirty
  session.commit_if_dirty
end

.configObject

Returns the configuration associated with the singleton session. See Sunspot::Configuration for details.

Returns

LightConfig::Configuration

configuration for singleton session



379
380
381
# File 'lib/sunspot.rb', line 379

def config
  session.config
end

.dirty?Boolean

True if documents have been added, updated, or removed since the last commit.

Returns

Boolean

Whether there have been any updates since the last commit

Returns:

  • (Boolean)


361
362
363
# File 'lib/sunspot.rb', line 361

def dirty?
  session.dirty?
end

.index(*objects) ⇒ Object

Indexes objects on the singleton session.

Parameters

objects…<Object>

objects to index (may pass an array or varargs)

Example

post1, post2 = Array(2) { Post.create }
Sunspot.index(post1, post2)

Note that indexed objects won’t be reflected in search until a commit is sent - see Sunspot.index! and Sunspot.commit



150
151
152
# File 'lib/sunspot.rb', line 150

def index(*objects)
  session.index(*objects)
end

.index!(*objects) ⇒ Object

Indexes objects on the singleton session and commits immediately.

See: Sunspot.index and Sunspot.commit

Parameters

objects…<Object>

objects to index (may pass an array or varargs)



162
163
164
# File 'lib/sunspot.rb', line 162

def index!(*objects)
  session.index!(*objects)
end

.new_search(*types) ⇒ Object

Create a new Search instance, but do not execute it immediately. Generally you will want to use the #search method to execute searches using the DSL; however, if you are building searches dynamically (using the Builder pattern, for instance), it may be easier to access the Query API directly.

Parameters

types<Class>…

Zero, one, or more types to search for. If no types are passed, all configured types will be searched for.

Returns

Sunspot::Search

Search object, not yet executed. Query parameters can be added manually; then #execute! should be called.



203
204
205
# File 'lib/sunspot.rb', line 203

def new_search(*types)
  session.new_search(*types)
end

.remove(*objects) ⇒ Object

Remove objects from the index. Any time an object is destroyed, it must be removed from the index; otherwise, the index will contain broken references to objects that do not exist, which will cause errors when those objects are matched in search results.

Parameters

objects…<Object>

Objects to remove from the index (may pass an array or varargs)

Example

post.destroy
Sunspot.remove(post)


307
308
309
# File 'lib/sunspot.rb', line 307

def remove(*objects)
  session.remove(*objects)
end

.remove!Object

Remove objects from the index and immediately commit. See Sunspot.remove

Parameters

objects…<Object>

Objects to remove from the index



318
319
320
# File 'lib/sunspot.rb', line 318

def remove!
  session.remove!(*objects)
end

.remove_all(*classes) ⇒ Object

Remove all objects of the given classes from the index. There isn’t much use for this in general operations but it can be useful for maintenance, testing, etc. If no arguments are passed, remove everything from the index.

Parameters

classes…<Class>

classes for which to remove all instances from the index (may pass an array or varargs)

Example

Sunspot.remove_all(Post, Blog)


337
338
339
# File 'lib/sunspot.rb', line 337

def remove_all(*classes)
  session.remove_all(*classes)
end

.remove_all!(*classes) ⇒ Object

Remove all objects of the given classes from the index and immediately commit. See Sunspot.remove_all

Parameters

classes…<Class>

classes for which to remove all instances from the index



349
350
351
# File 'lib/sunspot.rb', line 349

def remove_all!(*classes)
  session.remove_all(*classes)
end

.reset!Object

Resets the singleton session. This is useful for clearing out all static data between tests, but probably nowhere else.



387
388
389
# File 'lib/sunspot.rb', line 387

def reset!
  @session = nil
end

.search(*types, &block) ⇒ Object

Search for objects in the index.

Parameters

types<Class>…

Zero, one, or more types to search for. If no types are passed, all configured types will be searched.

Options (last argument, optional)

:keywords<String>

Fulltext search string

:conditions<Hash>

Hash of key-value pairs to be used as restrictions. Keys are field names. Scalar values are used as equality restrictions; arrays are used as “any of” restrictions; and Ranges are used as range restrictions.

:order<String>

order field and direction (e.g., ‘updated_at desc’)

:page<Integer>

Page to start on for pagination

:per_page<Integer>

Number of results to use per page. Ignored if :page is not specified.

Returns

Sunspot::Search

Object containing results, facets, count, etc.

The fields available for restriction, ordering, etc. are those that meet the following criteria:

  • They are not of type text.

  • They are defined for all of the classes being searched

  • They have the same data type for all of the classes being searched

  • They have the same multiple flag for all of the classes being searched.

The restrictions available are the constants defined in the Sunspot::Restriction class. The standard restrictions are:

with(:field_name).equal_to(value)
with(:field_name, value) # shorthand for above
with(:field_name).less_than(value)
with(:field_name).greater_than(value)
with(:field_name).between(value1..value2)
with(:field_name).any_of([value1, value2, value3])
with(:field_name).all_of([value1, value2, value3])
without(some_instance) # exclude that particular instance

without can be substituted for with, causing the restriction to be negated. In the last example above, only without works, as it does not make sense to search only for an instance you already have.

Equality restrictions can take nil as a value, which restricts the results to documents that have no value for the given field. Passing nil as a value to other restriction types is illegal. Thus:

with(:field_name, nil) # ok
with(:field_name).equal_to(nil) # ok
with(:field_name).less_than(nil) # bad

Example

Sunspot.search(Post) do
  keywords 'great pizza'
  with(:published_at).less_than Time.now
  with :blog_id, 1
  without current_post
  facet :category_ids
  order_by :published_at, :desc
  paginate 2, 15
end

If the block passed to #search takes an argument, that argument will present the DSL, and the block will be evaluated in the calling context. This will come in handy for building searches using instance data or methods, e.g.:

Sunspot.search(Post) do |query|
  query.with(:blog_id, @current_blog.id)
end

See Sunspot::DSL::Scope and Sunspot::DSL::Query for the full API presented inside the block.



288
289
290
# File 'lib/sunspot.rb', line 288

def search(*types, &block)
  session.search(*types, &block)
end

.setup(clazz, &block) ⇒ Object

Configures indexing and search for a given class.

Parameters

clazz<Class>

class to configure

Example

Sunspot.setup(Post) do
  text :title, :body
  string :author_name
  integer :blog_id
  integer :category_ids
  float :average_rating, :using => :ratings_average
  time :published_at
  string :sort_title do
    title.downcase.sub(/^(an?|the)\W+/, ''/) if title = self.title
  end
end
Attribute Fields vs. Virtual Fields

Attribute fields call a method on the indexed object and index the return value. All of the fields defined above except for the last one are attribute fields. By default, the field name will also be the attribute used; this can be overriden with the :using option, as in :average_rating above. In that case, the attribute :ratings_average will be indexed with the field name :average_rating.

:sort_title is a virtual field, which evaluates the block inside the context of the instance being indexed, and indexes the value returned by the block. If the block you pass takes an argument, it will be passed the instance rather than being evaluated inside of it; so, the following example is equivalent to the one above (assuming #title is public):

Sunspot.setup(Post) do
  string :sort_title do |post|
    post.title.downcase.sub(/^(an?|the)\W+/, ''/) if title = self.title
  end
end
Field Types

The available types are:

  • text

  • string

  • integer

  • float

  • time

  • boolean

Note that the text type behaves quite differently from the others - this is the type that is indexed as fulltext, and is searched using the keywords method inside the search DSL. Text fields cannot have restrictions set on them, nor can they be used in order statements or for facets. All other types are indexed literally, and thus can be used for all of those operations. They will not, however, be searched in fulltext. In this way, Sunspot provides a complete barrier between fulltext fields and value fields.

It is fine to specify a field both as a text field and a string field; internally, the fields will have different names so there is no danger of conflict.

Dynamic Fields

For use cases which have highly dynamic data models (for instance, an open set of key-value pairs attached to a model), it may be useful to defer definition of fields until indexing time. Sunspot exposes dynamic fields, which define a data accessor (either attribute or virtual, see above), which accepts a hash of field names to values. Note that the field names in the hash are internally scoped to the base name of the dynamic field, so any time they are referred to, they are referred to using both the base name and the dynamic (runtime-specified) name.

Dynamic fields are speficied in the setup block using the type name prefixed by dynamic_. For example:

Sunspot.setup(Post) do
  dynamic_string :custom_values do
    key_value_pairs.inject({}) do |hash, key_value_pair|
      hash[key_value_pair.key.to_sym] = key_value_pair.value
    end
  end
end

If you later wanted to facet all of the values for the key “cuisine”, you could issue:

Sunspot.search(Post) do
  dynamic :custom_values do
    facet :cuisine
  end
end

In the documentation, :custom_values is referred to as the “base name” - that is, the one specified statically - and :cuisine is referred to as the dynamic name, which is the part that is specified at indexing time.



132
133
134
# File 'lib/sunspot.rb', line 132

def setup(clazz, &block)
  Setup.setup(clazz, &block)
end