Ruby on Rails - PostgreSQL Grouping error when doing joins & group - postgresql

In my application I have models Visit & Post.
class Post < ActiveRecord::Base
has_many :visits
class Visit < ActiveRecord::Base
belongs_to :post, :counter_cache => true
Im trying to get all visits a post has in visits table. I did:
- a = Visit.joins(:post).group(:post_id).select(:post_id, :title, 'count(visits.id) as total_views').where(user: current_user)
- a.each do |a|
%tr
%td= a.title
%td= a.total_views
This works find in my development env/localhost (I think since I use sqlite3), butI am using PostgreSql in my production and I got this error:
PG::GroupingError: ERROR: column "posts.title" must appear in the GROUP BY clause or be used in an aggregate function
LINE 1: ...ECT count(visits.id) as total_views, "visits"."post_id", "title", c...
What am I doing wrong and how to fix it?

As the answer says "posts.title" must appear in the GROUP BY. In group add :title along with :post_id
Visit.joins(:post).group([:post_id, :title]).select('sum(cpc_bid) as earnings', :post_id, :title, 'count(visits.id) as total_views').where(influencer: current_user)

Related

belong_to association and ActiveRecord::InvalidForeignKey in Rails 5

My application has two models: Service and User. A service may have and assigned driver or not. I have implemented this as:
class User < ApplicationRecord
end
and:
class Service < ApplicationRecord
has_and_belongs_to_many :users
belongs_to :driver, class_name: "User", optional: true
end
Note that since a particular service may or may not have a driver, I have marked the association as optional. And I don't have any pointer from the User model to Service.
I have the following migration implementing this association:
class AddOPtionalDriverToService < ActiveRecord::Migration[5.0]
def change
add_reference :services, :driver, references: :users, index: true
add_foreign_key :services, :users, column: :driver_id
end
end
The relevant part of my schema when I run the migration is:
create_table "services", force: :cascade do |t|
t.string "destination"
....
t.text "comments"
t.index ["driver_id"], name: "index_tdy_requests_on_driver_id", using: :btree
end
...
add_foreign_key "tdy_requests", "users", column: "driver_id"
end
My problem is that when I try to create a new service without a driver my params contains a value of "0" for the driver:
Parameters: {"utf8"=>"✓",
"authenticity_token"=>"kVC53huZYZxuF4akSiqkGkSvoo5p2f4dQ==",
"service"=>{
"destination"=>"Some where", ... ,
"driver_id"=>"0",
"comments"=>""},
"commit"=>"Create Service"}
But since driver_id is "0" I get the following exception:
PG::ForeignKeyViolation: ERROR: insert or update on
table "services" violates foreign key constraint
"fk_rails_15497e1c36" DETAIL: Key (driver_id)=(0) is
not present in table "users"
This makes lots of sense but the funny thing is that I have discovered this when I move from SQLite to PostgreSQL because it was working fine with SQLite. At least the application was doing what I wanted to do. I'm using Rail 5.0.2.
I would like to know how can modify my models or migrations to avoid this exception. Any ideas?
Thank you very much in advance.
I'm sorry. I have just realized the solution. Something that I didn't say was that I enter the driver using a select element. Since the driver is optional I was adding and "empty" using:
<option value="0"></option>
changing to:
<option value=""></option>
solves the problem.
I'd prefer to remove the question since doesn't offer any value.

Rails 4 pg_search - multisearchable Issue

I'm using the pg_search gem for Rails (Ruby 2.1.1, Rails 4.1.4) and trying to implement a global multisearch for some of my models but I keep getting
PG::UndefinedFunction: ERROR: operator does not exist: text % unknown
when I run the multisearch command.
Here are my models:
# name :text
class Animal < ActiveRecord::Base
has_one :expression
multisearchable against: :name
end
# type :text
class Expression < ActiveRecord::Base
belongs_to :animal
multisearchable against: :type
end
My search query is then something like Happy Tiger
When I run PgSearch.multisearch("Happy Tiger") though, I get that above error.
I also have an initializer that tells PgSearch to use trigram
Any ideas???
I realized I needed to install trigram in postgres :P

Mongoid: If possible, how to assign a parent to pre-existing child?

