Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow ActiveRecord::QueryMethods#pluck to accept hash args with symbol & string values #51676

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
9 changes: 9 additions & 0 deletions activerecord/CHANGELOG.md
@@ -1,3 +1,12 @@
* Allow `ActiveRecord::Base#pluck` to accept hash arguments with symbol and string values.

```ruby
Post.joins(:comments).pluck(:id, comments: :id)
Post.joins(:comments).pluck("id", "comments" => "id")
```

*Joshua Young*

* Strict loading using `:n_plus_one_only` does not eagerly load child associations.

With this change, child associations are no longer eagerly loaded, to
Expand Down
6 changes: 5 additions & 1 deletion activerecord/lib/active_record/relation/calculations.rb
Expand Up @@ -275,10 +275,14 @@ def calculate(operation, column_name)
# # SELECT people.id FROM people WHERE people.age = 21 LIMIT 5
# # => [2, 3]
#
# Comment.joins(:person).pluck(:id, person: [:id])
# Comment.joins(:person).pluck(:id, person: :id)
# # SELECT comments.id, people.id FROM comments INNER JOIN people on comments.person_id = people.id
# # => [[1, 2], [2, 2]]
#
# Comment.joins(:person).pluck(:id, person: [:id, :name])
# # SELECT comments.id, people.id, people.name FROM comments INNER JOIN people on comments.person_id = people.id
# # => [[1, 2, 'David'], [2, 2, 'David']]
#
# Person.pluck(Arel.sql('DATEDIFF(updated_at, created_at)'))
# # SELECT DATEDIFF(updated_at, created_at) FROM people
# # => ['0', '27761', '173']
Expand Down
24 changes: 16 additions & 8 deletions activerecord/lib/active_record/relation/query_methods.rb
Expand Up @@ -2110,14 +2110,14 @@ def check_if_method_has_arguments!(method_name, args, message = nil)
def process_select_args(fields)
fields.flat_map do |field|
if field.is_a?(Hash)
arel_columns_from_hash(field)
arel_columns_from_hash(field, for_select: true)
else
field
end
end
end

def arel_columns_from_hash(fields)
def arel_columns_from_hash(fields, for_select: false)
Copy link
Contributor Author

@joshuay03 joshuay03 Apr 27, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a bit hacky, see my note in the 'Additional information' section for a possible alternative.

fields.flat_map do |key, columns_aliases|
case columns_aliases
when Hash
Expand All @@ -2126,22 +2126,30 @@ def arel_columns_from_hash(fields)
references = PredicateBuilder.references({ key.to_s => fields[key] })
self.references_values |= references unless references.empty?
end
arel_column("#{key}.#{column}") do
predicate_builder.resolve_arel_attribute(key.to_s, column)
end.as(column_alias.to_s)
build_arel_attribute(key.to_s, column.to_s).as(column_alias.to_s)
end
when Array
columns_aliases.map do |column|
arel_column("#{key}.#{column}", &:itself)
end
when String, Symbol
arel_column(key.to_s) do
predicate_builder.resolve_arel_attribute(klass.table_name, key.to_s)
end.as(columns_aliases.to_s)
if for_select
arel_column(key.to_s) do
predicate_builder.resolve_arel_attribute(klass.table_name, key.to_s)
end.as(columns_aliases.to_s)
else
build_arel_attribute(key.to_s, columns_aliases.to_s)
end
end
end
end

def build_arel_attribute(table_name, column_name)
arel_column("#{table_name}.#{column_name}") do
predicate_builder.resolve_arel_attribute(table_name.to_s, column_name)
end
end

STRUCTURAL_VALUE_METHODS = (
Relation::VALUE_METHODS -
[:extending, :where, :having, :unscope, :references, :annotate, :optimizer_hints]
Expand Down
5 changes: 5 additions & 0 deletions activerecord/test/cases/calculations_test.rb
Expand Up @@ -958,7 +958,10 @@ def test_pluck_with_hash_argument
[2, "The Second Topic of the day"],
[3, "The Third Topic of the day"]
]
assert_equal expected, Topic.order(:id).limit(3).pluck(:id, topics: :title)
assert_equal expected, Topic.order(:id).limit(3).pluck("id", "topics" => "title")
assert_equal expected, Topic.order(:id).limit(3).pluck(:id, topics: [:title])
assert_equal expected, Topic.order(:id).limit(3).pluck("id", "topics" => ["title"])
end

def test_pluck_with_hash_argument_with_multiple_tables
Expand All @@ -967,6 +970,8 @@ def test_pluck_with_hash_argument_with_multiple_tables
[1, 2, "Thank you again for the welcome"],
[2, 3, "Don't think too hard"]
]
assert_equal expected, Post.joins(:comments).order(posts: { id: :asc }, comments: { id: :asc }).limit(3).pluck(:id, comments: [:id, :body])
assert_equal expected, Post.joins(:comments).order(posts: { id: :asc }, comments: { id: :asc }).limit(3).pluck(posts: :id, comments: [:id, :body])
assert_equal expected, Post.joins(:comments).order(posts: { id: :asc }, comments: { id: :asc }).limit(3).pluck(posts: [:id], comments: [:id, :body])
end

Expand Down