Remove duplicates from custom search query - sql

I have a search method that combines the results from doing a fuzzy search using the fuzzily gem and doing a search in the tags used in acts_on_taggable gem.
if params[:search]
results = Post.find_by_fuzzy_title(params[:search])
tagged_results = Post.tagged_with("#{params[:search]}")
#posts = (Kaminari.paginate_array(results+tagged_results)).page(params[:page]).per(1)
else
The problem is that the returned query contains a concatenation of the two individual queries with duplicate results. What I would like to do is make sure a result is displayed only once even if it contains both a matching title and a matching tag to the search keywords.

Use this out of the ruby standard library
(Array1 + Array2).uniq

Related

Sunspot solr: boost scalar fields

I am scoping by multiple scalar fields and I am hoping to push the scoped results to the top of the search results without excluding results that do not meet the criteria for favorite authors.
Articles have and belong to many authors
This doesn't work but is what Im going for:
favorite_author_ids = #current_user.favorite_author_ids
#search = JournalArticle.solr_search do
fulltext params[:article_title]
any_of do
boost(2.0) {with(:author_ids), favorite_author_ids}
with(:author_ids), []
end
end
I suppose I could do two searches and concatenate the results, but Im wondering if there is a cleaner way.
It doesn't make sense to have the boost inside of an any_of block. You probably want to do something like this:
favorite_author_ids = #current_user.favorite_author_ids
#search = JournalArticle.solr_search do
fulltext params[:article_title] do
boost(2.0) {with(:author_ids, favorite_author_ids)}
end
end
You may need to increase the boost as well depending if you just want to increase the placement of favourite authors, or if you want them to always be at the top.

ActiveRecord search model using LIKE only returning exact matches

In my rails app I am trying to search the Users model based on certain conditions.
In particular, I have a location field which is a string and I want to search this field based on whether it contains the search string. For example, if I search for users with location 'oxford' I want it to also return users with a variation on that, like 'oxford, england'.
Having searched the web for the answer to this it seems that I should be using the LIKE keyword in the activerecord search, but for me this is only returning exact matches.
Here is a snippet of my code from the search method
conditions_array = []
conditions_array << [ 'lower(location) LIKE ?', options[:location].downcase ] if !options[:location].empty?
conditions = build_search_conditions(conditions_array)
results = User.where(conditions)
Am I doing something wrong? Or is using LIKE not the right approach to achieving my objective?
You need to do like '%oxford%'
% Matches any number of characters, even zero characters
conditions_array << [ 'lower(location) LIKE ?', "%#{options[:location].downcase}%" ] if !options[:location].empty?

Rails, Ransack: How to search HABTM relationship for "all" matches instead of "any"

I'm wondering if anyone has experience using Ransack with HABTM relationships. My app has photos which have a habtm relationship with terms (terms are like tags). Here's a simplified explanation of what I'm experiencing:
I have two photos: Photo 1 and Photo 2. They have the following terms:
Photo 1: A, B, C
Photo 2: A, B, D
I built a ransack form, and I make checkboxes in the search form for all the terms, like so:
- terms.each do |t|
= check_box_tag 'q[terms_id_in][]', t.id
If I use: q[terms_id_in][] and I check "A, C" my results are Photo 1 and Photo 2. I only want Photo 1, because I asked for A and C, in this query I don't care about B or D but I want both A and C to be present on a given result.
If I use q[terms_id_in_all][] my results are nil, because neither photo includes only A and C. Or, perhaps, because there's only one term per join, so no join matches both A and C. Regardless, I want just Photo 1 to be returned.
If I use any variety of q[terms_id_eq][] I never get any results, so I don't think that works in this case.
So, given a habtm join, how do you search for models that match the given values while ignoring not given values?
Or, for any rails/sql gurus not familiar with Ransack, how else might you go about creating a search form like I'm describing for a model with a habtm join?
Update: per the answer to related question, I've now gotten as far as constructing an Arel query that correctly matches this. Somehow you're supposed to be able to use Arel nodes as ransackers, or as cdesrosiers pointed out, as custom predicates, but thus far I haven't gotten that working.
Per that answer, I setup the following ransack initializer:
Ransack.configure do |config|
config.add_predicate 'has_terms',
:arel_predicate => 'in',
:formatter => proc {|term_ids| Photo.terms_subquery(term_ids)},
:validator => proc {|v| v.present?},
:compounds => true
end
... and then setup the following method on Photo:
def self.terms_subquery(term_ids)
photos = Arel::Table.new(:photos)
terms = Arel::Table.new(:terms)
photos_terms = Arel::Table.new(:photos_terms)
photos[:id].in(
photos.project(photos[:id])
.join(photos_terms).on(photos[:id].eq(photos_terms[:photo_id]))
.join(terms).on(photos_terms[:term_id].eq(terms[:id]))
.where(terms[:id].in(term_ids))
.group(photos.columns)
.having(terms[:id].count.eq(term_ids.length))
).to_sql
end
Unfortunately this doesn't seem to work. While terms_subquery produces the correct SQL, the result of Photo.search(:has_terms => [2,5]).result.to_sql is just "SELECT \"photos\".* FROM \"photos\" "
With a custom ransack predicate defined as in my answer to your related question, this should work with a simple change to your markup:
- terms.each do |t|
= check_box_tag 'q[id_has_terms][]', t.id
UPDATE
The :formatter doesn't do what I thought, and seeing as how the Ransack repo makes not a single mention of "subquery," you may not be able to use it for what you're trying to do, after all. All available options seem to be exhausted, so there would be nothing left to do but monkey patch.
Why not just skip ransack and query the "photos" table as you normally would with active record (or even with the Arel query you now have)? You already know the query works. Is there a specific benefit you hoped to reap from using Ransack?

