Query has_many relationship, contains this and also this - mysql

# engine.rb
has_many :pistons
#piston.rb
belongs_to :engine
Piston has a column, piston_count and, of course, engine_id
My database has the following 7 records
Engine.all
#=> [#<Engine id: 1>, #<Engine id: 2>, #<Engine id: 3>]
Piston.all
#=> [#<Piston id: 1, engine_id: 1, piston_count: 1>, #<Piston id: 2, engine_id: 1, piston_count: 2>, #<Piston id: 2, engine_id: 2, piston_count: 1>, #<Piston id: 2, engine_id: 3, piston_count: 2>]
I want to write a query that says, return the Engine containing Pistons with a piston_count of 1 and also contains a piston_count of 2
I've tried...
engines = Engine.joins(:pistons).merge(Piston.where(piston_count: 1))
#=> [#, #]
engines.joins(:pistons).merge(Piston.where(piston_count:2))
#=> []
It returns an empty array because active record turns that into one AND clause. However, if I do an OR statement, it will return too many records. Any thoughts?

Figured it out. This takes the intersect of both Active Record Queries.
engine_ids = Engine.joins(:pistons).merge(Piston.where(piston_count: 1)).pluck(:id) & Engine.joins(:pistons).merge(Piston.where(piston_count: 2)).pluck(:id)
Then go back and retrieve all the intersects.
Engine.where(id: engine_ids)

Related

MySQL merge json field with new data while removing duplicates, where the json values are simple scalar values

Suppose that I have a MySQL table with a JSON field that contains only numbers, like this (note: using MySQL 8):
CREATE TABLE my_table (
id int,
some_field json
);
Sample data:
id: 1
some_field: [1, 2, 3, 4, 5]
id: 2
some_field: [3, 6, 7]
id: 3
some_field: null
I would like to merge another array of data with the existing values of some_field, while removing duplicates. I was hoping that this might work, but it didn't:
update my_table set some_field = JSON_MERGE([1, 2, 3], some_field)
The result of this would be:
id: 1
some_field: [1, 2, 3, 4, 5]
id: 2
some_field: [1, 2, 3, 6, 7]
id: 3
some_field: [1, 2, 3]
Considering you have 3 records in your table and you want to merge 1 and 2 as mentioned in your example.
I hope JavaScript is suitable to follow through for you.
// Get both the records
const records = db.execute(“SELECT id, some_field FROM my_table WHERE id=1 OR id=2”);
// You get both the rows.
// Merging row1, you can either use the Set data structure if you’re dealing with numbers like your example, or you could loop using a map and use the spread operator if using JSON. Since your object is an array, I’ll just be explaining to merge 2 arrays.
records[0].some_field = Array.from(new Set(records[0].some_field + record[1].some_field))
// Same for second record.
records[1].some_field = Array.from(new Set(records[0].some_field + record[1].some_field))
// Now update both the records in the database one by one.

Clojure HoneySQL - How to aggregate a string value after a join into a single row?

