Using pg_search for multiple models - ruby-on-rails

Right now I have three models: restaurant, category and item. All of these table have an attribute for name. I'd like to set up search on the main page that can access all 3. The search will bring back the restaurant name, category name and / or item name when the search is conducted.
I'm using pg_search in a Rails 4 app.
https://github.com/Casecommons/pg_search
So far, I have bundled the gem, set up multi-search and ran the migration. My models currently look as follows:
MODELS
class Restaurant < ActiveRecord::Base
has_many :items
validates :name, presence: true
validates :gg, presence: true
include PgSearch
multisearchable against: :name
end
class Category < ActiveRecord::Base
has_many :items
validates :name, presence: true
validates :gg, presence: true
include PgSearch
multisearchable against: :name
end
class Item < ActiveRecord::Base
belongs_to :restaurants
belongs_to :categories
validates :name, presence: true
validates :gg, presence: true
include PgSearch
multisearchable against: :name
end
VIEW WITH SEARCHBOX
<%= form_tag restaurants_path, method: :get do %>
<%= text_field_tag :query, params[:query], class: "search-box" %>
<%= submit_tag "Search", name: nil, class: "btn btn-search" %>
<% end %>
RESTAURANTS CONTROLLER
class RestaurantsController < ApplicationController
def index
#restaurants = PgSearch.multisearch( params[:q] )
end
end
QUESTIONS
The controller is not getting back the multisearch items properly. How do I write this correctly?
If I want to implement all three search results on one view, should I set up a new controller or add the categories and items to the restaurants controller as well?
Please send along any good examples.

When you run the migration, you create a new table called pg_search_documents. That's the table that pg_search will query.
Most likely you have a problem with not having any data in that table. The docs say "If this model already has existing records, you will need to reindex this model to get existing records into the pg_search_documents table. See the rebuild task below."
You want to refer to this section of the documentation: https://github.com/Casecommons/pg_search#rebuilding-search-documents-for-a-given-class

Related

How to change model owner in Ruby in rails?

I use Ruby on rails and mongoid.
I have two models User.rb and Project.rb. If I want to change owner of Project model, how should I do this?
User.rb
class User
include Mongoid::Document
field :name, type: String
has_many :projects, dependent: :destroy
end
Project.rb
class Project
include Mongoid::Document
field :title, type: String
validates :user_id, presence: true
belongs_to :user, touch: true
end
in form.html.erb i have select mode
<div class="form-group">
<%= f.collection_select :user_id, User.all, :id, :name, class: 'form-control' %>
</div>
you should be able to just assign it to the user field and then save it to persist it to the database
project = Project.find(project_id)
new_owner = User.find(new_owner_id)
project.user = new_owner
project.save
i did not use mongoid long time ago, but you can try running these commands on your rails console:
project.user = owner_object;
project.save
OR
project.user_id = owner_id
project.save

Unpermitted parameter for join table via multiple select in Rails

