Rails select distinct - ruby-on-rails

in a scenario i want to retrieve the records with different values so i used distinct for that,
Book.where(["user_id = ?",#user_id]).select('distinct title_id')
`this, only retrives the records like this [#<Book title_id: 30>, #<Book title_id: 31> ]`
but i want to fetch the id of Book as well along with title_id
so, please advise me how to work on this
thanks

use grouping:
Book.where(:user_id => #user.id).grouped('title_id')
problem is that if you do grouping you can't have different book ids, they are all grouped into single row. You can use GROUP_CONCAT to workaround that:
Book...select('books.*, GROUP_CONCAT(id) as ids')
that way you'll have book ids attribute for every group

Related

Ruby on Rails / ActiveRecord: How Can I (Elegantly) Retrieve Data from Multiple Tables?

It's rather trivial to retrieve data from multiple tables that are related through foreign keys using raw SQL. I can do, for example:
SELECT title, domestic_sales
FROM movies
JOIN boxoffice
ON movies.id = boxoffice.movie_id;
This would give me a table with two colums: title and domestic_sales, where the data in the first column comes from the table movies and the data in the second column comes from the table boxoffice.
How can I do this in Rails using Ruby code? I can, of course, get the same result if I use raw SQL. So, I could do the following:
ActiveRecord::Base.connection.execute(<<-SQL)
SELECT title, domestic_sales
FROM movies
JOIN boxoffice
ON movies.id = boxoffice.movie_id;
SQL
This would give me a PG::Result object with the data I want. But this is super inelegant. I would like to be able to get this information without using raw SQL.
So, this is the first thing that comes to mind is:
Movie.select(:name, :domestic_sales).joins(:box_office)
The problem, however, is that the aforementioned line of code returns a bunch of Movie objects. Since the Movie class doesn't have the domestic_sales attribute, I don't get access to that information.
The next thing I thought was to use a loop. So, I could do something like:
Movie.joins(:box_office).to_a.map do |m|
{name: m.name, rating: m.box_office.domestic_sales}
end
This gives me exactly the data I want. But it costs n + 1 SQL queries, which is not good. I should be able to get this with just one query...
So: How can I retrieve the data I want without using raw SQL and without using loops that cost multiple queries?
SELECT title, domestic_sales
FROM movies
JOIN boxoffice
ON movies.id = boxoffice.movie_id;
translated to ActiveRecord would look like this
Movie
.select(:title, :domestice_sales)
.joins("boxoffice ON movies.id = boxoffice.movie_id")
When you have proper associations defined in your models you would would be able to write:
Movie
.select(:title, :domestice_sales)
.joins(:boxoffices)
And when you do not need an instance of ActiveRecord and would be fine with a nested array, you can even write:
Movie
.joins(:boxoffices)
.pluck(:title, :domestice_sales)
Try this way.
Movie.joins(:box_office).pluck(:title, :domestic_sales)

Neo4j: How to filter out nodes from a list of IDs?

I am trying to "select all posts" and exclude specific nodes that are in an array of IDs (post authors). With SQL you can use NOT IN (1, 2, 3). How can I create a CYPHER query to do this?
Post <-- author -- User
- ID - ID
Mostly the same as SQL ;)
MATCH (author)-[:author]->(post:Post)
WHERE NOT(ID(author) IN {id_list})
RETURN DISTINCT post
Since you tagged the question as Neo4j.rb:
User.as(:author).posts.where('NOT(ID(author) IN ?)', [1,2,3])
In newer versions of Neo4j.rb:
User.as(:author).posts.where_not('ID(author) IN ?', [1,2,3])
You didn't mention what kind of ids, so I default to Neo4j IDs, but keep in mind that those can be recycled so they aren't for long term usage as reference.
EDIT:
Your comment made be realize that perhaps a better way to go about it is:
User.where_not(id: ids).posts
It should translate the id to whatever you use for id_property (uuid by default).

Rails: select unique values from a column

I already have a working solution, but I would really like to know why this doesn't work:
ratings = Model.select(:rating).uniq
ratings.each { |r| puts r.rating }
It selects, but don't print unique values, it prints all values, including the duplicates. And it's in the documentation: http://guides.rubyonrails.org/active_record_querying.html#selecting-specific-fields
Model.select(:rating)
The result of this is a collection of Model objects. Not plain ratings. And from uniq's point of view, they are completely different. You can use this:
Model.select(:rating).map(&:rating).uniq
or this (most efficient):
Model.uniq.pluck(:rating)
Rails 5+
Model.distinct.pluck(:rating)
Update
Apparently, as of rails 5.0.0.1, it works only on "top level" queries, like above. Doesn't work on collection proxies ("has_many" relations, for example).
Address.distinct.pluck(:city) # => ['Moscow']
user.addresses.distinct.pluck(:city) # => ['Moscow', 'Moscow', 'Moscow']
In this case, deduplicate after the query
user.addresses.pluck(:city).uniq # => ['Moscow']
If you're going to use Model.select, then you might as well just use DISTINCT, as it will return only the unique values. This is better because it means it returns less rows and should be slightly faster than returning a number of rows and then telling Rails to pick the unique values.
Model.select('DISTINCT rating')
Of course, this is provided your database understands the DISTINCT keyword, and most should.
This works too.
Model.pluck("DISTINCT rating")
If you want to also select extra fields:
Model.select('DISTINCT ON (models.ratings) models.ratings, models.id').map { |m| [m.id, m.ratings] }
Model.uniq.pluck(:rating)
# SELECT DISTINCT "models"."rating" FROM "models"
This has the advantages of not using sql strings and not instantiating models
Model.select(:rating).uniq
This code works as 'DISTINCT' (not as Array#uniq) since rails 3.2
Model.select(:rating).distinct
Another way to collect uniq columns with sql:
Model.group(:rating).pluck(:rating)
If I am going right to way then :
Current query
Model.select(:rating)
is returning array of object and you have written query
Model.select(:rating).uniq
uniq is applied on array of object and each object have unique id. uniq is performing its job correctly because each object in array is uniq.
There are many way to select distinct rating :
Model.select('distinct rating').map(&:rating)
or
Model.select('distinct rating').collect(&:rating)
or
Model.select(:rating).map(&:rating).uniq
or
Model.select(:name).collect(&:rating).uniq
One more thing, first and second query : find distinct data by SQL query.
These queries will considered "london" and "london " same means it will neglect to space, that's why it will select 'london' one time in your query result.
Third and forth query:
find data by SQL query and for distinct data applied ruby uniq mehtod.
these queries will considered "london" and "london " different, that's why it will select 'london' and 'london ' both in your query result.
please prefer to attached image for more understanding and have a look on "Toured / Awaiting RFP".
If anyone is looking for the same with Mongoid, that is
Model.distinct(:rating)
Some answers don't take into account the OP wants a array of values
Other answers don't work well if your Model has thousands of records
That said, I think a good answer is:
Model.uniq.select(:ratings).map(&:ratings)
=> "SELECT DISTINCT ratings FROM `models` "
Because, first you generate a array of Model (with diminished size because of the select), then you extract the only attribute those selected models have (ratings)
You can use the following Gem: active_record_distinct_on
Model.distinct_on(:rating)
Yields the following query:
SELECT DISTINCT ON ( "models"."rating" ) "models".* FROM "models"
In my scenario, I wanted a list of distinct names after ordering them by their creation date, applying offset and limit. Basically a combination of ORDER BY, DISTINCT ON
All you need to do is put DISTINCT ON inside the pluck method, like follow
Model.order("name, created_at DESC").offset(0).limit(10).pluck("DISTINCT ON (name) name")
This would return back an array of distinct names.
Model.pluck("DISTINCT column_name")

ActiveRecord find and only return selected columns

edit 2
If you stumble across this, check both answers as I'd now use pluck for this
I have a fairly large custom dataset that I'd like to return to be echoe'd out as json. One part is:
l=Location.find(row.id)
tmp[row.id]=l
but I'd like to do something like:
l=Location.find(row.id).select("name, website, city")
tmp[row.id]=l
but this doesn't seem to be working. How would I get this to work?
thx
edit 1
alternatively, is there a way that I can pass an array of only the attributes I want included?
pluck(column_name)
This method is designed to perform select by a single column as direct SQL query Returns Array with values of the specified column name The values has same data type as column.
Examples:
Person.pluck(:id) # SELECT people.id FROM people
Person.uniq.pluck(:role) # SELECT DISTINCT role FROM people
Person.where(:confirmed => true).limit(5).pluck(:id)
see http://api.rubyonrails.org/classes/ActiveRecord/Calculations.html#method-i-pluck
Its introduced rails 3.2 onwards and accepts only single column. In rails 4, it accepts multiple columns
In Rails 2
l = Location.find(:id => id, :select => "name, website, city", :limit => 1)
...or...
l = Location.find_by_sql(:conditions => ["SELECT name, website, city FROM locations WHERE id = ? LIMIT 1", id])
This reference doc gives you the entire list of options you can use with .find, including how to limit by number, id, or any other arbitrary column/constraint.
In Rails 3 w/ActiveRecord Query Interface
l = Location.where(["id = ?", id]).select("name, website, city").first
Ref: Active Record Query Interface
You can also swap the order of these chained calls, doing .select(...).where(...).first - all these calls do is construct the SQL query and then send it off.
My answer comes quite late because I'm a pretty new developer. This is what you can do:
Location.select(:name, :website, :city).find(row.id)
Btw, this is Rails 4

Problem with sorting by row ( special case )

I have a requirement for sorting Contacts records by primary_contact_no.
My Contact fields contain primary_contact_no ,email , mobile_no.
this is no brainier....
BUT my view requires me to show mobile_no under Contact Number(view label) when primary_contact_no is not present.
Contacts.find(:all, :order => "primary_contact_no")
Now When i sort it by primary_contact , in the view , the records where these fields are absent get replaced with mobile_no but since they are already sorted by contact_no they appear at the bottom of the search result.
How can i combine the two results ( in case primary_contact is not present and carry out search on the combined record )
Is there any other solution to the problem where i can combine the row search records or something like that???
P.S.
I have used will paginate.
You could order once you retrieve them from the database.
So
contacts = Contact.all
u.sort!{|a,b| a.con_number<=> b.con_number}
Then in your Contact Model
def con_number
primary_contact_no||mobile_no
end
MySQL and PostgreSQL both have COALESCE function, so you can do something like:
Contacts.find(:all, :order => "COALESCE(primary_contact_no,mobile_no)")
to sort the records as you want. But beware, using sql functions and raw sql has its caveats. If you decide to switch databases, you have to check if each raw sql and sql function you used like this is supported in your new RDBMSI.
I would not sort the records in my application, as that means, I can not use pagination of will paginate to select limited data and have to retrieve full set of records, sort them and then use the relevant records based on pagination parameters. It will increase the response time consistently as the contacts table grows.

Resources