Rails 3 Sunspot Limiting Selected Records

at the moment I'm doing a Select from multiple tables using Sunspot. I'm limiting the records selected with the condition that the creation date must have been within the past 5 days. However I would like to take this further and limit the number of records selected to say, 20? Is there any way to do this with sunspot? I'm not doing any fancy groupings or anything. Simply selecting records belonging to various models and displaying them onto the screen.
My current code:
#updates = Sunspot.search(Upload,Help, User...) do
with(:created_at).greater_than(5.days.ago)
order_by(:created_at, :desc)
end
I tried adding the 'limit' clause as specified by the readme on Github but that produced an undefined method error probably because I'm doing a global Sunspot search as opposed to a search on a specific model.
Error code:
#updates = Sunspot.search(Upload,Help, User...) do
with(:created_at).greater_than(5.days.ago)
order_by(:created_at, :desc)
limit(20)
end
Error message:
undefined method `limit' for #<Sunspot::DSL::Search:0x0000000790b8c8>
The "limit" method is for "groups". See the readme section on groups. Sunspot provides pagination (and works like a charm with the kaminari gem btw). See the section on solr pagination in the readme. Specifically, try this:
#updates = Sunspot.search(Upload,Help, User...) do
with(:created_at).greater_than(5.days.ago)
order_by(:created_at, :desc)
paginate page: 1, per_page: 20
end

Simple search using attribute, AND acts_as_taggable

I am building an app where I'd like to use a simple search to search through the object's title, AND tags (using acts_as_taggable_on) in one search
I am able to construct either, but not both, and am at my wits end trying to figure it out.
To search using tags, I use:
#post = Post.tagged_with(params[:search])
To search the object, I use:
#post = Post.search(params[:search])
And I wrote a method called search in the Post model, as follows:
def self.search(search)
if search
where('question LIKE ?', "%#{search}%")
else
scoped
end
end
Any idea how to combine these two queries? Any attempts so far have been unsuccessful mainly because there isn't a "tag" column in my Post model, etc.
I figured it out and testing it in my solution, I wrote the joins myself so it isn't as clean as I would like so if anybody has a better solution, I'd still love to see it. Here's what I came up with:
q = "%#{search}%"
Post.select('distinct posts.*').joins("LEFT JOIN taggings on posts.id = taggings.taggable_id")
.joins("LEFT JOIN tags on tags.id = taggings.tag_id")
.where('title LIKE ? OR tags.name LIKE ?', q, q )
This is working as expected in my tests. I am testing with three records. One has the search term in the title, another has just the tag and the third has both the title and the tag, so it shouldn't return duplicates.
Hope this helps.
I found this:
Search form with acts_as_taggable_on (Rails 3)
this might work, but I am also paging my results with the kaminari gem so when I use this approach, I get the following error:
undefined method `page' for []:Array
So this won't work in my situation. I would love to find a solution with a single query to handle the whole thing.
As a followup to Andrew's code, I used this search method in my model -- just adding case-insensitivity and making the post "title" query a bit more explicit since my model actually contained a "name" attribute which was ambiguous.
def self.search(search)
if search.present?
q = "%#{search}%"
Post.select('distinct posts.*').joins("LEFT JOIN taggings on posts.id = taggings.taggable_id")
.joins("LEFT JOIN tags on tags.id = taggings.tag_id")
.where('lower(posts.title) LIKE lower(?) OR lower(tags.name) LIKE lower(?)', q, q )
else
all
end
end