Skip to content
This repository has been archived by the owner on Mar 19, 2021. It is now read-only.

Commit

Permalink
Merge pull request mbleigh#469 from ExamTime/master
Browse files Browse the repository at this point in the history
Featured added: Option to order by number of matching tags.
  • Loading branch information
seuros committed Jan 27, 2014
2 parents e562e0f + f081853 commit ad6b492
Show file tree
Hide file tree
Showing 3 changed files with 26 additions and 4 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ As such, a _Feature_ would map to either major or minor. A _bug fix_ to a patch.

* Breaking Changes
* Features
* [@jamesburke-examtime #467 Add :order_by_matching_tag_count option](https://github.com/mbleigh/acts-as-taggable-on/pull/469)
* Fixes
* [@rafael #406 Dirty attributes not correctly derived](https://github.com/mbleigh/acts-as-taggable-on/pull/406)
* [@bzbnhang #440 Did not respect strict_case_match](https://github.com/mbleigh/acts-as-taggable-on/pull/440)
Expand Down
17 changes: 13 additions & 4 deletions lib/acts_as_taggable_on/acts_as_taggable_on/core.rb
Original file line number Diff line number Diff line change
Expand Up @@ -71,13 +71,15 @@ def grouped_column_names_for(object)
# @param [Hash] options A hash of options to alter you query:
# * <tt>:exclude</tt> - if set to true, return objects that are *NOT* tagged with the specified tags
# * <tt>:any</tt> - if set to true, return objects that are tagged with *ANY* of the specified tags
# * <tt>:order_by_matching_tag_count</tt> - if set to true and used with :any, sort by objects matching the most tags, descending
# * <tt>:match_all</tt> - if set to true, return objects that are *ONLY* tagged with the specified tags
# * <tt>:owned_by</tt> - return objects that are *ONLY* owned by the owner
#
# Example:
# User.tagged_with("awesome", "cool") # Users that are tagged with awesome and cool
# User.tagged_with("awesome", "cool", :exclude => true) # Users that are not tagged with awesome or cool
# User.tagged_with("awesome", "cool", :any => true) # Users that are tagged with awesome or cool
# User.tagged_with("awesome", "cool", :any => true, :order_by_matching_tag_count => true) # Sort by users who match the most tags, descending
# User.tagged_with("awesome", "cool", :match_all => true) # Users that are tagged with just awesome and cool
# User.tagged_with("awesome", "cool", :owned_by => foo ) # Users that are tagged with just awesome and cool by 'foo'
def tagged_with(tags, options = {})
Expand All @@ -90,6 +92,7 @@ def tagged_with(tags, options = {})
conditions = []
having = []
select_clause = []
order_by = []

context = options.delete(:on)
owned_by = options.delete(:owned_by)
Expand Down Expand Up @@ -177,25 +180,31 @@ def tagged_with(tags, options = {})
end
end

taggings_alias, _ = adjust_taggings_alias("#{alias_base_name}_taggings_group"), "#{alias_base_name}_tags_group"
if options.delete(:order_by_matching_tag_count)
select_clause = "#{table_name}.*, COUNT(#{taggings_alias}.tag_id) AS #{taggings_alias}_count"
group_columns = ActsAsTaggableOn::Tag.using_postgresql? ? grouped_column_names_for(self) : "#{table_name}.#{primary_key}"
group = group_columns
order_by << "#{taggings_alias}_count DESC"

if options.delete(:match_all)
elsif options.delete(:match_all)
taggings_alias, _ = adjust_taggings_alias("#{alias_base_name}_taggings_group"), "#{alias_base_name}_tags_group"
joins << "LEFT OUTER JOIN #{ActsAsTaggableOn::Tagging.table_name} #{taggings_alias}" +
" ON #{taggings_alias}.taggable_id = #{quote}#{table_name}#{quote}.#{primary_key}" +
" AND #{taggings_alias}.taggable_type = #{quote_value(base_class.name, nil)}"


group_columns = ActsAsTaggableOn::Tag.using_postgresql? ? grouped_column_names_for(self) : "#{table_name}.#{primary_key}"
group = group_columns
having = "COUNT(#{taggings_alias}.taggable_id) = #{tags.size}"
end

order_by << options[:order] if options[:order].present?

select(select_clause).
joins(joins.join(" ")).
where(conditions.join(" AND ")).
group(group).
having(having).
order(options[:order]).
order(order_by.join(", ")).
readonly(false)
end

Expand Down
12 changes: 12 additions & 0 deletions spec/acts_as_taggable_on/taggable_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -369,6 +369,18 @@
TaggableModel.tagged_with(["depressed", "css"], :order => 'taggable_models.name', :any => true).to_a.should == [bob, frank]
end

it "should be able to order by number of matching tags when matching any" do
bob = TaggableModel.create(:name => "Bob", :tag_list => "fitter, happier, more productive", :skill_list => "ruby, rails, css")
frank = TaggableModel.create(:name => "Frank", :tag_list => "weaker, depressed, inefficient", :skill_list => "ruby, rails, css")
steve = TaggableModel.create(:name => 'Steve', :tag_list => 'fitter, happier, more productive', :skill_list => 'c++, java, ruby')

TaggableModel.tagged_with(["ruby", "java"], :any => true, :order_by_matching_tag_count => true, :order => 'taggable_models.name').to_a.should == [steve, bob, frank]
TaggableModel.tagged_with(["c++", "fitter"], :any => true, :order_by_matching_tag_count => true, :order => 'taggable_models.name').to_a.should == [steve, bob]
TaggableModel.tagged_with(["depressed", "css"], :any => true, :order_by_matching_tag_count => true, :order => 'taggable_models.name').to_a.should == [frank, bob]
TaggableModel.tagged_with(["fitter", "happier", "more productive", "c++", "java", "ruby"], :any => true, :order_by_matching_tag_count => true, :order => 'taggable_models.name').to_a.should == [steve, bob, frank]
TaggableModel.tagged_with(["c++", "java", "ruby", "fitter"], :any => true, :order_by_matching_tag_count => true, :order => 'taggable_models.name').to_a.should == [steve, bob, frank]
end

context "wild: true" do
it "should use params as wildcards" do
bob = TaggableModel.create(:name => "Bob", :tag_list => "bob, tricia")
Expand Down

0 comments on commit ad6b492

Please sign in to comment.