Skip to content

Instantly share code, notes, and snippets.

@dreyks
Created August 4, 2015 11:24
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save dreyks/13182f572c7ed38b7d4e to your computer and use it in GitHub Desktop.
Save dreyks/13182f572c7ed38b7d4e to your computer and use it in GitHub Desktop.
Thinking Sphinx monkeypatches
# fixing incorrect quoting of full table names
# e.g. db_name.table_name has to be quoted as
# `db_name`.`table_name` not `db_name.table_name`
ThinkingSphinx::ActiveRecord::ColumnSQLPresenter.class_eval do
def with_table
return __name if string?
return nil unless exists?
# original code does this wrong. See +properly_quoted_table+
quoted_table = escape_table? ? properly_quoted_table : table
"#{quoted_table}.#{adapter.quote __name}"
end
private
def properly_quoted_table
table.split('.').map { |t| adapter.quote(t) }.join('.')
end
end
# original code acts weird when sql_options[:order] is passed
ThinkingSphinx::Middlewares::ActiveRecordTranslator::Inner.class_eval do
def call
results_for_models
# original code
# context[:results] = if sql_options[:order]
# # this produces *nil* if nothing found which leads to crash when checking for stale ids
# results_for_models.values.first
# else
# context[:results].collect { |row| result_for(row) }
# end
context[:results].collect! { |row| result_for(row) }
end
end
# Storing each STI class under it;s own name leads to multiple identical queries
# when populating results.
# ActiveRecord::Inheritance#base_class returns correct parent class for both STI
# parent and children. Also correct for non-STI classes
ThinkingSphinx::RealTime::Index::Template.class_eval do
def class_column
[:class, :base_class, :name]
end
end
# this allows searching on scopes
# Article.select(:id, :user_id).includes(:tags, :users).search('sometext')
# becomes
# Article.search('sometext', sql: {select: [:id, :user_id], includes: [:tags, :users]})
ThinkingSphinx::ActiveRecord::Base::ClassMethods.class_eval do
private
def merge_search(search, query, options)
merger = ThinkingSphinx::Search::Merger.new search
merger.merge! *default_sphinx_scope_response if default_sphinx_scope?
if current_scope && !merger.search.options[:ignore_scopes]
merger.merge! query, scope_to_sql_options(options)
else
merger.merge! query, options
end
result = merger.merge! nil, :classes => [self]
result.populate if result.options[:populate]
result
end
def scope_to_sql_options(options)
sql_options = [:includes, :joins, :order, :select, :group].map do |method|
[method, current_scope.send("#{method}_values")]
end.to_h
options.merge(sql: sql_options)
end
end
# sql_options[:include] should really be sql_options[:includes] to allow nice
# scoped searching code
ThinkingSphinx::Middlewares::ActiveRecordTranslator::Inner.class_eval do
private
def model_relation_with_sql_options(relation)
# sql_options[:includes], not sql_options[:include]
relation = relation.includes sql_options[:includes] if sql_options[:includes]
relation = relation.joins sql_options[:joins] if sql_options[:joins]
relation = relation.order sql_options[:order] if sql_options[:order]
relation = relation.select sql_options[:select] if sql_options[:select]
relation = relation.group sql_options[:group] if sql_options[:group]
# for backwards compatibility
relation = relation.includes sql_options[:include] if sql_options[:include]
relation
end
end
# Article.facets(with: {user_id: 123}).for(date: 20150707)
# resulted in losing user_id = 123 in sphinx query
ThinkingSphinx::FacetSearch.class_eval do
def for(facet_values = {})
filter_facets = facet_values.keys.collect { |key|
facets.detect { |facet| facet.name == key.to_s }
}
tmp_options = options.merge(:indices => index_names_for(*filter_facets))
# poor man's Hash#deep_merge
# (const_get because we're in class eval)
self.class.const_get(:Filter).new(facets, facet_values).to_hash.each do |filter_type, filter_value|
if tmp_options[filter_type]
tmp_options[filter_type].merge! filter_value
else
tmp_options[filter_type] = filter_value
end
end
ThinkingSphinx::Search.new query, tmp_options
# original code overwrites options[:with] while merging
# ThinkingSphinx::Search.new query, options.merge(
# :indices => index_names_for(*filter_facets)
# ).merge(Filter.new(facets, facet_values).to_hash)
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment