Сложный рельсовый запрос - союзы? к югу выбрать? я все еще могу использовать named_scope? - PullRequest
6 голосов
/ 20 июня 2009

Отчасти я люблю Rails в том, что я ненавижу SQL - я думаю, что это больше похоже на язык ассемблера, которым нужно манипулировать с помощью инструментов более высокого уровня, таких как ActiveRecord. Однако я, кажется, перешел границы этого подхода, и я не в себе с SQL.

У меня сложная модель с множеством вложенных записей. У меня также есть набор 30-40 named_scopes, которые реализуют бизнес-логику от клиента. Эти области видимости соединяются вместе условно, поэтому у меня есть эти joins_ области видимости, чтобы соединения не были засорены.

У меня есть пара из них, которые работают неправильно, или, по крайней мере, не так, как клиент хочет, чтобы они работали. Вот примерное представление о структуре модели с несколькими именованными областями (не все, что нужно для примера), которые иллюстрируют мой подход и указывают на мои проблемы. (прошу прощения за любые синтаксические ошибки)

class Man < ActiveRecord::Base
  has_many :wives

  named_scope :has_wife_named       lambda { |n| { :conditions => { :wives => {:name => n}}}}
  named_scope :has_young_wife_named lambda { |n| { :conditions => { :wives => {:name => n, :age => 0..30}}}}
  named_scope :has_yw_named_v2      lambda { |n| { :conditions => ["wives.name = ? AND wives.age <= 30", n]}}
  named_scope :joins_wives         :joins => :wives

  named_scope :has_red_cat          :conditions => { :cats => {:color => 'red'}}        
  named_scope :has_cat_of_color     lambda { |c| { :conditions => { :cats => {:color => c}}}}
  named_scope :has_7yo_cat          :conditions => { :cats => {:age => 7}}
  named_scope :has_cat_of_age       lambda { |a| { :conditions => { :cats => {:age => a}}}}
  named_scope :has_cat_older_than   lambda { |a| { :conditions => ["cats.age > ?", a] }}
  named_scope :has_cat_younger_than lambda { |a| { :conditions => ["cats.age < ?", a] }}
  named_scope :has_cat_fatter_than  lambda { |w| { :conditions => ["cats.weight > ?", w] } }
  named_scope :joins_wives_cats     :joins => {:wives => :cats}
end

class Wife < ActiveRecord::Base
  belongs_to :man
  has_many :cats
end

class Cat < ActiveRecord::Base
  belongs_to :wife
end
  1. Я могу найти мужчин, чьи жены имеют рыжих кошек И семи лет

    @men = Man.has_red_cat.has_7yo_cat.joins_wives_cats.scoped({:select => 'DISTINCT men'})
    

    И я даже могу найти мужчин, у жен которых есть кошки весом более 20 фунтов и старше 6 лет

    @men = Man.has_cat_fatter_than(20).has_cat_older_than(5).joins_wives_cats.scoped({:select => 'DISTINCT men'})
    

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

  2. Я могу найти мужчин с женами по имени Эстер

    @men = Man.has_wife_named('Esther')
    

    Я даже могу найти мужчин с женами по имени Эстер, Руфь ИЛИ Ада (сладкая!)

    @men = Man.has_wife_named(['Esther', 'Ruth', 'Ada'])
    

    но я хочу найти мужчин с женами по имени Эстер, Руфь и Ада.

  3. Ха-ха, только шучу, на самом деле, мне это нужно: я могу найти мужчин с женами до 30 лет по имени Эстер

    @men = Man.has_young_wife_named('Esther')
    

    найти мужчин с молодыми женами по имени Эстер, Руфь или Ада

    @men = Man.has_young_wife_named(['Esther', 'Ruth', 'Ada'])
    

    но, как и выше, я хочу найти мужчин с молодыми женами по имени Эстер, Руфь и Ада. К счастью, в этом случае зафиксирован минимум, но было бы неплохо указать и минимальный возраст.

  4. есть ли способ проверить неравенство с хэш-синтаксисом, или вам всегда приходится возвращаться к :conditions => ["", n] - обратите внимание на разницу между has_young_wife_named и has_yw_named_v2 - мне больше нравится первое, но диапазон работает только для конечных значений. Если вы ищете старую жену, я думаю, вы могли бы использовать a..100, но затем, когда жене исполняется 101 год, она прекращает поиск. (хм. Она умеет готовить? Дж / к)

  5. есть ли способ использовать область в пределах области? Мне бы понравилось, если бы :has_red_cat мог каким-то образом использовать :has_cat_of_color, или если бы был какой-то способ использовать область из дочерней записи в ее родительском элементе, чтобы я мог поместить связанные с кошкой области в модель Wife.

Я действительно не хочу делать это в прямом SQL без использования named_scope, если только нет чего-то действительно более приятного - предложения по плагинам и тому подобное, которые не очень ценятся, или указание на тот тип SQL, который мне нужно изучить. Один мой друг предположил, что UNION или подиски будут работать здесь, но, похоже, они мало обсуждаются в контексте Rails. Я пока ничего не знаю о взглядах - они будут полезны? Есть ли способ сделать их счастливыми с помощью рельсов?

Спасибо!

Как я собирался в Сент-Айвс
Я встретил мужчину с семью женами
У каждой жены было семь мешков
В каждом мешке было по семь кошек
У каждой кошки было семь наборов
Комплекты, кошки, мешки, жены
Сколько собирались в Сент-Айвс?

Ответы [ 3 ]

2 голосов
/ 20 июня 2009

Ну, у меня были отличные результаты с named_scope с такими:

named_scope :has_cat_older_than   lambda { |a| { :conditions => ["men.id in ( select man_id from wives where wives.id in ( select wife_id from cats where age > ? ) )", a] } }

и

named_scope :has_young_wife_named lambda { |n| { :conditions => ["men.id in ( select man_id from wives where name = ? and age < 30)", n] } }

Теперь я могу успешно сделать

Member.has_cat_older_than(6).has_young_wife_named('Miriam').has_young_wife_named('Vashti')

и получите то, что я ожидаю. Эти области не требуют использования объединений, и, похоже, они хорошо сочетаются с другими объединениями в стиле.

w00t!

Комментарий выявил, является ли это эффективным способом сделать это, или же существует более «рельсовый» способ. Может быть полезен способ включить область действия из другой модели в качестве фрагмента подзапроса sql ...

2 голосов
/ 24 июля 2009

Я использовал construct_finder_sql, чтобы выполнить отбор одного named_scope в другом. Это может быть не для всех, но использование этого позволяет нам высушить пару named_scopes мы использовали для отчетов.

Man.has_cat_older_than(6).send(:construct_finder_sql,{})

Попробуйте это в вашем скрипте / консоли.

0 голосов
/ 20 июня 2009

Вы использовали самое нативное решение для Rails. Прямой SQL будет иметь такую ​​же производительность, поэтому нет причин использовать его.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...