Aggregation on Many to many relations activerecord - ruby-on-rails

I want to make ES aggregation on many to many activerecord model on rails but no way
My model :
class Foo < ApplicationRecord
searchkick
def search_data
{
bar: bar
}
end
has_many :bars
end
I have tried many solution but always get
{"active"=>{"doc_count_error_upper_bound"=>0, "sum_other_doc_count"=>0, "buckets"=>[]}}

The answer is add this :
def search_data
Attributes.merge(
bar_names: bar.map(&:name)
)
end

Here more about how to work with associated models, here is:
Item.rb (model):
# Relations
belongs_to :brand
## many-to-many relations between Items and Textures
has_many :item_attribute_for_textures
has_many :textures, through: :item_attribute_for_textures, :class_name => 'ItemAttribute::Texture'
# Searchkick
searchkick
def search_data
{
full_item_title: full_item_title,
brand: brand.try(:title),
texture: textures.map(&:title)
}
end
ItemController.rb:
def index
args = {}
args[:brand] = params[:brand] if params[:brand].present?
args[:texture] = params[:texture] if params[:texture].present?
query = params[:busca].presence || "*"
#items = Item.search query, where: args,
aggs: {
full_item_title: {},
brand: {},
texture: {}
}
end
and view index.html.erb:
<div class="row">
<div class="col-sm-2" style="font-size: 0.75rem">
<h5>Brands</h5>
<div>
<% #items.aggs["brand"]["buckets"].sort_by{ |b| b["key"] }.each do |bucket| %>
<div>
<% if params[:brand] == bucket["key"].to_s %>
<strong><%= bucket["key"] %></strong>
<% else %>
<%= link_to bucket["key"], request.params.merge(brand: bucket["key"]) %>
<% end %>
(<%= bucket["doc_count"] %>)
</div>
<% end %>
</div>
<hr>
<h5>Texture</h5>
<div>
<% #items.aggs["texture"]["buckets"].sort_by{ |b| b["key"] }.each do |bucket| %>
<div>
<% if params[:texture] == bucket["key"].to_s %>
<strong><%= bucket["key"] %></strong>
<% else %>
<%= link_to bucket["key"], request.params.merge(texture: bucket["key"]) %>
<% end %>
(<%= bucket["doc_count"] %>)
</div>
<% end %>
</div>
<hr>
</div>
</div>

Related

ElasticSearch Range Aggregation no output results with no error

Hello I've got this problem with the rails Elascticsearch range aggregations, it seems right as there's no error output but then again it also doesn't aggregate.
Heres my controller
def results
min_price = params[:min_price] if params[:min_price].present?
max_price = params[:max_price] if params[:max_price].present?
price_ranges = [{to: max_price}, {from: min_price, to: max_price}, {from: min_price}]
#results = Item.search(params[:q], aggs: {item_final_price: {ranges: price_ranges}}, page: params[:page], per_page: 10) if params[:q].present?
end
and my model
class Item < ApplicationRecord
include Elasticsearch::Model
include Elasticsearch::Model::Callbacks
# Item.import
searchkick callbacks: :async, highlight: [:item_name]
def search_data
{
item_name: item_name,
item_details: item_details,
item_final_price: item_final_price,
item_av_rating: item_av_rating
}
end
end
and my views
<%= form_tag results_path, method: :get, enforce_utf8: false, id: "q_filter" do %>
<section class="widget widget-categories">
<%= hidden_field_tag :q, params[:q] %>
<h3 class="widget-title">Price Range</h3>
<div class="form-group">
<label>Price Between</label>
<%= number_field_tag :min_price, params[:min_price], class: "form-control form-control-sm", placeholder: "Min Price" %>
</div>
<div class="form-group">
<label>And</label>
<%= number_field_tag :max_price, params[:max_price], class: "form-control form-control-sm", placeholder: "Max Price" %>
</div>
<%= button_tag(type: "submit", name: nil, class: "btn btn-outline-primary btn-sm btn-block") do %>
Filter Search
<% end %>
</section>
<% #results.each do |item| %>
<%= item.item_name %>
<% end %>
Try to target this in your view
#results.response["aggregations"]["item_final_price"]["buckets"]
If you're running in development, can you throw a binding.pry and see what the result keys are? The data should be there if the query returns results.

Rails 5 - Nested attributes in form, params missing or empty

I am trying to create two associated objects from two different models but I am still getting the same error "param is missing or the value is empty: repost" I have tried to figure out why it fails for days but in vain. Any hint?
I have the Model repost
class Repost < ApplicationRecord
has_many :recurrences, inverse_of: :repost
accepts_nested_attributes_for :recurrences
def self.create_repost (twit_id)
twit = Twit.find_by_id(twit_id)
Repost.find_or_create_by(link: twit.link) do |repost|
repost.twit_id = twit.id
repost.content = twit.content
repost.image_url = twit.image_url
repost.content_url = twit.content_url
repost.click = 0
repost.like = 0
repost.retweet = 0
repost.engagement = 0
repost.number_of_publications = repost.publications.count
repost.likes_sum = 0
repost.retweets_sum = 0
repost.engagement_sum = 0
repost.recurrence = repost.recurrences.recurring
end
end
and the model Recurrence
class Recurrence < ApplicationRecord
serialize :recurring, Hash
belongs_to :repost, inverse_of: :recurrences
def self.set(repost_id, frequency)
repost = Repost.find_by_id(repost_id)
Recurrence.create do |recurrence|
recurrence.repost_id = repost.id
recurrence.recurring = frequency
recurrence.start_time = recurrence.created_at
recurrence.end_time = nil
end
end
def recurring=(value)
if RecurringSelect.is_valid_rule?(value)
super(RecurringSelect.dirty_hash_to_rule(value).to_hash)
else
super(nil)
end
end
def rule
rule = IceCube::Rule.from_hash(self.recurring)
rule
end
def schedule
schedule = IceCube::Schedule.new(self.created_at)
schedule.add_recurrence_rule(rule)
schedule
end
end
here is my Repost Controller
class RepostsController < ApplicationController
before_action :find_repost, only: [:show, :edit, :update, :destroy, :get_likes]
def index
#repost = Repost.new
if params[:filter_by]
#reposts = Repost.filter(params[:filter_by], params[:min], params[:max], params[:start_at], params[:end_at])
else
#reposts = Repost.all.order("created_at DESC")
end
end
def show
end
def new
#repost = Repost.new
end
def create
#repost = Repost.create_repost(repost_params)
redirect_to reposts_path
end
def destroy
#repost.destroy
redirect_to reposts_path
end
private
def find_repost
#repost = Repost.find(params[:id])
end
def repost_params
params.require(:repost).permit(:twit_id, recurrences_attributes: [:recurring])
end
end
and here is my view with the form
<div class="container">
<div class="row">
<div class="col-xs-12 col-sm-10 col-sm-offset-1">
<h1 class="twit-h1">My best Tweets</h1>
<p><%= render "filter_form" %></p>
<p><% #twits.each do |twit| %></p>
<p><%= form_for #repost do |f| %></p>
<%= hidden_field_tag :twit_id , twit.id %>
<div class="twit">
<%= image_tag "#{twit.image_url}", class: "twit-image" %>
<div class="twit-body">
<% if twit.content.present? %>
<h3><%= twit.content %></h3>
<% else %>
<%= "This tweet has no text" %>
<% end %>
<% if twit.content_url.present? %>
<%= link_to "#{twit.content_url}".truncate(40), twit.content_url, class: "twit-link" %>
<% else %>
<%= "This tweet has no link" %>
<% end %>
<%= f.fields_for :recurrences do |r| %>
<h6 style="float: right;"><%= r.select_recurring :recurring %></h6>
<% end %>
<h6>
<i class="fa fa-bullhorn fa" aria-hidden="true"></i> <%= twit.engagement %>
<i class="fa fa-retweet fa" aria-hidden="true"></i> <%= twit.retweet %>
<i class="fa fa-heart fa" aria-hidden="true"></i> <%= twit.like %>
</h6>
<p>Tweeted on <%= (twit.first_date).strftime("%A, %B %d, %Y at %I:%M%p %Z") %></p>
<%= link_to "Delete", twit_path(twit), name: nil, class: "btn btn-danger btn-sm", method: :delete, data: {:confirm => 'Are you sure?'} %>
<%= f.submit "Add to your Reposts library", class: "btn btn-primary btn-sm" %>
<% end %>
</div>
</div>
<% end %>
</div>
</div>
</div>
Update, here is my logs
Started POST "/__better_errors/923b59f8da1318ef/variables" for ::1 at 2017-02-21 15:52:56 +0100
Started POST "/reposts" for ::1 at 2017-02-21 15:54:14 +0100
Processing by RepostsController#create as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"62pE3le5m99HF10RgQcd70wFHqWQWiRmQQO2ndNHT3gnceRu94Y1ttyBuSExBjr4/cxeVUtgY60GRThxdpFGJg==", "twit_id"=>"1844", "recurring"=>"{\"interval\":1,\"until\":null,\"count\":null,\"validations\":{\"day\":[1],\"hour_of_day\":1,\"minute_of_hour\":0},\"rule_type\":\"IceCube::WeeklyRule\",\"week_start\":0}", "commit"=>"Add to your Reposts library"}
Completed 400 Bad Request in 2ms (ActiveRecord: 0.0ms)
ActionController::ParameterMissing - param is missing or the value is empty: repost:
It seems my error comes from this private method in my Repost controller but I can't understand why?
def repost_params
params.require(:repost).permit(:twit_id, recurrences_attributes: [:recurring])
end
Thank you!

