Skip to content

Commit

Permalink
* major cleanup. added 'delete_by_query' support. fixed bugs in 'find…
Browse files Browse the repository at this point in the history
…_by_solr' it was returning an array of an array

* added support for count_by_solr
* added support for eager loading of ActiveRecord models via :include
  • Loading branch information
ruckus committed Mar 19, 2010
1 parent 4ee7991 commit 3928b5e
Show file tree
Hide file tree
Showing 10 changed files with 131 additions and 138 deletions.
1 change: 0 additions & 1 deletion README
Expand Up @@ -50,7 +50,6 @@ results = Profile.find_by_solr(clause.to_s)
TODO
====

+ add support for eager loading of ActiveRecord models
+ add support for boosting at index time

Copyright (c) 2010 Cody Caughlan, released under the MIT license
16 changes: 16 additions & 0 deletions init.rb
@@ -1,4 +1,20 @@
require 'will_paginate' #required for pagination support
require 'solr_searchable'

#require 'rsolr/curb'
# monkey patch RSolr to use our new Curb based HTTP client
# module RSolr
# module Connectable
# def connect opts={}
# Client.new RSolr::Connection::Curb.new(opts)
# end
# end
# extend Connectable
# end
# begin
# RSolr.connect :url => '' #dummy
# rescue => ex
# #ignore
# end

ActiveRecord::Base.send(:extend, SolrSearchable::SolrSearchableMethods)
11 changes: 9 additions & 2 deletions lib/class_methods.rb
Expand Up @@ -18,8 +18,11 @@ def find_by_solr(query, options = {})
SolrSearchable::Handler::Select.new(query_options, self, environment_options).execute
end

# Raw Solr query - dont invoke any ActiveRecord callbacks or load any models from the DB
# Returns the response from Solr as-is (Ruby style)
def solr_query(query, options = {})
query_options = generate_query_options(query, options)
activerecord_options = extract_activerecord_options(options)
SolrSearchable::Handler::Select.new(query_options, self, {:format => :raw}).execute
end

Expand All @@ -30,6 +33,11 @@ def count_by_solr(query, options = {})
result[:total_hits]
end

def delete_by_query(query)
rsolr = SolrSearchable::IO.get_connection
rsolr.delete_by_query(query)
end

# this is a copy of WillPaginate#paginate specialized for "find_by_solr"
def paginate_by_solr(*args, &block)
options = args.extract_options!
Expand All @@ -46,8 +54,7 @@ def paginate_by_solr(*args, &block)
pager.total_entries = found[:total_hits]
# Highlights?
#pager.highlights = found.highlights if found.highlights
found = found[:docs]
pager.replace found
pager.replace found[:docs]
rescue => ex
return pager.replace([])
Rails.logger.info("Search error: #{ex.message}")
Expand Down
16 changes: 12 additions & 4 deletions lib/handler/base.rb
@@ -1,12 +1,17 @@
module SolrSearchable
module Handler
class Base
attr_reader :handler, :model, :options
attr_reader :handler, :model, :options, :activerecord_options

def initialize(handler, model, options = {})
def initialize(handler, model, options = {}, activerecord_options = {})
@handler = handler
@model = model
@options = options
@activerecord_options = {}
if @options.has_key?(:include)
@activerecord_options[:include] = @options[:include]
@options.delete(:include)
end
end

def to_hash
Expand All @@ -20,11 +25,14 @@ def parse_response(solr_data)
:start => 0
}
return result if solr_data.nil? || !solr_data.has_key?('response')
#puts solr_data.inspect
if @options[:format] == :objects
ids = solr_data['response']['docs'].collect {|doc| doc["#{@model.solr_searchable_configuration[:primary_key_field]}"]}.flatten
if ids.compact.size > 0
ids.compact!
conditions = [ "#{@model.table_name}.#{@model.primary_key} IN (?)", ids ]
result = reorder(@model.find(:all, :conditions => conditions), ids)
@activerecord_options[:conditions] = conditions
result = reorder(@model.find(:all, @activerecord_options), ids).flatten
end
elsif @options[:format] == :raw
result = solr_data['response']['docs']
Expand All @@ -38,7 +46,7 @@ def parse_response(solr_data)
def reorder(things, ids)
ordered_things = []
ids.each do |id|
record = things.find {|thing| @model.record_id(thing).to_s == id.to_s}
record = things.select {|thing| @model.record_id(thing).to_s == id.to_s}
#raise "Out of sync! The id #{id} is in the Solr index but missing in the database!" unless record
if record
ordered_things << record
Expand Down
62 changes: 0 additions & 62 deletions lib/handler/select.rb
Expand Up @@ -32,72 +32,10 @@ def execute

def to_hash
hash = {}

# standard request param processing
# if @params[:sort]
# hash[:sort] = @params[:sort].collect do |sort|
# key = sort.keys[0]
# "#{key.to_s} #{sort[key] == :descending ? 'desc' : 'asc'}"
# end.join(',') if @params[:sort]
# end

# common parameter processing
hash[:debugQuery] = @params[:debug_query] if @params[:debug_query]
hash[:explainOther] = @params[:explain_other] if @params[:explain_other]

