Unique search results in Rails - ruby-on-rails

I am using Ernie Miller's Metasearch in a rails 3 app and have run into a problem.
In my Model
class Prospect < ActiveRecord::Base
has_many :steps
In my controller
#search=Prospect.search(params[:search])
In my view (search form)
<%= f.date_select :steps_updated_at_greater_than%>
This works such that I get all the Prospects where steps have been updated since date_select. My problem is that if there has been multiple updated steps, I get duplicates in #search, which I do not want when I go to format my search results in a view.
My question is what is the best way to remove the duplicates from #search? Or prevent duplicates from being added to #search in the first place?

#search=Prospect.search(params[:search]).select('DISTINCT step_id')

#search = Prospect.search(params[:search])
#prospects = #search.relation.uniq

You want:
#search = Prospect.search(params[:search])
#search.relation.select("DISTINCT(step_id), prospects.*")

Related

Rails scaffolding belongs_to - showing #<MyClass:xxxx>

Experienced Java developer, new to Rails - wondering about belongs_to relationship in scaffolding.
Saw another answer like this
Does rails scaffold command support generate belongs_to or many to many model middle table migration info?
and followed the rails generate scaffold_controller obj:references pattern.
The index/show page is showing #<MyClass:xxxx> instead of the string I want - is there a method in the target class (parent side of the belongs_to) I need to override to specify the identifier?
Also in the edit view, it looks like it's trying to modify the reference as a string rather than as drop-down - is there something I need to specify to make that happen?
Thanks!
BTW - I was able to get similar scaffolding to work in Django and Grails, where the foreign key turned into a drop-down; I'm hoping Rails is equally easy and I'm just missing it.
You can override the #to_s method on the instances, as it is the one being called.
class FooDoodle < ActiveRecord::Base
def to_s
name
end
end
That's when showing a record.
However, when you're actually using the form to set the associations, scaffold will only generate an input field in the view so you can enter the id. You could have a dropdown menu for example, but the options for that dropdown would somehow have to be selected in a manner.
For example, if there are 2000 possible associated records, which ones do you show? Do you show the 2000? Only the first 10? That logic would go into your controller.
So, for example:
class FooDoodlesController < ApplicationController
def edit
#foodoodle = FooDoodle.find(params[:id])
#friends = #foodoodle.possible_friends # or else
end
end
and using select and options_for_select as choices
# _form.html.erb
<%= form_for #foodoodle do |f| %>
<%= f.label :friend %>
<%= f.select :friend, #friends.map{ |p| [p.to_s, p.id] } %>

How Do I Use collection_select Command for Multiple Values to Update A Second Model?

I am developing a Media Library (model MediaLibrary) where I want to be able to assign multiple topics to each item.
I have created a model MediaTopic that will contain the id of the media library item and the topic id from the Topic model.
The MediaLibrary model has_many :media_topics. The MediaTopic model belongs_to :media_library.
Here is my collection_select statement.
<%= f.label :topic, "Main Topic" %><%= collection_select(:media_library, :topic_id, Topic.order('name'), :id, :name, {prompt: true}, {multiple: true}) %>
I currently have :topic_id in the MediaLibrary model. I got an obvious error where it is trying to write an array of ids into a single integer value. I want to keep the current command without the multiple: true parameter and add a new command changing :topic_id to something that can hold more than one value then create the MediaTopic rows. When I change :topic_id in the collection_select statement above I get an undefined method error. My goal is to get the new logic working before I drop the column.
How do I capture the array that the above collection_select statement is creating? Once I figure that out I can loop through the array and build my MediaTopic rows. I thought about maybe using fields_for. However after seeing that I already have an array that the collection_select is creating I would like to just access the array and loop through it.
I have seen several solutions for more difficult situations on Stack Overflow. I have tried piecing them together but to no avail. I hope that since I know I am creating an array of IDs that somehow I can assign them to something in the collection_select statement.
Any help would be appreciated. I'm obviously missing something that I will probably scream DUH if someone gives me the answer. I'm just not seeing it at the moment.
UPDATE 2/18/2013 7:10 am
Here is the code that I ended up using in my view:
<%= fields_for :media_topics do |media_topic| %>
<%= media_topic.label :topic, "Topics" %><%= media_topic.collection_select(:topic_id, Topic.order('name'), :id, :name, {}, {multiple: true}) %>
<% end %>
Here is the code that I ended up using in my controller (create):
#media_library = MediaLibrary.new(params[:media_library])
#media_topics = params[:media_topics]
if #media_library.save
if #media_topics
#topic_ids = #media_topics["topic_id"]
#topic_ids.each do |topic|
if topic.blank?
else
#media_topic = MediaTopic.new
#media_topic.media_library_id = #media_library.id
#media_topic.topic_id = topic.to_i
#media_topic.save
end
end
end
end
Here is the code that I ended up using in my controller (update):
#media_library = MediaLibrary.update_attributes(params[:media_library])
#media_topics = params[:media_topics]
if #media_library.save
if #media_topics
MediaTopic.where("media_library_id = ?", params[:id]).destroy_all
#topic_ids = #media_topics["topic_id"]
#topic_ids.each do |topic|
if topic.blank?
else
#media_topic = MediaTopic.new
#media_topic.media_library_id = #media_library.id
#media_topic.topic_id = topic.to_i
#media_topic.save
end
end
end
end
In my loops I checked for the array item for blank because the 1st entry of the array was blank. I assume it has something to do with the prompt feature. Anyway my code is working well. I have duplicated it with another model and will probably do this for two more. Next trick is to add this to my already complicated search engine logic.
Since I originally wrote this question I have rewritten my application in Rails 4. I was able to develop a solution to this using has_many through and replacing my fields_for statement with a straight collection_select statement. I ended up answering my own question. The answer on the link below details how I finally solved this issue.
Rails 4 - Using collection_select to get an array of ids for an Intermediate Table with Nested Attributes