How to filter out certain objects in a table with certain ids? (Rails 4, Postgresql)

before I ask my question, I will provide some context.
I have 3 Models which interact with each other.
Scoreboard has many Teams. Team belongs to Scoreboard.
The following use a through table.
Scoreboard many-to-many with User.(let's call this Scoreboard.followers)
Team many-to-many with User.(Let's call this Team.members)
I have a Team_Members Controllers with the following method:
Team_member#new Method:
def new
#selected = true
#scoreboard = Scoreboard.find(params[:scoreboard_id])
#team = Team.find(params[:team_id])
#team_members = #team.members
#followers = #scoreboard.followers.search(params[:search]).paginate(page: params[:page], per_page: 50)
end
What I'm attempting to do is display all the Scoreboard followers in my new.html.erb view. I can search through them and add members to a Team that is associated to the same Scoreboard.
Here's is the new.html.erb view:
<%= form_tag(new_scoreboard_team_team_member_path, :method => "get", id: "member-search-form", autocomplete: "off") do %>
<div class="row new-member-field">
<div class="col-xs-12 col-sm-6">
<%= text_field_tag :search, params[:search], placeholder: "Filter Members by Name", class:"form-control" %>
</div>
</div>
<% end %>
<% if #followers.any? %>
<% #followers.each do |follower| %>
<div class="row member-follower-div" id="follower_<%=follower.id%>">
<div class="col-xs-10 col-sm-5 member-prof-link">
<%= link_to follower.name, user_path(follower.id) %>
</div>
<div class="col-xs-2 col-sm-1 member-add">
<%= link_to (add_scoreboard_team_team_member_path(#scoreboard,#team, follower)), method: :post, remote: true, :data => {:disable_with => ".."} do %>
<i class="fa fa-plus-square fa-lg" aria-hidden="true"></i>
<% end %>
</div>
</div>
<% end %>
<div class="row">
<div class="col-xs-12 col-sm-6 new-member-pages">
<%= will_paginate #followers %>
</div>
</div>
<% end %>
Okay now for the question. The problem arises in the controller method. The #team.members and #scoreboard.followers both source objects from the Users table. How would I filter out the User objects from the #scoreboard.followers that exist in the #team.members? I don't want to display #followers on my view that are a part of the #team.members collection. Also, as a bonus, I would like to alphabetically order the followers by user.name.
I tried the .where("users.id NOT IN (?)", #team_members.pluck(:id)) but if #team_members collection is null, no results are displayed on the page.
I solved it like so:
def new
#selected = true
#scoreboard = Scoreboard.find(params[:scoreboard_id])
#team = Team.find(params[:team_id])
#team_members = #team.members
if (#team_members.any?)
#wonderful_humans = #scoreboard.favourited_by.where("users.id NOT IN (?)", #team_members.pluck(:id)).search(params[:search]).sort_by{ |a| a.name.downcase }
else
#wonderful_humans = #scoreboard.favourited_by.search(params[:search]).sort_by{ |a| a.name.downcase }
end
#followers = #wonderful_humans.paginate(page: params[:page], per_page: 50)
end

How to Change Font Color based on Conditional?

When a User checks off :good in the _form
<%= f.text_field :result_value %>
<%= f.date_select :date_value %>
<%= f.check_box :good %>
how can we render the font color in the index of that result (:result_value & :date_value) to green?
result.good below pulls up true or false in the index. I'll take that out once we represent true or false with colors: true = green, false = red (default color).
<% averaged.results.each do |result| %>
<li>
<%= result.result_value %>
<%= result.date_value.strftime("%b %Y") %>
<%= result.good %>
</li>
<% end %>
.
class Result < ActiveRecord::Base
belongs_to :user
belongs_to :quantified
has_many :comments, as: :commentable
default_scope { order('date_value DESC') }
scope :good, -> { where(good: true) }
scope :good_count, -> { good.count }
end
Thank you so much for your help.
You could add a class to the LI based on the output?
<% if result.good == true %>
<li class="green">
<% else %>
<li>
<% end %>
Then in your css just set a colour for that class?
.green {
color: green;
}

Setting up pagination with will_paginate properly

I have been trying to setup pagination properly but I am having trouble following any tutorial and getting it to work properly.
What I basically want setup is that when a user chooses the number of companies they want to appear on a page, the following loop with paginate the given number of companies on each page. If the user doesn't choose a specific number of companies/page, there would be a default value that would be set.
If anyone could maybe give me a push in the right direction, that would be much appreciated.
View
<ul class="list-group">
<%= #comps.find_each do |comp| %>
<li class="list-group-item">
<div class="row">
<div class="span12">
<p style="font-size: 1.5em"><strong><%= link_to comp.name, comp %></strong></p>
<hr class="divider">
<div class="row">
<div class="span6">
<p><strong>Primary Field:</strong> <%= comp.primary_field %></p>
<p><strong>Description:</strong> <%= truncate(comp.description, length: 150) { link_to "Read More", comp, :target => "_blank"}%>
</p>
</div>
<div class="span5">
<% if signed_in? %>
<p><% if !(current_user.favorites.include? comp) %>
<%= link_to 'Favorite',
favorite_company_path(comp, type: "favorite"),
class: "btn btn-primary",
method: :put%>
<% else %>
<%= link_to 'Unfavorite',
favorite_company_path(comp, type: "unfavorite"),
class: "btn btn-danger",
method: :put%>
<% end %></p>
<% end %>
<p><strong>Website:</strong> <%= auto_link(comp.website, :html => { :target => '_blank' })%></p>
<p><strong>Location:</strong> <%= comp.address %></p>
</div>
</div>
</div>
</div>
</li>
<% end %>
<%= will_paginate #comps%>
</ul>
Controller (Where the list page is handled)
def filter
if ((params[:field] != "") and (params[:field] != "All Fields") and (params[:field] != nil))
# This will apply the filter
#comps = Company.where(primary_field: params[:field])
else
# This will return all records
#comps = Company.all
end
end
def list
#fields = ##primary_field
filter
#comps = #comps.paginate(:page => params[:page], :per_page => params[:number_of_companies] ||= 5)
end
Model for the list page
class DynamicPages < ActiveRecord::Base
self.per_page = 5
end
Since you are using will_paginate. You could do in this way.
Post.paginate(:page => params[:page], :per_page => params[:number_of_companies] ||= 10)
Here params[:number_of_companies] is the number of companies a user has selected and 10 is the
default page value.
class Post
self.per_page = 10
end
Also, your code snippet <%= #comps.find_each do |comp| %>, should be this <% %> instead of <%= %>

Resources