Select Rails objects that include value in array attribute - ruby-on-rails

I have a Rails model called Box. Each Box object has a column :products, which is an array of strings that includes all the products that are being stored inside it at the time.
For each Box object, it is possible that the same value was stored in another Box.
Is there a query I can use to return all the Boxes that have value x stored in :products?
I know "where" works for finding objects with certain values, and with an array you might use "include?", but I'm having trouble working out a way to use either in this case, if it's at all possible.

There was an answer posted here before that worked well enough, but I looked around and found another query that was more succinct.
selected_boxes = Box.where("?=ANY(products)", x)
Where x is the value you are seeking in each object.

Scope!
scope :contains, ->(items) { where("products LIKE ?", "%#{items.to_yaml}%") } # items is an array of your potential strings
So you'd call this as Box.contains(%w(foo bar)) or Box.contains(['some thing'])
Passing the array should let you search for multiple items at a time...
You can name the scope anything you want, obviously
LIKE for mySQL... ILIKE for postgreSQL

Related

Rails 4 ActiveRecord .where any element in one array is in another array

I have a list of ids that looks something like this:
feed_ids = [1,2,3,4,5] # will be filled with random ids
I have a Post model that has an attribute parent_ids that might look something like this:
parent_ids = [20,14,1]
I want to retrieve all records where an element in parent_ids matches an element in feed_ids
I tried this but it's not working:
nodes = Post.where(parent_ids: feed_ids)
It's not giving me an error but it's also not returning any records.
The find method can take in an array. You could use array intersection here.
Post.find(parent_ids & feed_ids)
Disclaimer
I do not have Rails installed, so I have to go by my instinct.
Also, this might not be the most efficient solution if you have a large data-set. But with relatively few records, it should be fine.

Get array of attribute values, along with an attribute from a join

I've gotten close, I believe. My current query is this
items = Item.select("items.icon, items.name, item_types.name AS type, items.level, items.rarity, items.vendor_value")
.joins(:item_type)
.where("item_types.name = '#{params[:item_type]}'")
This gets me an array of Item objects that at least respond to :type with the item_type.name.
What I am looking for is an array of arrays that look so:
[icon, name, item_type.name, level, rarity, vendor_value]
I've already had it working fairly easily, but it is important to me that this be done in one fell swoop via sql, instead of creating a map afterwards, because there are times where I need to respond with 40k+ items and need this to be as fast as possible.
Not sure how to go from the above to an array of attributes, without performing a map.
Thanks for your help!
The pluck method does precisely what you want. In your case, it would look like this:
items = Item.joins(:item_type)
.where("item_types.name = ?", params[:item_type])
.pluck("items.icon", "items.name", "item_types.name AS type",
"items.level", "items.rarity", "items.vendor_value")
I also changed the where call to use parameterization instead of string interpolation—interpolation isn't recommended, especially when you're getting a value from the user.
Further reading:
Official documentation for pluck
An in-depth explanation of how to use pluck

Ruby map/collect is slow on a large Mongoid collection

I have a Mongoid collection on which I run a where query.
Now, I would like to build an array containing a values of a specific field from all the documents in the collection.
e.g. if my Monogid model is
class Foo
field :color, type: String
end
I'd like to do something like this -
red_ducks = Foo.where(color: 'red')
red_duck_ids = red_ducks.map(&:_id)
Unfortunately, when the result of the query is large it takes a long time. It takes 6 seconds for 10,000 documents in my case, for example.
Is there any way to speed this up?
Can't you just call distinct on your scope with _id as an attribute?
red_duck_ids = Foo.where(color: 'red').distinct(:_id)
Which will return you a list of all _ids that meet your conditions. You can find more information on Mongo's distinct documentation.
You can also have a look at only and if you are using version 3.1 or newer you can also use Criteria#pluck.
have you tried
Foo.where(color: 'red').pluck(:id)
might be faster (not sure)

List of Model's Field in Rails

I've been looking at the Rails documentation and can't find this.
Lets say I have a Model User with field strength.
How do I get a list of all strengths for every instance of the model object in an array?
list_of_str = [1, 2, 3, 4, etc..]
I know that you can call User.all and iterate through each of their strength fields and append them to a list but is there a more elegant way of doing this?
User.pluck 'strength'
New in rails 3.2 if my memory is correct
You can use
User.uniq.pluck 'strength'
If you need to avoid duplicates, and you can also combine this with scopes and conditions, for example
User.where(...).pluck 'strength'
If you're really after the distinct strengths then:
User.select('distinct strength').pluck(:strength)
will get them. If you don't care about duplicates then Frederick Cheung's plain pluck would be a good idea.
You can use the select method for that, it generates a query where only the values of the desired field are retrieved (which is faster than loading all model data from the database):
User.select(:strength)
This still returns an array of model-objects, so you have to use map to get an array of the values only:
User.select(:strength).map(&:strength)
The creation of the model object will be slower than just querying the database for the values, but using raw SQL should be avoided if performance is not critical in this place.
Edit: There is an even better way (pluck), see the accepted answer.

Ruby on Rails/will_paginate: Ordering by custom columns

I've got a rather complicated SQL-Query whose results should be paginated and displayed to the user. I don't want to go into details here, but to put it simple I just select all columns of a model, and an additional column, that is used just for sorting purposes.
Note.select( ['notes.*', "<rather complicated clause> AS 'x'"] ).joins(...)...
After some .join()'s and a .group(), I finally call .order() and .paginate on the relation. If I order by any of the model's natural columns everything works fine, if I however order by the "artificial" column x, rails gives me the error:
no such column: x
This seems to occur because will_paginate seems to do a COUNT(*)-statement before getting the actual data, simply to get the amounts of data it has to process. This COUNT(*)-statement is (of course) generated from the original statement, which includes the ORDER BY x. The problem now is, that will_paginate keeps the ORDER BY in the statement but simply replaces the column definitions with COUNT(*) and thus cannot find the column x.
Using Rails 3 and will_paginate 3.0.pre2.
Is there any way to get around this?
thx for any help
You can disable the initial count query by passing in the value manually:
total_entries = Note.select(...).joins(...).count
Note.select( ... ).joins(...).group().paginate(:total_entries => total_entries)
The above is not a literal code sample and you may need to tweak your count query to get the correct results for your join. This should let you do a straight up complex query on the pagination.

Resources