How can I search across multiple tables(models) with Sunspot Solr?

I want users to be able to use one search box to search for different objects. I would differentiate them on the results page. Both would be full text search.
Should I make a Search controller and load everything into the index action with something like:
#posts = Post.all
#groups = Group.all
Something tells me that would be fantastically inefficient.
I'm not really sure where to start, I haven't managed to find anything addressing this question on the interwebs, but if I have overlooked something let me know.
Thanks
EDIT:
here's my search bar that is available globally on my website:
-form_tag posts_path, :method => :get do
=text_field_tag :search, params[:search], :id => 'searchfield'
=submit_tag '',:name => nil, :id => 'searchbutton'
it only searches the "Post" model right now, and displays the results on the Post#index view
I want to be able to have queries typed into the search box be searched for in both the Post and Group tables, and the results be displayed in two separate columns on the same page. maybe through a search controller/view
If you want to search both types at once, you can use this form:
# Pass your models in by class constant
Sunspot.search(Post,Group) do |s|
s.fulltext(params[:search])
end
This is documented in the wiki:
https://github.com/sunspot/sunspot/wiki/Working-with-search#initiating-a-search
Add the searchable directive from sunspot solr to your models for indexing. For example:
class Post < ActiveRecord::Base
searchable do
text :title, :body
end
end
class Group < ActiveRecord::Base
searchable do
text :name
end
end
If you have existing data in DB make sure to run rake sunspot:solr:reindex for indexing. For new data the indexing will be done in a hook.
Now you can search:
#posts = Post.search {fulltext params[:search]}
#groups = Group.search {fulltext params[:search]}
Now you have the data for your two columns.
This is not the answer, but I found something useful. It is a gem that helps you searching across multiple tables:
https://github.com/toptierlabs/acts_as_fulltextable.
It helps you keeping all the searchable data (from different models) in one place.

Display search query in results of basic search in Rails

I built a basic search form that queries one column in one table of my app. I followed episode 37 Railscast: http://railscasts.com/episodes/37-simple-search-form
Here's my problem. I want to display the search query that the user makes in the view that displays the search results. In my app, the search queries the zip code column of my profile model, and returns a list of profiles that contain the right zip code. On the top of the list of profiles returned from the search, I want it to say "Profiles located in [zip code that was queried]."
I'm sure I can do this because the queried zip code gets passed into the url displaying the results. So if the url can pick it up, there must be some way to display it in the view on the page as well. But I don't how.
Please keep in mind that I'm not using any search pluggins and I don't want to use any for now. This is my first app, so I don't want to add complexity where it's not needed.
Per Ryan's instructions in the Railscast, here's my setup:
PROFILES CONTROLLER
def index
#profiles = Profile.search(params[:search])
end
PROFILE MODEL
def self.search(search)
if search
find(:all, :conditions => ['zip LIKE ?', "%#{search}%"])
else
find(:all)
end
end
PROFILE/INDEX.HTML.ERB
<% form_tag ('/profiles', :method => :get) do %>
<%= text_field_tag :search, params[:search], :maxlength => 5 %>
<%= submit_tag "Go", :name => nil %>
<% end %>
The search itself is working perfectly, so that's not an issue. I just need to know how to display the queried zip code in the view displaying the results.
Thanks!
Just set it to an instance variable and use that.
def index
#search = params[:search]
#profiles = Profile.search(#search)
end
In your view, you can reference #search.
Also, as a friendly tip, please use an indent of 2 spaces for Rails code. It's the standard way to do it, and others who are reading your code will appreciate it.

How do I implement text fields with autocomplete for habtm fields?

I tried the example from Rails Cookbook and managed to get it to work. However the text_field_with_auto_complete works only for one value.
class Expense < ActiveRecord::Base
has_and_belongs_to_many :categories
end
In the New Expense View rhtml
<%= text_field_with_auto_complete :category, :name %>
Auto complete works for the first category. How do I get it working for multiple categories? e.g. Category1, Category2
Intended behavior: like the StackOverflow Tags textbox
Update:
With some help and some more tinkering, I got multiple comma-seperated autocomplete to show up (will post code-sample here).
However on selection, the last value replaces the content of the text_field_with_auto_complete. So instead of Category1, Category2.. the textbox shows Category2 when the second Category is selected via auto-complete. Any ideas how to rectify this?
If you are just trying to support multiple instances of autocomplete per field, you can pass a delimiter to the autocomplete options with the symbol :token. This provides a delimiter to allow multiple results. Stackoverflow would use :token => ' ' (there should be a space between the quotes, but the autoformat is removing it) to specify space at the delimiter between multiple takes although ',' is more commonly used.
This is not quite your question, but I wouldn't recommend using HABTM anymore. You should create a join model and use has_many :through. (In your case you'd create a new model called ExpenseCategoryAssignment, or something)
The problem is that HABTM creates ambiguities that rails doesn't like, and it tends to expose bugs you wouldn't see otherwise.
You need to use "data-delimiter" param like this
<%= f.autocomplete_field :brand_name, welcome_autocomplete_brand_name_path, "data-delimiter" => ', ' %>

Resources