mirror of
https://github.com/rails/rails.git
synced 2022-11-09 12:12:34 -05:00
Optimize ActiveRecord::Relation#include? on an unloaded relation
Perform an efficient existence query through #exists? instead of loading the entire relation into memory and searching it. Before: > Person.where(name: "David").include?(david) SELECT `people`.* FROM `people` WHERE `people`.`name` = 'David' => true After: > Person.where(name: "David").include?(david) SELECT 1 AS one FROM `people` WHERE `people`.`name` = 'David' AND `people`.`id` = 1 LIMIT 1 => true
This commit is contained in:
parent
614580270d
commit
166b63eb67
3 changed files with 94 additions and 1 deletions
|
@ -319,6 +319,21 @@ module ActiveRecord
|
||||||
skip_query_cache_if_necessary { connection.select_rows(relation.arel, "#{name} Exists?").size == 1 }
|
skip_query_cache_if_necessary { connection.select_rows(relation.arel, "#{name} Exists?").size == 1 }
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Returns true if the relation contains the given record or false otherwise.
|
||||||
|
#
|
||||||
|
# No query is performed if the relation is loaded; the given record is
|
||||||
|
# compared to the records in memory. If the relation is unloaded, an
|
||||||
|
# efficient existence query is performed, as in #exists?.
|
||||||
|
def include?(record)
|
||||||
|
if loaded?
|
||||||
|
records.include?(record)
|
||||||
|
else
|
||||||
|
record.is_a?(klass) && exists?(record.id)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
alias :member? :include?
|
||||||
|
|
||||||
# This method is called whenever no records are found with either a single
|
# This method is called whenever no records are found with either a single
|
||||||
# id or multiple ids and raises an ActiveRecord::RecordNotFound exception.
|
# id or multiple ids and raises an ActiveRecord::RecordNotFound exception.
|
||||||
#
|
#
|
||||||
|
|
|
@ -375,6 +375,84 @@ class FinderTest < ActiveRecord::TestCase
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def test_include_on_unloaded_relation_with_match
|
||||||
|
assert_sql(/1 AS one.*LIMIT/) do
|
||||||
|
assert_equal true, Customer.where(name: "David").include?(customers(:david))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_include_on_unloaded_relation_without_match
|
||||||
|
assert_sql(/1 AS one.*LIMIT/) do
|
||||||
|
assert_equal false, Customer.where(name: "David").include?(customers(:mary))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_include_on_unloaded_relation_with_mismatched_class
|
||||||
|
topic = topics(:first)
|
||||||
|
assert Customer.exists?(topic.id)
|
||||||
|
|
||||||
|
assert_no_queries do
|
||||||
|
assert_equal false, Customer.where(name: "David").include?(topic)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_include_on_loaded_relation_with_match
|
||||||
|
customers = Customer.where(name: "David").load
|
||||||
|
david = customers(:david)
|
||||||
|
|
||||||
|
assert_no_queries do
|
||||||
|
assert_equal true, customers.include?(david)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_include_on_loaded_relation_without_match
|
||||||
|
customers = Customer.where(name: "David").load
|
||||||
|
mary = customers(:mary)
|
||||||
|
|
||||||
|
assert_no_queries do
|
||||||
|
assert_equal false, customers.include?(mary)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_member_on_unloaded_relation_with_match
|
||||||
|
assert_sql(/1 AS one.*LIMIT/) do
|
||||||
|
assert_equal true, Customer.where(name: "David").member?(customers(:david))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_member_on_unloaded_relation_without_match
|
||||||
|
assert_sql(/1 AS one.*LIMIT/) do
|
||||||
|
assert_equal false, Customer.where(name: "David").member?(customers(:mary))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_member_on_unloaded_relation_with_mismatched_class
|
||||||
|
topic = topics(:first)
|
||||||
|
assert Customer.exists?(topic.id)
|
||||||
|
|
||||||
|
assert_no_queries do
|
||||||
|
assert_equal false, Customer.where(name: "David").member?(topic)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_member_on_loaded_relation_with_match
|
||||||
|
customers = Customer.where(name: "David").load
|
||||||
|
david = customers(:david)
|
||||||
|
|
||||||
|
assert_no_queries do
|
||||||
|
assert_equal true, customers.member?(david)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_member_on_loaded_relation_without_match
|
||||||
|
customers = Customer.where(name: "David").load
|
||||||
|
mary = customers(:mary)
|
||||||
|
|
||||||
|
assert_no_queries do
|
||||||
|
assert_equal false, customers.member?(mary)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
def test_find_by_array_of_one_id
|
def test_find_by_array_of_one_id
|
||||||
assert_kind_of(Array, Topic.find([ 1 ]))
|
assert_kind_of(Array, Topic.find([ 1 ]))
|
||||||
assert_equal(1, Topic.find([ 1 ]).length)
|
assert_equal(1, Topic.find([ 1 ]).length)
|
||||||
|
|
|
@ -48,7 +48,7 @@ module ActiveRecord
|
||||||
QUERYING_METHODS =
|
QUERYING_METHODS =
|
||||||
ActiveRecord::Batches.public_instance_methods(false) +
|
ActiveRecord::Batches.public_instance_methods(false) +
|
||||||
ActiveRecord::Calculations.public_instance_methods(false) +
|
ActiveRecord::Calculations.public_instance_methods(false) +
|
||||||
ActiveRecord::FinderMethods.public_instance_methods(false) - [:raise_record_not_found_exception!] +
|
ActiveRecord::FinderMethods.public_instance_methods(false) - [:include?, :member?, :raise_record_not_found_exception!] +
|
||||||
ActiveRecord::SpawnMethods.public_instance_methods(false) - [:spawn, :merge!] +
|
ActiveRecord::SpawnMethods.public_instance_methods(false) - [:spawn, :merge!] +
|
||||||
ActiveRecord::QueryMethods.public_instance_methods(false).reject { |method|
|
ActiveRecord::QueryMethods.public_instance_methods(false).reject { |method|
|
||||||
method.end_with?("=", "!", "value", "values", "clause")
|
method.end_with?("=", "!", "value", "values", "clause")
|
||||||
|
|
Loading…
Reference in a new issue