I have a difficult filtering problem in my Rails app. The user wants to filter items using tags. This is no problem, I just use a named scope. But the tags come in boxes that the user can choose from, like this:
Size
[XS]
[S]
Color
[Red]
[Blue]
The tags are in “tagsets”, one tagset to many tags.
My question: how can I chain together multiple filter queries when the user wants to narrow down the search?
When the user picks XS from the first box, it should filter the result set on that tag alone. When the user picks XS and S, we add to the filter and get more results. Still not a problem with a named scope. But what if the user wants to filter XS and S and then Red color from the second box? That’s when things get hairy – I can’t just tack on another Item.by_tag(“tagname”) for that tag, it needs to be a tag filter on the results I already have, narrowing the results down.
Multiple checkboxes in the same tagset box – more results. A check in another tagset box – fewer results. I tried doing one SELECT for each tagset box and then merging the result sets in ruby to show only the hits that were in all the sets, but that breaks the pagination since I never know how many results will be returned. It feels like this needs to be one query and it’s probably some kind of join, but I’m not great at SQL and I can’t figure it out. This is what the relevant parts of the schema looks like:
create_table "items" do |t|
t.string "title"
end
create_table "items_tags", :id => false do |t|
t.integer "item_id"
t.integer "tag_id"
end
create_table "tags" do |t|
t.string "title"
t.integer "tagset_id"
end
create_table "tagsets" do |t|
t.string "title"
end
It seems that Rails doesn’t provide a way to build an OR query. So you have to do it by yourself.
According to the schema, an Item has_and_belongs_to_many :tags, right?
In Rails 2.3, following named_scopes should work. If you’re using Rails 3, you need to rewrite it.
Then you call it like this:
I am assuming that you’re using MySQL.