Home.where(code: "x123").where.not(state_id: 1)
How can I use a equivalent query but instead of using "where" I want to use find_by.
find_by returns a single record (first matching the specified conditions), so there is no way (and sense) to do what you want.
EDIT
sure! But I want only one object!
The following query would do it then (it will search for a code 123 and any state_id except for 1. And it returns a first record matching this condition):
Home.find_by(code: '123', state_id: State.ids - [1])
If the goal is to return a single object you can simply use first:
Home.where(code: "x123").where.not(state_id: 1).first
If the reason you want to use find_by is because you want missing records to raise ActiveRecord::RecordNotFound then you can use first! instead, which will do exactly that.
Related
I am trying to get an array of names from an activerecord model based on a search query.
I have this method in my item model.
def self.search(search)
if search
where(['lower(name) LIKE ?', "%#{search}%"])
else
Item.all
end
end
I am trying to figure out the difference between using these two lines they are both returning the same thing.
Item.search('ex').select('name').map(&:name) vs
Item.search('ex').map(&:name)
Item.search('ex').select('name').map(&:name)
In the above statement, you are selecting only the name column from the result of Item.search('ex') and then getting the names for all of them using .map(&:name).
But, in the follwing statement:
Item.search('ex').map(&:name)
You are not selecting the name column, just getting the names using .map(&:name) from the result of Item.search('ex').
And Yes, they return the exact same result.
So, if you want the array of names only from the search result, then selecting the name column is redundant. Just go with this:
Item.search('ex').map(&:name)
Or, even better, do it using pluck:
Item.search('ex').pluck(:name)
It bypasses the need for instantiating every ActiveRecord object, and instead just returns the queried values in an Array directly, which improves performance both in terms of execution time and memory consumption.
Basically what .select does is what we call a projection, which is filtering the fields that are returned by the query. However, if you do not call .select at all, Rails default to selecting all the fields from the table.
So the diference between Item.search('ex').select('name') and Item.search('ex') is that the former selects only the column name and the latter selects all the columns from the items table.
Given that, when you map all the items to grab only the name, it doesn't make any diference, since both ways of doing have name selected.
1st Query: Item.search('ex').select('name').map(&:name)
Let's take the entire statement piece by piece.
Item.search('ex') # it trigger one SQL query and return `ActiveRecord::Relation` object in Array
SELECT `items`.* FROM `items` WHERE (lower(name) LIKE '%ex%')
Next
Item.search('ex').select('name')
SELECT `items`.`name` FROM `items` WHERE (lower(name) LIKE '%ex%')
As we might can see it return ActiveRecord::Relation object in Array with selecting name fields.
Now
Item.search('ex').select('name').map(&:name)
SELECT `items`.`name` FROM `items` WHERE (lower(name) LIKE '%ex%')
Further here as you are calling #map method on ActiveRecord::Relation object which itself defined in Enumerable Module.
#map Returns a new array with the results of running block once for
every element in enum.
2nd Query: Item.search('ex').map(&:name)
SELECT `items`.* FROM `items` WHERE (lower(name) LIKE '%ex%')
Here you are calling#map method on ActiveRecord::Relation object. Further you are saying, " Hey ActiveRecord::Relation I need only name field from search object, and ActiveRecord::Relation replay back to give all name in form of array. "
For more information The clever hack that makes items.map(&:name) work.
Hope this help !!!
I'm doing a select with a count-distinct in ActiveRecord, but it's not returning any of my aggregated fields.
User.
select(
'users.id, count(distinct(shc.id)) as shipping_credit_count,
count(distinct(sc.id)) as service_credit_count'
).
...
...
group('users.id')
Is only returning #<ActiveRecord::Relation [#<User id: 119>]> I was expecting to see the count in my aggregated fields? Why is nothing being returned?
Your query probably works as expected but the inspect method is throwing you of. Read my answer here for a better description: Why group calculation fields do not show up in query result?
You should be able to call service_credit_count and service_credit_count on your objects even though it does not show up when you log them.
I would however implement it a little bit different. I would on the User model add the methods
def service_credit_count
return service_credit_count_sql if self.respond_to?(:service_credit_count_sql)
services.count
end
def shipping_credit_count
return shipping_credit_count_sql if self.respond_to?(:shipping_credit_count_sql)
shippings.count
end
And then in your query name the fields with the suffix. This way you can always use these counts. There is also a small (quite imature) gem I have written that does this: https://github.com/trialbee/association_count
This is one example of one entry in my database:
Market id: 1, name: "Independence Park (Independently Run Farmers Market...", address: "3945 N. Springfield Ave., Chicago, IL", zipcode: "60618", created_at: "2013-01-01 21:22:24", updated_at: "2013-01-01 21:22:24"
All I want to do is list the 43 zipcodes from all the entries in my database. Why don't these queries work?
Market.all.each { |m| m.zipcode }
Market.all.zipcode
m = Market.all
m.each{ |m| m.zipcode }
Thanks!
If all you want is an array of zip codes, I would suggest to try this:
Market.pluck(:zipcode)
The other answers have pointed out the correct way to do what you want, but haven't explained why your other attempts didn't work (which is technically the question you asked).
The reason attempts 1 and 3 don't work is that each doesn't return anything, it's only use is to loop through a set of records and perform an action on them (such as updating them or using some data to call an external service). Replacing each with map would fix them, as map uses the return value of the block to return an array of values. This has a disadvantage in that map is an Array method, so all of your records will have to be loaded into memory before the values can be found.
Attempt 2 doesn't work as you're trying to call a field name on an ActiveRecord::Relation (all), so you'll end up raising a NoMethodError.
The neatest solution, and one that has already been pointed out, is to use pluck, which not only returns all the values for the requested fields, but does so at the database query level, so you call should be more efficient.
You could also do the following, it returns an array of zipcodes:
Market.all.map(&:zipcode)
Use Benchmark to determine which is better.
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")
I have a class method on my Consumer model that functions as a scope (acts on an ActiveRecord::Relation, returns an ActiveRecord::Relation, can be daisy-chained) and returns doubles of some consumers. I want it to only return unique consumers, and I can't seem to find either a way to do it with Rails helpers or the right SQL to pass in to get what I want. I feel like there's a simple solution here - it's not a complicated problem.
I essentially want Array#uniq, but for ActiveRecord::Relation. I tried a select DISTINCT statement
Consumer.joins(...).where(...).select('DISTINCT consumers.id')
It returned the correct 'uniqueness on consumers.id' property, but it only returned the consumer ids, so that when I eventually loaded the relation, the Consumers didn't have their other attributes.
Without select DISTINCT:
my_consumer = Consumer.joins(...).where(...).first
my_consumer.status # => "active"
With select DISTINCT:
my_consumer = Consumer.joins(...).where(...).select('DISTINCT consumers.id').first
my_consumer.status # => ActiveModel::MissingAttributeError: missing attribute: status
I didn't think that an ActiveRecord::Relation would ever load less than the whole model, but I guess it will with the select statement. When I query the Relation after the select statement for the class it contains, it returns Consumer, which seems strange.
.select('DISTINCT consumers.*')