Rails 2.3.8: выборка объектов через ActiveRecord без построения всех объектов - PullRequest
2 голосов
/ 21 июня 2011

Мне интересно, есть ли способ извлечь объекты из БД через ActiveRecord, не используя Rails для сборки целых объектов (всего несколько полей).

Например, Иногда мне нужно проверить, содержит ли определенный объект определенное поле. Допустим, у меня есть объект Student, ссылающийся на объект Bag (у каждого студента есть одна сумка). Мне нужно проверить, существует ли студентка, что в ее сумке больше 4 карандашей.

В ActiveRecord мне нужно было бы сделать что-то вроде этого:

exists = Student.female.find(:all, conditions => 'bags.pencil_count > 4', :include => :bag).size > 0

Проблема в том, что, если есть 1000 студентов, выполняющих это условие, AR построит 1000 объектов, включая 1000 сумок.

Это сводит меня к использованию простого SQL для этого запроса (из соображений производительности), что нарушает AR. Я не буду использовать именованные области, и я должен был бы помнить, чтобы обновить их все вокруг кода, если что-то в названной области видимости изменится.

Это пример, но есть еще много случаев, по причинам производительности, Я должен был бы использовать SQL вместо того, чтобы позволить AR строить много объектов, и это нарушает инкапсуляцию.

Есть ли способ сказать AR не строить объекты или просто строить определенное поле (также в ассоциациях)?

Ответы [ 2 ]

4 голосов
/ 21 июня 2011

Если вы проверяете только наличие соответствующей записи, просто используйте Model.count из ActiveRecord :: Calculations , например ::

exists = Student.female.count(  :conditions => 'bags.pencil_count > 4',
                                :joins => :bag
                             ) > 0

count просто (как следует из названия класса), выполняет вычисления и не строит никаких объектов.

(И для дальнейшего использования полезно знать разницу между :include и :joins. Первый из них загружает связанную модель, а второй нет, но все же позволяет использовать эти поля в :conditions .)

2 голосов
/ 21 июня 2011

Джордан дал лучший ответ здесь - особенно по поводу использования объединений вместо include (потому что объединение на самом деле не создаст объекты bag)

Я просто добавлю к этому, сказав, что если вы действительно сделаете этовсе еще нужны объекты "Student" (только с небольшим количеством информации), вы также можете использовать ключевое слово: select - которое работает так же, как в mysql и означает, что ввод-вывод db будет уменьшен до информации, которую вы вставляетевыберите - и вы также можете добавить производные поля из других таблиц, например:

students = Student.female.all( 
               :select => 'students.id, students.name, bags.pencil_count AS pencil_count', 
               :conditions => 'students.gender = 'F' AND bags.pencil_count > 4',
               :joins => :bag
               )
students.each do |student|
  p "#{student.name} has #{student.pencil_count} pencils in her bag"
end

даст, например:

Jenny has 5 pencils in her bag
Samantha has 14 pencils in her bag
Jill has 8 pencils in her bag

(хотя обратите внимание, что производное поле (например карандаша_count) будетбыть строкой - вам может потребоваться привести, например, с student.pencil_count.to_i)

...