In a model definition can you have more than one belongs_to statement? If the answer is no, read no further. I am trying to create three 1-n referenced relationships with mongoid in a sinatra app.
models
class SkillTrack
include Mongoid::Document
belongs_to :student
belongs_to :grading_period
belongs_to :teacher
end
class Student
include Mongoid::Document
field :name
field :nickname
field :dob, type: Date
has_many :skill_tracks
end
class GradingPeriod
include Mongoid::Document
field :school_year
field :period_name
field :signing_date, type: Date
has_many :skill_tracks
end
class Teacher
include Mongoid::Document
field :name
has_many :skill_tracks
end
routes
post "/skill_track/new" do
form = params[:formdata] # using sinatra form helpers gem
student = Student.find("#{formdata["student_mongo_id"]}")
working = (student.skill_tracks.create).id
??? what do I do with working to make it a child of a teacher and of a grading_period?
end
what I have tried
The thing that looked the most promising to me from the mongoid docs was:
band.member_ids = [ id ] #Set the related document ids.
I mucked about in irb and tried lots of variations in my models but I could not set a parent teacher or grading period for the newly created skilltracking object. The student foreign key was set properly on creation.
I am hoping I have a simple syntax ignorance, but I tried so many variations I wonder if I can do this at all.
UPDATE:
I just needed to add working.save to David Troyer answer and boom working.
post "/skill_track/new" do
form = params[:formdata]
student = Student.find("#{formdata["student_mongo_id"]}")
working = student.skill_tracks.create
working.teacher = Teacher.create # or find
working.grading_period = GradingPeriod.create # or find
working.save
end
I believe so. If I understand your question correctly, try using some setters on the child SkillTrack document.
post "/skill_track/new" do
form = params[:formdata]
student = Student.find("#{formdata["student_mongo_id"]}")
working = student.skill_tracks.create
working.teacher = Teacher.create # or find
working.grading_period = GradingPeriod.create # or find
end
Dig a little bit further into the Operations section of the mongoid docs you referenced

RoR/Postgres Query for the first or last child object of all parents

I have seen similar questions, but no answers that match exactly what I need. If it's out there, I haven't seen it. I'm on Rails 3.1, Ruby 1.9.2, deployed on Heroku Cedar stack (so I'm using postgres)
I've got this model:
class User
has_many :orders
end
class Order
belongs_to :user
#scope :last_for_each_user, ???
#scope :first_for_each_user, ???
end
I need scopes (or methods) that return all orders that are all users' first orders, and all orders that are all users' last orders. My first instinct was to use GROUP BY, which worked fine for :last_for_each_user in SQLite3, but Postgres won't let you select columns that aren't in the GROUP BY clause or selected as part of aggregate functions, and I need fully formed orders objects (i.e., all of the columns, orders.*).
Any ideas? I don't want to select the whole table and sort it out in Ruby. That just doesn't feel right, and it's a lot to hold in memory.
So here's what I ended up doing, for future head-scratchers:
scope :last_for_each_user, lambda {
select("orders.*") & Order.select("max(orders.id) as id").group("orders.user_id")
}
scope :first_for_each_user, lambda {
select("orders.*") & Order.select("min(orders.id) as id").group("orders.user_id")
}
If anyone has a better idea, I am very much open to hear it. And thanks to the whole SO community (even though no one responded), this is the first time I've signed up/asked a question but every time I google a problem, I end up here!
You could use the DISTINCT ON() rules to get only the last one. Here's an example following your model structure:
scope :for_users, lambda {|ids, is_first| includes(:user).where("user_id IN (?)", ids).select("DISTINCT ON (users.id) *").order("orders.created_at ?", is_first ? : "ASC" : "DESC") }
scope :last_for_each_users, lambda { |users| for_each_users(users.map{|u| u.id }, false }
scope :first_for_each_users, lambda { |users| for_each_users(users.map{|u| u.id }, true }

Mongo ids leads to scary URLs

This might sound like a trivial question, but it is rather important for consumer facing apps
What is the easiest way and most scalable way to map the scary mongo id onto a id that is friendly?
xx.com/posts/4d371056183b5e09b20001f9
TO
xx.com/posts/a
M
You can create a composite key in mongoid to replace the default id using the key macro:
class Person
include Mongoid::Document
field :first_name
field :last_name
key :first_name, :last_name
end
person = Person.new(:first_name => "Syd", :last_name => "Vicious")
person.id # returns "syd-vicious"
If you don't like this way to do it, check this gem: https://github.com/hakanensari/mongoid-slug
Define a friendly unique field (like a slug) on your collection, index it, on your model, define to_param to return it:
def to_param
slug
end
Then in your finders, find by slug rather than ID:
#post = Post.where(:slug => params[:id].to_s).first
This will let you treat slugs as your effective PK for the purposes of resource interaction, and they're a lot prettier.
Unfortunately, the key macro has been removed from mongo. For custom ids,
users must now override the _id field.
class Band
include Mongoid::Document
field :_id, type: String, default: ->{ name }
end
Here's a great gem that I've been using to successfully answer this problem: Mongoid-Slug
https://github.com/digitalplaywright/mongoid-slug.
It provides a nice interface for adding this feature across multiple models. If you'd rather roll your own, at least check out their implementation for some ideas. If you're going this route, look into the Stringex gem, https://github.com/rsl/stringex, and acts_as_url library within. That will help you get the nice dash-between-url slugs.