Почему запрос в рельсах с ограничением не работает, если только .all не ставится в конец - PullRequest
10 голосов
/ 13 июля 2011

У меня есть такой запрос:

locations = Location.order('id ASC').limit(10)

, который возвращает массив из 500 или около того записей - все записи в таблице - т.е. предложение limit игнорируется.

Тем не менее, если я ставлю .all в конце:

locations = Location.order('id ASC').limit(10).all

, он работает и возвращает 10 записей.

Этот код запускается в виде грабли, и я использую PostgreSQL, если это делаетлюбая разница.

Почему он это делает?Конечно, все не должно быть.Чего мне не хватает?

Ответы [ 2 ]

8 голосов
/ 13 июля 2011

Я думаю, что поведение зависит от того, как вы обрабатываете переменную locations после ее установки.Это связано с тем, что Location.order('id ASC').limit(10) не запрашивает записи, а возвращает объект типа ActiveRecord::Relation.Запрос будет выполнен только после того, как вы вызовете all, first, each, map и т. Д. Для этого объекта.

В моем тестировании

Location.order('id ASC').limit(10).map { |l| l.id }

возвращаетмассив из 10 идентификаторов, как и следовало ожидать.Но

Location.order('id ASC').limit(10).count

возвращает общее количество местоположений в базе данных, потому что он выполняет SQL

SELECT COUNT(*) FROM "locations" LIMIT 10

, который возвращает полное количество строк местоположений (ограничение на количествовозвращенные строки, а не само количество).

Поэтому, если вы обрабатываете результат Location.order('id ASC').limit(10) как массив, перебирая его, вы должны получить тот же результат, как если бы вы добавили all.Если вы звоните count, вы не будете.Вроде неудачно, так как я думаю, что в идеале они должны вести себя одинаково, и вам не нужно знать, что вы имеете дело с ActiveRecord::Relation вместо массива.

1 голос
/ 13 июля 2011

Хорошо, вот мое объяснение Прежде всего, если вы сделаете Location.order('id ASC').limit(10).class, вы увидите ActiveRecord::Relation далее на сайте с rails API ActiveRecord::Relation не имеет метода all, однако он включает ActiveRecord::FinderMethods и если вы посмотри там ты найдешь следующий

# File activerecord/lib/active_record/relation/finder_methods.rb, line 142
def all(*args)
  args.any? ? apply_finder_options(args.first).to_a : to_a
end

так он вызывает to_a метод Как упоминалось в railscasts , этот метод определяется как

def to_a  
  ...
  @records = eager_loading? ? find_with_associations : @klass.find_by_sql(arel.to_sql)  
  ...  
  @records  
end 

поэтому он выполняет запрос SQL в третьей строке с @records = eager_loading? ? find_with_associations : @klass.find_by_sql(arel.to_sql)

...