# # facet parameter processing
# if @params[:facets]
# # TODO need validation of all that is under the :facets Hash too
# hash[:facet] = true
# hash["facet.field"] = []
# hash["facet.query"] = @params[:facets][:queries]
# hash["facet.sort"] = (@params[:facets][:sort] == :count) if @params[:facets][:sort]
# hash["facet.limit"] = @params[:facets][:limit]
# hash["facet.missing"] = @params[:facets][:missing]
# hash["facet.mincount"] = @params[:facets][:mincount]
# hash["facet.prefix"] = @params[:facets][:prefix]
# if @params[:facets][:fields] # facet fields are optional (could be facet.query only)
# @params[:facets][:fields].each do |f|
# if f.kind_of? Hash
# key = f.keys[0]
# value = f[key]
# hash["facet.field"] << key
# hash["f.#{key}.facet.sort"] = (value[:sort] == :count) if value[:sort]
# hash["f.#{key}.facet.limit"] = value[:limit]
# hash["f.#{key}.facet.missing"] = value[:missing]
# hash["f.#{key}.facet.mincount"] = value[:mincount]
# hash["f.#{key}.facet.prefix"] = value[:prefix]
# else
# hash["facet.field"] << f
# end
# end
# end
# end
#
# # highlighting parameter processing - http://wiki.apache.org/solr/HighlightingParameters
# #TODO need to add per-field overriding to snippets, fragsize, requiredFieldMatch, formatting, and simple.pre/post
# if @params[:highlighting]
# hash[:hl] = true
# hash["hl.fl"] = @params[:highlighting][:field_list].join(',') if @params[:highlighting][:field_list]
# hash["hl.snippets"] = @params[:highlighting][:max_snippets]
# hash["hl.requireFieldMatch"] = @params[:highlighting][:require_field_match]
# hash["hl.simple.pre"] = @params[:highlighting][:prefix]
# hash["hl.simple.post"] = @params[:highlighting][:suffix]
# hash["hl.fragsize"] = @params[:highlighting][:fragsize] || 500
# end

# if @params[:collapse]
# hash["collapse.field"] = @params[:collapse][:field]
# end
#
# if @params[:order]
# hash["sort"] = @params[:order]
# end
hash = hash.merge(@params)
#super_hash = super.to_hash
# if @params[:query_type]
# super_hash[:qt] = @params[:query_type]
# end
hash.merge(super.to_hash)
end

Expand Down
19 changes: 1 addition & 18 deletions lib/parser_methods.rb
Expand Up @@ -6,7 +6,7 @@ module ParserMethods
def generate_query_options(query, options = {})
query_options = {}
query_options[:q] = query
query_options[:fl] = [solr_searchable_configuration[:primary_key_field], 'score']
query_options[:fl] = [solr_searchable_configuration[:primary_key_field], 'score', 'id']
if options[:fl] && options[:fl].is_a?(Array)
query_options[:fl].concat(options[:fl])
end
Expand All @@ -17,24 +17,7 @@ def generate_query_options(query, options = {})
query_options[:fq].concat(options[:fq])
end
query_options

# valid_options = [:fq, :offset, :limit, :facets, :models, :results_format, :order, :scores, :operator, :highlight, :collapse, :query_type]
# query_options = {}
# return if query.nil?
# raise "Invalid parameters: #{(options.keys - valid_options).join(',')}" unless (options.keys - valid_options).empty?
#
# query_options[:operator] = options[:operator]
# query_options[:fl] = [solr_searchable_configuration[:primary_key_field], 'score']
# query_options[:q] = query
# query_options[:fq] = (options[:fq] || [])
# query_options[:fq] << "#{solr_searchable_configuration[:type_field]}:#{self.name}"
#
# rsolr = SolrSearchable::IO.get_connection
# rsolr.select query_options

end # parse_query



end #ParserMethods
end #SolrSearchable
2 changes: 1 addition & 1 deletion lib/solr_searchable.rb
Expand Up @@ -94,7 +94,7 @@ def solr_searchable(opts = {})
end

#== ActiveRecord callback hooks
before_save :solr_save
after_save :solr_save
before_destroy :solr_destroy
end #solr_searchable

Expand Down
48 changes: 27 additions & 21 deletions test/fixtures/models.rb
@@ -1,6 +1,8 @@
class FakeProfile < ActiveRecord::Base

attr_accessor :first_name, :last_name, :bio, :age
#attr_accessor :first_name, :last_name, :bio, :age

belongs_to :fake_user

solr_searchable :fields => [
{:text => :first_name},
Expand All @@ -9,24 +11,28 @@ class FakeProfile < ActiveRecord::Base
{:string => :age}
]

def first_name
"Cody"
end

def last_name
"Caughlan"
end

def bio
"I am you!"
end

def age
30
end

def id
93
end

# def first_name
# "Cody"
# end
#
# def last_name
# "Caughlan"
# end
#
# def bio
# "I am you!"
# end
#
# def age
# 30
# end

#def id
# 93
#end

end

class FakeUser < ActiveRecord::Base
has_one :fake_profile
end
5 changes: 5 additions & 0 deletions test/schema.rb
@@ -1,6 +1,7 @@
ActiveRecord::Schema.define(:version => 0) do

create_table :fake_profiles, :force => true do |t|
t.integer :fake_user_id
t.string :first_name
t.string :last_name
t.text :bio
Expand All @@ -10,4 +11,8 @@
t.datetime :updated_at
end

create_table :fake_users, :force => true do |t|
t.string :email
end

end

0 comments on commit 3928b5e

Please sign in to comment.