I'm running into an error when nesting parameters in Rails 5: Unpermitted parameter: specialties
I have an Expertise model:
class Expertise < ApplicationRecord
has_many :buckets, through: :specialties
has_many :specialties
end
A Bucket model:
class Bucket < ApplicationRecord
has_many :expertises, through: :specialties
has_many :specialties
end
And a Specialty model:
class Specialty < ApplicationRecord
belongs_to :expertise
belongs_to :bucket
end
I'm trying to allow the User to edit his or her Expertises and adjust the Specialties associated with them. The #buckets are passed in from the controller, and the form currently looks like this:
<%= form_for(expertise) do |f| %>
<%= f.fields_for :specialties do |s| %>
<%= s.collection_select :bucket_ids, #buckets, :id, :name, {}, { multiple: true, class: "input" } %>
<% end %>
<% end %>
I based the form on this answer.
Here's the relevant snippet from the ExpertisesController:
def expertise_params
params.require(:expertise).permit(:user_id, :name, :rating, :description, specialties_attributes: [:id, :expertise_id, :bucket_id, :_destroy, bucket_ids: []])
end
And here are the parameters that are being passed in:
Parameters: {"expertise"=>{"specialties"=>{"bucket_ids"=>["", "1"]}, "description"=>""}, "id"=>"97"}
Specialties should be an array, right? I'm not sure how to do that.
The aim is to easily enable the User to select from the available Buckets (#buckets) to toggle his or her Expertise Specialties on or off. So let's say there are 5 Buckets available, the User would only be able to toggle on/off 5 possible Specialties for that Expertise.
Unpermitted parameter: specialties
You didn't set up accept_nested_attributes_for which spits out with that error
class Expertise < ApplicationRecord
has_many :specialties
has_many :buckets, through: :specialties
accepts_nested_attributes_for :specialties
end
When I try that, the nested fields_for form doesn't return any
specialties and so the HTML element is empty. Then, when I try to use
#expertise.specialties.build, I get undefined method bucket_ids for
Specialty because bucket_ids isn't actually an attribute, but
bucket_id is. Worth keeping in mind that the User needs to be able to
toggle multiple Specialties, each of which is tied to a Bucket (via a
bucket_id), and from what I've ready I'm supposed to use bucket_ids
(the plural) there
You don't need to have plural form(_ids) just because to accept multiple values. Just keep bucket_id to accept multiple values. And don't forget to build the associated model in the controller
def new
#expertise = Expertise.new
#expertise.specialties.build
end
Change bucket_ids to bucket_id in the form
<%= s.collection_select :bucket_id, #buckets, :id, :name, {}, { multiple: true, class: "input" } %>
And finally, expertise_params should be
def expertise_params
params.require(:expertise).permit(:user_id, :name, :rating, :description, specialties_attributes: [:id, :expertise_id, :_destroy, bucket_id: []])
end
Update:
Ok after some research, it looks like it should be bucket_ids, but the bucket_ids should be allowed as attribute for expertise. Check this post and tweak your form and expertise_params accordingly. You won't be needing accept_nested_attributes_for too!
The situation: Expertise has_many Buckets through Specialties and you want to update some bucket status of a specific expertise. So you can do this:
class ExpertisesController < ApplicationController
def your_action
#expertise = Expertise.find params[:id]
bucket_ids = params[:expertise][:specialties][:bucket_ids]
#expertise.specialties.where(id: bucket_ids).update(status: :on)
end
end

how to obtain a subset of records for a grouped_collection_select in Rails

I'm trying to obtain a subset of records for a grouped_collection_select, in the schema I have a Brand and a Model, and we handle different lines of products, when we request a product, we specify the line that we are going to request, so, we can have the Brands, and the models of that line, so the models are defined as follows:
Model for Brand
class Brand < ActiveRecord::Base
validates :brand_key, uniqueness: true, presence:true
has_many :models
accepts_nested_attributes_for :models, allow_destroy: true
end
Model for Modelx
class Modelx < ActiveRecord::Base
belongs_to :brand
belongs_to :line
validates :model_key, uniqueness: true, presence:true
end
Model for Line
class Line < ActiveRecord::Base
validates :line_key, uniqueness: true, presence:true, length: {in: 1..6}
has_many :line_features, -> { order(:sort_order) }
has_many :modelx
accepts_nested_attributes_for :line_features, allow_destroy: true
end
Model for Product
class Product < ActiveRecord::Base
belongs_to :vendor
belongs_to :line
belongs_to :brand
belongs_to :modelx
belongs_to :operating_status
has_many :line_features, -> { order(:sort_order)}
store_accessor :line_features
end
Controller for Product (new)
def new
#brands=brand.where(id: Modelx.select(:brand_id).where(line_id: params[:line_id]))
#modelxs=Modelx.where(line_id: params[:line_id])
...
end
excerpt from partial form
<%= f.collection_select( :brand_id, #brands, :id, :brand,{:prompt =>'Brands'}) %>
<%= f.grouped_collection_select(:modelx_id, #brands, :modelx, :brand, :id, :modelox) %>
Now, the problem that I'm facing is that when I display a model I need to present only the models available for that brand and line, but, the request is bringing all the models for the brand as it's supposed to be and I don't know how to discriminate those lines that are not needed.
Any help, hint or suggestion is highly appreciated.
Update to question
I don't know if this is a workaround or a solution to the question, but, it was the only way that I found to get to a solution for the requirement, for that reason I'm posting as an update instead of answering the question for whom it may help.
Reviewed the documentation once again and find out that the method :modelx referred by <%= f.grouped_collection_select(:modelx_id, #brands, :modelx, :brand, :id, :modelox) %> was requesting all the models as noted in apidock, this method was the solution for the problem.
Created a method in the brand model according to the subset depicted above, due to the fact that the grouping was made by brand, here the excerpt
Model for Brand (Excerpt)
...
def modelx_ltvi
Modelx.where("line_id = ? and brand_id =?",$line_id, self.id)
end
Special Note: Due to my inexperience, I was not able to pass the value of the :line_id from the form, so, I put it in a global variable.
Modified the form partial
Excerpt from partial form
...
<% $line_id=#product.line_id %>
...
<%= f.grouped_collection_select(:modelx_id,
#brands, :modelx_ltvi, :brand,
:id, :modelx) %>
<% end %>
And that makes the hamster run.
Filter your models by the brands you just found, something like:
#brands=Brand.where(id: Modelx.select(:brand_id).where(line_id: params[:line_id]))
#modelxs=Modelx.where(line_id: params[:line_id], brand_id: #brands.map(&:id))
#selected_model_names = #modelxs.map(&:name).sort.uniq

How to properly display value in edit form while using bootstrap3_autocomplete_input and association

I am trying to use auto-complete/type-ahead feature provided by following Rails gem https://github.com/maxivak/bootstrap3_autocomplete_input , together with https://github.com/plataformatec/simple_form
Everything works fine in case I choose "new" action for new record in form. I am able to choose value in input field by auto-complete feature. Problem is in case i choose "edit" action to edit already existing record. In that case field does not show correct value (pre-filled by form), but it shows something like: #<Airport:0x007f98b478b7a8>
Even in "show" action, I can see correct value displayed.
I tried to change f.input with f.association as I had it before I started implementing auto-complete, but this did not helped.
Records in Cargo model have correct airports_id reference stored, I checked that manually in rails console.
Question is how can I get correct Airport value pre-filled by form in case I choose "edit" action, instead some kind of reference, I got.
Rails 4.1.7
My code is:
Cargo model:
class Cargo < ActiveRecord::Base
belongs_to :airport
...
Cargo view:
...
<%= f.input :airport, :as => :autocomplete, :source_query => autocomplete_airport_city_airports_url %>
...
Airport model:
class Airport < ActiveRecord::Base
has_many :cargos, :dependent => :destroy
attr_accessible :iata_code, :name, :city
validates :iata_code, :name, :city, presence: true
validates :iata_code, :uniqueness => { :scope => :name }
validates :iata_code, length: { is: 3 }, format: { with: /\A[a-zA-Z\d\s]*\z/ }
validates :name, :city, length: { minimum: 2, maximum: 128 }
def full_airport_name
"#{city} / #{iata_code}"
end
end
Airports controller
class AirportsController < ApplicationController
autocomplete :airport, :city, { :display_value => 'full_airport_name', :full_model=>true }
...
Routes:
resources :airports do
get :autocomplete_airport_city, :on => :collection
end
Actually I found the problem. First of all I refactored Airports model, removed all columns but name, and reseed name column with data concatenated from separate strings IATA code / City. After this, there is need to specify in model, what to show as value. Simply this solved this issue:
class Airport < ActiveRecord::Base
has_many :cargos, :dependent => :destroy
attr_accessible :name
validates :name, presence: true
validates :name, :uniqueness => true
def to_s
name
end
end
This is described, I didnot understand it on first sight previously, in original documentation here https://github.com/maxivak/bootstrap3_autocomplete_input section Model.
User f.association and because rails will automatically look for :nameand you do not have that, you'll have to define it like so:
f.association :airport, label_method: :full_airport_name, value_method: :id........etc

active admin, cannot create nested resource at the same time as the parent

In my app, Invoice has_many Item. So in my active admin UI, I want to be able to create a invoice, and at the same time create its items.
But I can only add items after the invoice is created using the Edit Invoice button in active admin. Trying to create them together will not direct me anywhere from the New Invoice page. And there aren't any errors shown. Could someone help me out on this?
I have the following form structure in my app/admin/invoice.rb
permit_params :paid, :due, :customer_id,
items_attributes: [:price, :description, :invoice_id, :purchased_product_id]
form multipart: true do |f|
f.inputs do
input :customer
input :due
input :paid, as: :radio
end
f.inputs "Items" do
f.has_many :items do |item|
item.input :price
item.input :description
item.input :purchased_product
end
end
f.actions
end
I have added the accepts_nested_attributes_for in my Invoice model as follow:
class Invoice < ActiveRecord::Base
belongs_to :customer
has_many :items
accepts_nested_attributes_for :items, allow_destroy: true
validates :customer, presence: true
I am using Rails 4, and activeadmin '~> 1.0.0.pre1'
The problem is to deal with my validations in my Item model. I had the following validation rule in my Item model class
validates :price, :invoice, presence: true
This says that in order to create an item, it has to have an invoice connected. But since in the creation process of the invoice and its contained items, invoice is yet saved to database. The items can't find an invoice to connect to yet, and the validation failed.
The problem is solved by removing the presence validation of invoice, to
validates :price, presence: true

Resources