Skip to content

Search added for venues (sunspot and sql) #9

Closed
wants to merge 4 commits into from
View
1 CONTRIBUTORS.md
@@ -32,6 +32,7 @@ These people have contributed to Calagator's design and implementation:
* Markus Roberts
* Michael Bunsen
* Paige Saez
+ * Pam Selle
* Reid Beels
* Sam Keen
* Sam Livingston-Gray
View
1 Gemfile
@@ -50,6 +50,7 @@ gem 'validation_reflection', '1.0.0'
gem 'acts-as-taggable-on', '2.0.6'
gem 'themes_for_rails', '0.4.2'
gem 'jquery-rails', '1.0.12'
+gem 'pg'
# gem 'paper_trail_manager', :git => 'https://github.com/igal/paper_trail_manager.git'
# gem 'paper_trail_manager', :path => '../paper_trail_manager'
View
7 app/controllers/venues_controller.rb
@@ -27,11 +27,12 @@ def index
if params[:term].present? # for the ajax autocomplete widget
conditions = ["title LIKE :query", {:query => "%#{params[:term]}%"}]
+ @venues = scoped_venues.find(:all, :order => 'lower(title)', :conditions => conditions)
elsif params[:query].present?
- conditions = ["title LIKE :query OR description LIKE :query", {:query => "%#{params[:query]}%"}]
+ @venues = Venue.search(params[:query], :include_closed => params[:include_closed], :wifi => params[:wifi])
+ else
+ @venues = scoped_venues.all
end
-
- @venues = scoped_venues.order('lower(title)').where(conditions)
else # default view
@most_active_venues = scoped_venues.limit(10).order('events_count DESC')
@newest_venues = scoped_venues.limit(10).order('created_at DESC')
View
45 lib/search_engine/sql.rb
@@ -5,8 +5,48 @@ class SearchEngine::Sql < SearchEngine::Base
def self.add_searching_to(model)
case model.new
- when Venue, Source
+ when Source
# Do nothing
+ when Venue
+ model.class_eval do
+ # Return an Array of non-duplicate Venue instances matching the search +query+..
+ #
+ # Options:
+ # * :order => How to order the entries? Defaults to :name. Permitted values:
+ # * :name => Sort by event title
+ # * :title => same as :name
+ # * :limit => Maximum number of entries to return. Defaults to +solr_search_matches+.
+ # * :wifi => Require wifi
+ # * :include_closed => Include closed venues? Defaults to false.
+ def self.search(query, opts={})
+ wifi = opts[:wifi]
+ include_closed = opts[:include_closed] == true
+ limit = opts[:limit] || 50
+
+ scoped_venues = Venue.non_duplicates
+ # Pick a subset of venues (we want in_business by default)
+ unless include_closed
+ scoped_venues = scoped_venues.in_business
+ end
+
+ scoped_venues = scoped_venues.with_public_wifi if wifi
+
+ order = \
+ case opts[:order].try(:to_sym)
+ when nil, :name, :title
+ 'LOWER(venues.title) ASC'
+ else
+ raise ArgumentError, "Unknown order: #{order}"
+ end
+
+ keywords = query.split(" ")
+ tag_conditions = Array.new(keywords.size, "LOWER(tags.name) = ?").join(" OR ")
+
+ conditions = ["title LIKE ? OR description LIKE ? OR (#{tag_conditions})", *(["%#{query}%", "%#{query}%"] + keywords) ]
+ return scoped_venues.joins('LEFT OUTER JOIN taggings on taggings.taggable_id = venues.id AND taggings.taggable_type = "Venue"',
+ 'LEFT OUTER JOIN tags ON tags.id = taggings.tag_id').where(conditions).order(order).group(Venue.columns.map(&:name).map{|attribute| "venues.#{attribute}"}.join(', ')).limit(limit)
+ end
+ end
when Event
model.class_eval do
# Return an Array of non-duplicate Event instances matching the search +query+..
@@ -48,7 +88,8 @@ def self.search(query, opts={})
conditions_arguments = [Date.yesterday.to_time] + conditions_arguments
end
conditions = [conditions_text, *conditions_arguments]
- return Event.joins(:taggings, 'LEFT OUTER JOIN tags ON tags.id = taggings.tag_id').includes(:venue).where(conditions).order(order).group(Event.columns.map(&:name).map{|attribute| "events.#{attribute}"}.join(', ')).limit(limit)
+ return Event.joins('LEFT OUTER JOIN taggings on taggings.taggable_id = events.id AND taggings.taggable_type = "Event"',
+ 'LEFT OUTER JOIN tags ON tags.id = taggings.tag_id').includes(:venue).where(conditions).order(order).group(Event.columns.map(&:name).map{|attribute| "events.#{attribute}"}.join(', ')).limit(limit)
end
end
else
View
74 lib/search_engine/sunspot.rb
@@ -5,8 +5,80 @@ class SearchEngine::Sunspot < SearchEngine::Base
def self.add_searching_to(model)
case model.new
- when Venue, Source
+ when Source
# Do nothing
+ when Venue
+ # Add search to venues
+ model.class_eval do
+ # Return an Array of non-duplicate Venue instances matching the search +query+..
+ #
+ # Options:
+ # * :order => How to order the entries? Defaults to :score. Permitted values:
+ # * :score => Sort with most relevant matches first
+ # * :name => Sort by event title
+ # * :title => same as :name
+ # * :limit => Maximum number of entries to return. Defaults to +solr_search_matches+.
+ # * :wifi => Require wifi
+ # * :include_closed => Include closed venues? Defaults to false.
+ def self.search(query, opts={})
+ wifi = opts[:wifi]
+ include_closed = opts[:include_closed] == true
+ limit = opts[:limit] || 50
+
+ # Sunspot 1.2.1 seems to ignore pagination, e.g.:
+ ### paginate(:page => 1, :per_page => 100)
+ Sunspot.config.pagination.default_per_page = 100
+
+ ordering = \
+ case opts[:order].try(:to_sym)
+ when :name, :title
+ [:title, :asc]
+ when :score
+ [:score, :desc]
+ else
+ nil
+ end
+
+ searcher = self.solr_search do
+ keywords(query)
+ ordering ?
+ order_by(*ordering) :
+ order_by(:score, :desc)
+ with(:duplicate_for_solr, false)
+ with(:wifi, true) if wifi
+ with(:closed, false) unless include_closed
+ end
+
+ return searcher.results.uniq
+ end
+ # return venues
+
+ # Do this last to prevent Sunspot from taking over our ::search method.
+ unless Rails.env == 'test'
+ # Why aren't these loaded by default!?
+ include Sunspot::Rails::Searchable unless defined?(self.solr_search)
+ Sunspot::Adapters::InstanceAdapter.register(Sunspot::Rails::Adapters::ActiveRecordInstanceAdapter, self)
+ Sunspot::Adapters::DataAccessor.register(Sunspot::Rails::Adapters::ActiveRecordDataAccessor, self)
+
+ searchable do
+ text :title, :default_boost => 3
+ string :title
+ text :description
+ text :address
+ text :street_address
+ text :postal_code
+ text :locality
+ text :region
+ text :tag_list, :default_boost => 3
+ text :url
+ boolean :closed
+ boolean :wifi
+ boolean :duplicate_for_solr do |record|
+ record.duplicate_of_id.present?
+ end
+ end
+ end
+ end
when Event
model.class_eval do
# Return an Array of non-duplicate Event instances matching the search +query+..
View
3 lib/tasks/sunspot_extras.rake
@@ -14,6 +14,9 @@ namespace :sunspot do
Event.find_in_batches(:batch_size => 100, :include => [:venue, :tags]) do |events|
events.each(&:index)
end
+ Venue.find_in_batches(:batch_size => 100, :include => [:tags]) do |venues|
+ venues.each(&:index)
+ end
Sunspot.optimize
Sunspot.commit
end
View
10 themes/default/views/site/about.html.erb
@@ -16,9 +16,9 @@
<h2><a name='find_local_events'>Find local events</a></h2>
<h3>Browse</h3>
<ul>
- <li><a href='http://calagator.org/'>Browse an overview</a> of events for the next two weeks.</li>
- <li><a href='http://calagator.org/events'>Browse events</a> in the future and past, and filter by date.</li>
- <li><a href='http://calagator.org/venues'>Browse venues</a> and view future events occurring at each venue.</li>
+ <li><%= link_to "Browse an overview", root_path %> of events for the next two weeks.</li>
+ <li><%= link_to "Browse events", events_path %> in the future and past, and filter by date.</li>
+ <li><%= link_to "Browse venues", venues_path %> and view future events occurring at each venue.</li>
</ul>
<h3>Search</h3>
@@ -35,8 +35,8 @@
<h2><a name='share_local_events'>Share local events</a></h2>
<ul>
- <li><a href='http://calagator.org/events/new'>Add</a> an event by entering its details.</li>
- <li><a href='http://calagator.org/sources/new/'>Import</a> event(s) by entering a URL to a feed or webpage, or via a bookmarklet.</li>
+ <li><%= link_to "Add", new_event_path %> an event by entering its details.</li>
+ <li><%= link_to "Import", new_source_path %> event(s) by entering a URL to a feed or webpage, or via a bookmarklet.</li>
<li><strong>Improve</strong> content by editing events and venues.</li>
</ul>
Something went wrong with that request. Please try again.