Search always returns every record in the table - sunspot-rails

I created a simple full text search rails app using the sunspot_rails and sunspot_solr gems. The search always returns every record from the database regardless of the search term.
class FooController < ApplicationController
def search
#foos = Foo.search do
fulltext params[:query]
end.result
respond_to do |format|
format.html { render :action => "index" }
format.xml { render :xml => #foos }
end
end
end
class Foo < ApplicationRecord
searchable do
text :name
end
end
resources :foos do
collection do
get :search
end
end
gem 'rails', '~> 5.1.0'
gem 'sunspot_rails','~> 2.5.0'
gem 'sunspot_solr','~> 2.5.0'
I run bundle exec rake sunspot:reindex before going to the index page, type any search term and the controller returns every record in the foos table regardless of the search term I give it.

I looked at the docs and couldn't find a .result method but there is a .results which the docs say return an array with the first 30 results by default. Maybe this is your issue?

Related

rendering json gets error no template found for controller rendering head no_content 204

I have a controller that render a json with data from models. When i enter route to get it it do nothing and just show and error in console.
Controller:
class Api::ForecastController < ApplicationController
before_action :set_hourly_forecast, only: %i[ show edit update destroy ]
def index
respond_to do |format|
format.html do
#hourly_forecasts = HourlyForecast.where(forecast_location_id: params[:forecast_location_id]).paginate(:page => params[:page], :per_page=>24) if params[:forecast_location_id].present?
end
format.json do
weather_service = WeatherService.where(name: params[:name])
#forecast_location = ForecastLocation.where(weather_service_id: weather_service)#& municipality: #municipality.name)
#hourly_forecasts = HourlyForecast.where(forecast_location_id: forecast_location.id ).paginate(:page => params[:page], :per_page=>24) if params[:forecast_location_id].present?
end
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_hourly_forecast
#hourly_forecast = HourlyForecast.find(params[:id])
end
# Only allow a list of trusted parameters through.
def hourly_forecast_params
params.require(:hourly_forecast).permit(:forecast_location_id, :date, :temperature, :humidity, :rain, :rain_probability, :wind_speed, :wind_direction)
end
end
Error:
> Started GET "/api/forecast.json?name=dark_sky" for 127.0.0.1 at 2022-04-20 18:33:29 +0200
Processing by Api::ForecastController#index as JSON
Parameters: {"name"=>"dark_sky"}
No template found for Api::ForecastController#index, rendering head :no_content
Completed 204 No Content in 53ms (ActiveRecord: 6.2ms | Allocations: 4983)
The route i use its
127.0.0.1:3000/api/forecast.json?name=dark_sky
So the output should be a json with all hourly model.
But it just do nothing and on console it does get and the select but jumps this error of template, i dont understand it since im new on rails.
If need more controllers, models or anything ask in comments.
You have to have a separate template file to render json index.json.jbuilder for example.
# app/views/api/forecasts/index.json.jbuilder
json.array! #hourly_forecasts do |hourly_forecast|
json.extract! hourly_forecast, :id, :forecast_location_id, :date, :temperature, :humidity, :rain, :rain_probability, :wind_speed, :wind_direction
json.url api_forecast_url(hourly_forecast, format: :json)
end
https://github.com/rails/jbuilder
If you don't need to customize rendered json too much, render json inline in the controller
format.json do
weather_service = WeatherService.where(name: params[:name])
#forecast_location = ForecastLocation.where(weather_service_id: weather_service)#& municipality: #municipality.name)
#hourly_forecasts = HourlyForecast.where(forecast_location_id: forecast_location.id ).paginate(:page => params[:page], :per_page=>24) if params[:forecast_location_id].present?
render json: #hourly_forecasts
end
https://guides.rubyonrails.org/layouts_and_rendering.html#rendering-json
https://api.rubyonrails.org/classes/ActiveModel/Serializers/JSON.html

acts_as_api and bullet N+1 queries

I am using acts_as_api to provide a JSON response for some models in my system. My API related code is (reduced to make the example easier):
# File app/modes/item.rb
# API
acts_as_api
api_accessible :v1_list do |template|
template.add :id
template.add :client_name
end
This API is working as expected. It is important to know that client_name is a method containing:
def client_name
client.name
end
That is, the client name is not included in the item model but in the client model. Thus, this info is not contained in the items table.
Using Bullet gem I have noticed that a N+1 query is being performed in the clients table. For each item, a SQL query to the clients table is also performed.
I know that ActiveRecord has some utilities in the API that avoids N+1 queries, and I would like to know if there is a way to use that ActiveRecord feature together with acts_as_api gem.
The gem documentation shows this
def index
#users = User.all
#Note that it’s wise to add a root param when rendering lists.
respond_to do |format|
format.xml { render_for_api :name_only, :xml => #users, :root => :users }
format.json { render_for_api :name_only, :json => #users, :root => :users }
end
end
So for your case you should simply eager load the client association
def index
#items = Item.includes(:client).all
# Note that it’s wise to add a root param when rendering lists.
respond_to do |format|
format.xml { render_for_api :name_only, :xml => #items, :root => :items }
format.json { render_for_api :name_only, :json => #items, :root => :items }
end
end

RoR nested attributes produces duplicates when edit

I'm trying to follow Ryan Bates RailsCast #196: Nested model form part 1. There're two apparent differences to Ryans version: 1) I'm using built-in scaffolding and not nifty as he's using, and 2) I'm running rails 4 (I don't really know what version Ryans using in his cast, but it's not 4).
So here's what I did
rails new survey2
cd survey2
bundle install
rails generate scaffold survey name:string
rake db:migrate
rails generate model question survey_id:integer content:text
rake db:migrate
Then I added the associations to the models like so
class Question < ActiveRecord::Base
belongs_to :survey
end
and so
class Survey < ActiveRecord::Base
has_many :questions
accepts_nested_attributes_for :questions
end
Then I added the nested view part
<%= form_for(#survey) do |f| %>
<!-- Standard rails 4 view stuff -->
<div class="field">
<%= f.label :name %><br>
<%= f.text_field :name %>
</div>
<div class="field">
<%= f.fields_for :questions do |builder| %>
<div>
<%= builder.label :content, "Question" %><br/>
<%= builder.text_area :content, :rows => 3 %>
</div>
<% end %>
</div>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
and finally the controller so that 3 questions are created whenever a new survey is instantiated
class SurveysController < ApplicationController
before_action :set_survey, only: [:show, :edit, :update, :destroy]
# Standard rails 4 index and show
# GET /surveys/new
def new
#survey = Survey.new
3.times { #survey.questions.build }
Rails.logger.debug("New method executed")
end
# GET /surveys/1/edit
def edit
end
# Standard rails 4 create
# PATCH/PUT /surveys/1
# PATCH/PUT /surveys/1.json
def update
respond_to do |format|
if #survey.update(survey_params)
format.html { redirect_to #survey, notice: 'Survey was successfully updated.' }
format.json { head :no_content }
else
format.html { render action: 'edit' }
format.json { render json: #survey.errors, status: :unprocessable_entity }
end
end
end
# Standard rails 4 destroy
private
# Use callbacks to share common setup or constraints between actions.
def set_survey
#survey = Survey.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def survey_params
params.require(:survey).permit(:name, questions_attributes: [:content])
end
end
So, creating a new survey with three questions is fine. However, if I try to edit one of the surveys, the original three questions are maintained, while an additional three more are created. So instead of having 3 questions for the edited survey, I now have 6. I added
Rails.logger.debug("New method executed")
to the new method in the controller, and as far as I can tell, it is not executed when I'm doing an edit operation. Can anyone tell me what I'm doing wrong?
Any help is greatly appreciated!
I had to add :id to the permitted params in the survey_params method. It now looks like this:
# Never trust parameters from the scary internet, only allow the white list through.
def survey_params
params.require(:survey).permit(:name, questions_attributes: [:id, :content])
end
which works perfectly. I guess new id's were generated instead of being passed to the update action.
Using cocoon gem on Rails 4, I was still getting duplicate fields even after adding :id to the permitted list when editing. Noticed the following as well
Unpermitted parameters: _destroy
Unpermitted parameters: _destroy
So I added the :_destroy field to the permitted model_attributes: field and things worked smoothly after that.
For example...
def survey_params
params.require(:survey).permit(:name, questions_attributes: [:id, :content, :_destroy])
end

Ruby on Rails joins models for json output

I'm trying to build a JSON API end point with Ruby on Rails.
I followed the instruction in the following and was able to create JSON API for my models
http://railscasts.com/episodes/350-rest-api-versioning?view=comments/
I have the following controller:
/api/v1/movies_controller.rb
class MoviesController < ApplicationController
def index
if params[:p].nil?
p = 1
else
p = params[:p].to_i
end
#movies = Movie.order("id DESC").page(p)
end
def show
#movie = Movie.find(params[:id])
end
end
I need to join this with the Genre object where Movie has_many :genres, and Genre belongs_to :movie
However, I'm not able to use the following to get the genres joined with the movie object for the JSON output:
#movie = Movie.find(params[:id], :joins => :genres)
I did notice that the following command is able to generate the joined output in ruby console
#movie.to_json(:include=>:genres)
But then adding this in the controller doesn't show the additional genres fields
Can someone help me please?
Thanks!
My advise: Go with active_model_serializers
Add the following line to your Gemfile
gem 'active_model_serializers'
Do a bundle install afterwards.
After that do a
rails g serializer movie
rails g serializer genre
Than customize the following code:
/api/v1/movies_controller.rb
class MoviesController < ApplicationController
respond_to :json
def index
if params[:p].nil?
p = 1
else
p = params[:p].to_i
end
#movies = Movie.order("id DESC").page(p)
respond_with #movies
end
def show
#movie = Movie.find(params[:id])
respond_with #movie
end
end
app/serializers/movie_serializer.rb
class MovieSerializer < ActiveModel::Serializer
embed :ids, :include => true
attributes :id, :name
has_many :genres
end
app/serializers/genre_serializer.rb
class GenreSerializer < ActiveModel::Serializer
embed :ids, :include => true
attributes :id, :name
end
Have a look at the full documentation of active_model_serializers at
https://github.com/rails-api/active_model_serializers

How can I convert MySQL latitude / longitude data to JSON using Ruby on Rails

Thanks in advance for any help you can provide on this question: How can I use Ruby on Rails to create a JSON object from the "latitude" and "longitude" data in my MySQL table, "Locations"?
My ultimate goal is to create a Google Map with multiple markers pulled from a MySQL database. I know that I can do this through PHP and the Google tutorial (https://developers.google.com/maps/articles/phpsqlsearch_v3), but I'm not strong in PHP and am looking for a shorter way to do it through Rails.
I have tried this other tutorial (http://mokisystemsblog.blogspot.com/2013/04/add-markers-to-google-map-with-ruby-on.html), and here is my code for my controller:
class MapallController < ApplicationController
# GET /mapall
# GET /mapall.xml
# GET /mapall.js
def index
respond_to do |format|
format.html do
#locations = Location.find(:all)
end
format.xml { render :xml => #locations }
format.js do
ne = params[:ne].split(',').collect{|e|e.to_f}
sw = params[:sw].split(',').collect{|e|e.to_f}
#locations = Location.find(:all, :limit => 100, :bounds => [sw, ne])
render :json => #locations.to_json
end
end
end
end
However, when I visit www.example.com/mapall.js, I get an error code. I expect that this link would give me the complete set of results from my database.
Again, I appreciate your advice and patience as I learn this process!
EDIT 1 - ERROR CODE
Below is the log for what happens when I visit example.com/mapall, example.com/mapall.js, and example.com/mapall.xml. When I visit example.com/mapall, I expect a Google Map that renders all of my locations from the MySQL database. Barring that, I expected to see the lat / long data when I visited mapall.js. From the log below, I'm guessing the reason that I'm getting a 404 is that there is no route to mapall.js in my routes file. Is the solution to create a route to the routes file, and if so, how should that read?
Thanks again for your help!
Processing MapallController#index (for IP at DATE TIME) [GET]
Rendering template within layouts/mapall
Rendering mapall/index
Completed in 779ms (View: 7, DB: 100) | 200 OK [http://example.com/mapall]
Processing ApplicationController#index (for IP at DATE TIME) [GET]
ActionController::RoutingError (No route matches "/mapall.js" with {:method=>:get}):
/phusion_passenger ERROR INFO
Rendering /home/example/web/current/public/404.html (404 Not Found)
Processing ApplicationController#index (for IP at DATE TIME) [GET]
ActionController::RoutingError (No route matches "/mapall.xml" with {:method=>:get}):
/phusion_passenger ERROR INFO
Rendering /home/example/web/current/public/404.html (404 Not Found)
EDIT 2 - New Controller
Thanks to #kyllo's feedback, I've been able to update the controller so that I can get ALL of my location data to appear at http://example.com/mapall.js. Only one step left: getting just the nickname, latitude, and longitude fields to appear. The below shows ALL of the data. How should I change this to show only the nickname, latitude, and longitudinal fields?
class MapallController < ApplicationController
# GET /mapall
# GET /mapall.xml
# GET /mapall.js
def index
respond_to do |format|
#masterlocation = Masterlocation.find(:all)
format.html do
end
format.xml { render :xml => #masterlocation }
format.js do
#masterlocation = Masterlocation.find(:all)
render :json => #masterlocation.to_json
end
end
end
end
I see a few problems here.
Yes, you are getting a "no route matches" error so you need a route in routes.rb similar to this, so that you can access :format as a parameter to respond to:
match 'mapall.:format'
Then in your controller, you have the following:
def index
respond_to do |format|
format.html do
#locations = Location.find(:all)
end
format.xml { render :xml => #locations }
format.js do
ne = params[:ne].split(',').collect{|e|e.to_f}
sw = params[:sw].split(',').collect{|e|e.to_f}
#locations = Location.find(:all, :limit => 100, :bounds => [sw, ne])
render :json => #locations.to_json
end
end
end
Your #locations = Location.find(:all) needs to be outside of the format.html do block, otherwise #locations is undefined inside of your format.xml block and you will get an error.
format.js do will allow your user to access the JSON API by visiting http:/www.example.com/mapall.js. If you use format.json do then they can access it at http:/www.example.com/mapall.json. The choice is yours.
Hope that helps to put you on the right track!