I'm performing the following query that joins across 3 tables to pull back workouts and the tags associated with them.
(db/query {:select [:workouts.id :workouts.name :tag.tag_name]
:from [:workouts]
:left-join [[:workout_tags :workout_tag] [:= :workout_tag.workout_id :workouts.id]
[:tags :tag] [:= :tag.tag_id :workout_tag.tag_id]]
:where [:= :workouts.id 1]}))
This returns the following:
({:id 1, :name "Short", :tag_name "cardio"}
{:id 1, :name "Short", :tag_name "No weights"})
Ideally, I'd like to return to the end user a single result with the tag_name being combined into a single field. Something like:
{:id 1, :name "Short", :tag_name ["cardio" "No weights"]}
It would see that I could fairly easily do this after the fact, but I wanted to see if there's a built in MySQL function that can do what I'm looking to accomplish. It seems maybe GROUP_CONACT might do what I'm looking for, but I can't seem to get it to work in the context of HoneySQL.
The following should be close to what you need:
(require '[honeysql.core :as hc])
(hc/format {:select [:workouts.id :workouts.name [(hc/call :group_concat :tag.tag_name) :tag_name]]
:from [:workouts]
:left-join [[:workout_tags :workout_tag] [:= :workout_tag.workout_id :workouts.id]
[:tags :tag] [:= :tag.tag_id :workout_tag.tag_id]]
:where [:= :workouts.id 1]
:group-by [:tag.tag_name]})
That produces the following SQL:
SELECT workouts.id, workouts.name, group_concat(tag.tag_name) AS tag_name
FROM workouts
LEFT JOIN workout_tags workout_tag ON workout_tag.workout_id = workouts.id
LEFT JOIN tags tag ON tag.tag_id = workout_tag.tag_id
WHERE workouts.id = ?
GROUP BY tag.tag_name
You didn't show what you had tried or what failed, which would definitely be helpful for us in ascertaining what we should suggest as an answer.
I would do it after the fact:
(let [data [{:id 1, :name "Short", :tag_name "cardio"}
{:id 1, :name "Short", :tag_name "No weights"}
{:id 2, :name "Long", :tag_name "Tall"}
{:id 2, :name "Long", :tag_name "Hills"}]
grouped (group-by :id data)
id-tags (vec (for [[id data-maps] grouped]
(let [tags (mapv :tag_name data-maps)]
{:id id :tags tags})))]
(is= id-tags
[{:id 1, :tags ["cardio" "No weights"]}
{:id 2, :tags ["Tall" "Hills"]}]))
The intermediate result grouped looks like
grouped =>
{1
[{:id 1, :name "Short", :tag_name "cardio"}
{:id 1, :name "Short", :tag_name "No weights"}],
2
[{:id 2, :name "Long", :tag_name "Tall"}
{:id 2, :name "Long", :tag_name "Hills"}]}
See my favorite template project for full config details.

Django. How i can get list of Users with a duplicate?

i have array like this: [1, 1, 1, 2, 3]. How i can get users with a duplicate? For example this query return list without duplicate
list= User.objects.filter(id__in=[1, 1, 1, 2, 3])
for example it will be users with id's:
1,
2,
3
but i need list of users like this:
1,
1,
1,
2,
3
list = []
for x in [1, 1, 1, 2, 3]:
list.append(User.objects.filter(id=x)
It this what you mean? I don't quite understand the spacing.
Get your queryset sorted in the right order. .order_by('id) for ascending by id (which may be the default anyway). Then iterate over the queryset with code to make extra operations with the same object (or a copy thereof) as dictated by the list of IDs.
idlist = [1, 1, 1, 2, 3]
queryset = User.objects.filter(id__in = idlist ).order_by('id')
for object in queryset:
for _ in range( idlist.count( object.id))
do_something_with( object)
Note, this is only one DB call (one queryset), unlike the accepted answer which does one DB query for each element in the id list. Not good.

Get grouped values by multiple ids in ActiveRecord

Sorry for bad English ))
I have an array of ids in my ruby code.
Example:
[[10], [], [10, 1, 3], []]
Can I load User model from MySQL table users in one query by saving grouping?
Example:
[[#<User id=10>], [], [#<User id=10>, #<User id=1>, #<User id=3>], []]
Environment: Ruby 2.5.1 | Rails 5 | MySQL
One of found solution:
I can flat my array of ids and load my model by that array into hash:
hash = User.where(id: array.flatten).index_by(&:id)
Then, during iterating, through array I can load my objects from hash in the right order like that:
array.each do |ids|
users = ids.map { |id| hash[id] }
# do smth
end
This is simple: use flatten method for array:
ids = [[123], [], [123, 1, 3, 4], [70, 80]]
user_ids = ids.flatten.reject(&:blank?).uniq
users = User.where(id: user_ids)
edited:
not optimal (recursive) method for your need:
def map_users_by_id(ids_array:, users:)
result = []
ids_array.each do |array_element|
if (array_element).is_a? Array
result << map_users_by_id(ids_array: array_element, users: users)
else
result << users[array_element]
end
end
return result
end
ids = [[123], [], [123, 1, 3, 4], [70, 80]]
user_ids = ids.flatten.reject(&:blank?).uniq
users = Hash[User.where(id: user_ids).map{|user|[user.id, user]}]
result = map_users_by_id(ids_array: ids, users: users)

How to "push ahead" records with a given value?

I am using Ruby on Rails 3.2.9 and MySQL. I have an Article model with a user_id attribute (this attribute represents the foreign key - id - for associated author users) and I would like to retrieve articles ordering those "for" a given author. That is, given I have following records:
<#Article id: 1, title: "Title 1", user_id: 1>
<#Article id: 2, title: "Title 2", user_id: 2>
<#Article id: 3, title: "Title 3", user_id: 1>
<#Article id: 4, title: "Title 4", user_id: 3>
<#Article id: 5, title: "Title 5", user_id: 1>
...
<#Article id: N, title: "Title N", user_id: M>
When I look for articles ordered "for" the author with id 1 (user_id = 1) then the returning articles should be ordered as-like the following:
<#Article id: 1, title: "Title 1", user_id: 1>
<#Article id: 3, title: "Title 3", user_id: 1>
<#Article id: 5, title: "Title 5", user_id: 1>
<#Article id: 2, title: "Title 2", user_id: 2>
<#Article id: 4, title: "Title 4", user_id: 3>
...
<#Article id: N, title: "Title N", user_id: M>
In other words, I am looking to retrieve all articles but making those ordered with this "priority": articles created by the given author returned first and then all other articles (that is, I would like to "push ahead" articles created by a given author).
How can I make that?
Note: I am looking for a Ruby on Rails implementation, maybe through the order method.
Your example is not that clear since you are searching for user_id 1 and a normal ordering by user_id would put those first anyway. I believe you mean to do something like:
SELECT id, title, user_id
FROM myTable
ORDER BY CASE WHEN user_id = #search_id THEN 1 ELSE 2 END, user_id
In your example above, #search_id should be 1.
Try:
Article.where(user_id: 1).order("user_id ASC").order("id ASC")
Or this:
Article.where(user_id: 1).order("user_id ASC, id ASC")
Try this ::
Select
*
from
myTable
order by
user_id, id
For a given user_id:
Select
*
from
myTable
where user_id='?'
order by
user_id, id
The query in Rails could be like this:
Article.order(Article.send(:sanitize_sql, ["IF(user_id = ?, 1, 2)", params[:user_id]]))
IF(user_id = ?, 1, 2) returns 1 or 2 for ordering articles. sanitize_sql, a private method, is used to sanitize parameters.
If params[:user_id] is always an integer, the query could be simplified to this:
Article.order("IF(user_id = #{params[:user_id].to_i}, 1, 2)")